
Solanaでブロックハッシュエラーに対処する方法
ブロックハッシュとは?
ブロックハッシュを理解するには、まずスロットとブロックについて理解する必要があります。
- スロットとは、バリデータがブロックを生成できる時間枠です
- ブロックとは、バリデータが処理するトランザクションとメタデータの集合です。各ブロックのメタデータは直前のブロックにリンクし、チェーンを形成します。
スロットは400~600ms続き、各スロットでバリデータが新しいブロックを提案できることを理解しておくことが重要です。ブロックが作成されなかった場合、スロットがインクリメントされ、別のバリデータが新しいブロックの作成を試みます。つまり、すべてのスロットに対応するブロックがあるわけではありませんが、すべてのブロックには、そのブロックが提案されたスロットが対応しています。
では、ブロックハッシュとは何でしょうか?ブロックハッシュとは、1つのスロット中に作成された、ブロックチェーン上のすべての台帳エントリに対する一意のハッシュ値です。これはブロックの最後のエントリIDから計算されます。ブロックが生成されるたびに、一意のブロックハッシュが作成されます。これらのブロックハッシュはタイムスタンプとして使用されます。
Solanaのコミットメントレベルとは?
もう1つ理解しておくべき重要な概念が、コミットメントレベルです。コミットメントレベルは、特定のブロックに対するネットワークの確認状況を示します。選択肢はprocessed、confirmed、finalizedの3つです。
バリデータがブロックをチェーンに送信した時点では、そのブロックはprocessed状態です。必要な数のバリデータ(バリデータの66%)がブロックを含めることに投票すると、そのブロックはチェーンに追加され、コミットメントレベルがconfirmedに変わります。さらにそのブロックの上に31個のブロックが構築されると、コミットメントレベルはfinalizedに変わります。
ブロックハッシュエラーが発生する理由
すべてのトランザクションにはタイムスタンプとして機能する直近のブロックハッシュが含まれており、そのブロックハッシュが十分に新しいと見なされなくなると、トランザクションは期限切れになります。トランザクションを処理するバリデータは「BlockhashQueue」(直近300件のブロックハッシュのリスト)を確認し、トランザクションのブロックハッシュが十分に新しいかどうかを判定します。ブロックハッシュがリストにない場合、トランザクションは拒否されます。通常、スロットは400ms~600ms続くため、ブロックハッシュは60~90秒間有効です。
ブロックハッシュが見つからない(Transaction simulation failed: Blockhash not found)
「Blockhash not found」エラーは、バリデータがトランザクションを処理する際、そのトランザクションに含まれるブロックハッシュが有効と見なされない場合に発生します。ブロックハッシュが古すぎる場合のほか、新しすぎる場合にも発生することがあります。
このエラーの最も一般的な原因は、トランザクションに含まれるブロックハッシュが、バリデータが照合する直近300件のブロックハッシュのキューに存在しないことです。この場合、blockhash not foundエラーが発生します。トランザクションが必要な時間内に作成・処理されないと、期限切れになることがあります。たとえば、ユーザーがトランザクションへの署名に時間をかけすぎた場合です。また、有効なトランザクションが送信されたものの現在のブロックに含まれず、後続のブロックに含められる段階では、そのトランザクションのブロックハッシュが古くなりすぎている場合もあります。
このようにトランザクションが事実上期限切れになった場合、ブロック高超過エラー(TransactionExpiredBlock heightExceededError)がよく発生します。このエラーを詳しく理解するには、ブロック高の意味を理解する必要があります。ブロック高とは、現在のブロックより下にあるブロックの数です。現在のブロックがブロック1000であれば、ブロック高は1000です。トランザクションを作成すると、そのトランザクションが有効である最大ブロック高が記録されます。トランザクションの処理中に、現在のブロック高がトランザクションの最大有効ブロック高を超えていることが確認されると、エラーが発生します。
「blockhash not foundエラー」が発生するもう1つの状況は、トランザクションのブロックハッシュが、そのトランザクションの有効期限を確認するために使用されるブロックハッシュよりも新しい場合です。少し分かりにくいため、例を挙げます。特定のブロック(たとえばブロック1000)のブロックハッシュを含むトランザクションを作成し、すぐにRPCへ送信したとします。RPCは、1つ前のブロック、この場合はブロック999のブロックハッシュを取得する可能性があります(RPCがより高いコミットメントレベルを使用している場合や、RPCノードがネットワークより遅れている場合)。その結果、トランザクション内のブロックハッシュが見つかりません。これは珍しい状況ですが、次の2つのシナリオで発生する可能性があります。
1. コミットメントレベルの不一致
トランザクションの作成時にはconfirmedコミットメントレベルを使用していても、RPCが有効性を計算する際には、デフォルトでfinalizedコミットメントが使用されます。
finalizedブロックは最新ブロックより31ブロック「遅れている」ため、有効性の確認に使われるブロックハッシュがトランザクションのブロックハッシュより古くなる可能性があります。
最終的に破棄される少数派フォーク上で、トランザクションのブロックハッシュを取得するためにprocessedコミットメントレベルを使用すると、そのブロックハッシュは無効になり、処理時に見つかりません。
2. RPCの不一致
ブロックハッシュの取得とトランザクションの送信に異なる2つのRPCを使用した場合、送信側RPCの遅延によって、有効性の確認に使用されるブロックが、トランザクション作成時に使用されたブロックより古くなる可能性があります。
ブロックハッシュエラーを解決する方法
上記の各状況に対処するには、いくつかの手順があります。
1. 古すぎないブロックハッシュでトランザクションを送信する:
トランザクションのブロックハッシュを取得する際は、confirmedコミットメントレベルを使用してください。これにより、finalizedコミットメントレベルよりも新しいブロックハッシュを含めることができます。
さらに新しいブロックハッシュを取得するために、processedコミットメントレベルを使用することもできます。ただし、processedブロックの約5%はクラスターによってfinalizedされません。その場合、トランザクションのブロックハッシュは破棄されたフォークに属することになり、無効になります。
2. トランザクションのブロックハッシュが、有効性の確認に使用されるブロックハッシュより新しくならないようにする:
preflightCommitmentは、トランザクションのブロックハッシュ取得時と同じコミットメントレベルに必ず設定してください(skipPreflightを使用する場合も同様です)。これにより、トランザクションのブロックハッシュが、有効性の確認に使用されるブロックハッシュより新しくなることを防げます。
トランザクション送信時のRPCノードの遅延に対処するには、RPCにトランザクションを繰り返し送信してください。一定間隔で再送信することで、RPCに遅延が発生していても、最終的には追いついてトランザクションの期限切れを検出できるようになります。
simulateTransactionリクエストを使用する場合は、replaceRecentBlockhashパラメータを設定してください。このフラグを設定すると、シミュレーション対象トランザクションのブロックハッシュが、シミュレーションで常に有効なブロックハッシュに置き換えられます。
3. ブロックハッシュが有効な間はトランザクションを再試行し続ける:
トランザクションを作成して最新のブロックハッシュを取得したら、そのブロックハッシュのlastValidBlockHeightを記録してください。その後、現在のブロック高がトランザクションの有効ブロック高を超えるまで、そのブロックハッシュを使ってトランザクションを再試行できます。getBlockHeight RPC呼び出しを使用すると、トランザクションがまだ有効かどうかを継続的に確認できます。現在のブロック高がトランザクションのlastValidBlock高を超えたら、新しいブロックハッシュを取得して使用してください。
このブログ記事が、ブロックハッシュエラーへの理解を深め、エラーの発生回数を減らすために役立てば幸いです。ほかにご質問がある場合は、ぜひDiscordコミュニティにご参加いただくか、Xでダイレクトメッセージをお送りください。
追加リソース:
関連記事
Heliusを購読
Solana開発の最新情報や新しい記事の公開通知を受け取れます


