統合アドレス(Unified Address、ZIP-316)の検証
これは学習ガイドであり、パッケージ化されたデコーダーやコピー&ペースト用の支払いライブラリではありません。統合アドレスがどのように構成されているかを説明し、保守されているライブラリが内部で何をしているかを理解できるようにします。実際の資金を扱うものについては、ZIP-316仕様および以下にリンクされている公式実装に従ってください。
全体像
統合アドレス(UA)は、複数の受信者タイプ、すなわち公開型(Transparent)、Sapling、Orchard、またはそれらの組み合わせを含む単一のアドレス文字列です。支払い側のウォレット(Wallet)は、対応する最適な受信者プール(pool)を自動的に選択します。
UAは、複数のラベル付きカードが入った封印された封筒のようなものだと考えてください。各カードは、あなたに到達する異なる方法を表します。アドレスを確認するには、アプリケーションは次のことを行う必要があります。
- 封筒を開く: 文字列をデコードします。
- 内容の並び替えを元に戻す: 保護用のスクランブル(F4Jumble)を解除します。
- 各カードを読む: 個々の受信者を抽出します。
- プロトコル規則を適用する: タイプコード範囲に従ってエントリーを無視または拒否します。
「Bech32mをデコードするだけ」では不十分な理由
UAはBech32mテキストエンコーディングを使用しますが、Bech32mをデコードするだけでは利用可能な受信者は判明しません。
ZIP-316では、エンコード前にペイロードをF4Jumbleで意図的にスクランブルします。F4Jumbleにより、アドレス内の1文字だけを変更しても、デコード結果は完全に変化します。これにより、攻撃者がプレフィックスとサフィックスを有効に見せたまま、アドレス中央のバイトを入れ替えるアドレス可鍛性攻撃を防ぎます。
重要な規則: 可鍛性保護は、アプリケーションが完全なデコードおよび検証パイプラインを実行する場合にのみ機能します。部分的なデコードでは、安全性が失われる一方でリスクはすべて残ります。
デコードパイプラインの手順
手順1: Bech32mをデコードし、ネットワークを確認する
- 人間可読部(HRP):
uはメインネットを識別し、utestはテストネットを識別します。(メインネットのUAはu1で始まります。1はBech32の区切り文字です。)
- 長さの上限: 標準のBech32mでは90文字の上限が適用されます。UAは通常この上限を超えるため、デコーダーでは標準の長さチェックを無効にする必要があります。
- 5ビットのBech32mワードを標準の8ビットバイトへ戻します。
手順2: F4Jumbleを逆変換する
F4Jumbleは、BLAKE2bに基づく4ラウンドのFeistelネットワークです。
- 左半分の長さ:
min(64, floor(length / 2))バイトです。64バイトの上限はBLAKE2bの最大出力サイズに対応します。右半分には残りのペイロードが含まれます。
- ハッシュ関数: 固定のパーソナライゼーションラベル(
UA_F4Jumble_GおよびUA_F4Jumble_H)を使用してGとHを交互に適用します。
- ラウンド順序: 順方向のエンコードではG(0) → H(0) → G(1) → H(1)を実行します。逆変換(スクランブル解除)ではH(1) → G(1) → H(0) → G(0)を実行します。
- 範囲チェック: ZIP-316のペイロードサイズ制限外の入力を拒否します。
手順3: パディングを削除し、HRPを検証する
スクランブル前に、エンコーダーはHRPを含みゼロでパディングされた16バイトを追加します。
- スクランブル解除後、末尾の16バイトを削除します。
- 埋め込まれたHRPが想定するネットワーク(
uまたはutest)と一致することを確認します。これにより、テストネットのアドレスがメインネットで誤って受け入れられることを防ぎます。
手順4: 受信者を抽出する
残りのペイロードは(typecode, length, content)エントリーで構成され、タイプコードと長さはコンパクトサイズ整数(小さい値の場合は1バイト)として格納されます。既知の受信者タイプコードは次のとおりです。
| タイプコード | 受信者タイプ | コンテンツ長 |
|---|
0x00 | 公開型(P2PKH) | 20バイト |
0x01 | 公開型(P2SH) | 20バイト |
0x02 | Sapling | 43バイト |
0x03 | Orchard | 43バイト |
これらに加えて、ZIP-316は将来の互換性のためにさらに2つの範囲を予約しています。
0xC0–0xDF(MUST-understandではないメタデータ): コンシューマーは、この範囲内で認識できないメタデータ項目を無視しなければなりません。
0xE0および0xE1(割り当て済みのMUST-understand有効期限メタデータ): 現在のZIP-316レジストリーでは、これらをアドレスの有効期限ブロック高および時刻に割り当てています。コンシューマーはこれらの項目を理解するか、アドレスを拒否しなければなりません。
0xE2–0xFC(未割り当てのMUST-understandメタデータ): コンシューマーは、この範囲内で認識できない項目に遭遇した場合、アドレスを拒否しなければなりません。
既知の受信者タイプについては、エンコードされた長さがそのタイプで指定されたコンテンツ長と一致することを検証します。メタデータ項目については、エンコードされたコンパクトサイズ長を使用してコンテンツ長を決定します。切り詰められたエントリーまたは末尾に残るバイトを拒否します。
優先する受信者順序。 アドレスの解析に成功したら、ウォレットまたは支払いツール(tools)は、Orchard、次にSapling、最後に公開型の順で最適な受信者を選択するべきです。
必須のZIP-316拒否規則
デコードに成功しても、アドレスが有効になるわけではありません。 公式のZcashウォレットは、以下の規則に違反するアドレスを厳格に拒否します。支払い失敗を防ぐため、ウェブツールもこれらを拒否しなければなりません。
- シールドされた(shielded)受信者の欠落: アドレスには、少なくとも1つのSaplingまたはOrchard受信者が必須です。公開型受信者のみを含むUAは、ZIP-316では無効です。
- 重複するタイプコード: 各受信者タイプは最大1回のみ出現できます。
- ソートされていないタイプコード: 受信者はタイプコードの厳密な昇順で出現しなければなりません。
- 競合する公開型受信者: UAにはP2PKHまたはP2SHのいずれかを含められますが、両方を含めることはできません。
- 不正な形式のエントリーまたはパディング: 一致しないネットワークプレフィックス、切り詰められたペイロード、または長さの不一致は、直ちに拒否しなければなりません。
- 認識されないタイプコード: コンシューマーは、MUST-understandメタデータ範囲(
0xE0–0xFC)内の項目を除き、認識されない項目を無視しなければなりません。この範囲内の認識されない項目は拒否しなければなりません。現在のレジストリーでは、0xE0および0xE1は割り当て済みの有効期限タイプであり、0xE2–0xFCは未割り当てです。これとは別に、SaplingまたはOrchard受信者の要件を含め、上記の必須有効性規則に違反するアドレスは拒否します。
デベロッパー向けベストプラクティス
- 生の文字列ではなく、解析済みの受信者を比較してください。 等価性を確認する前に、まずアドレスをデコードします。
- 資金を扱うものには保守されているライブラリを使用してください。 カスタムのJavaScriptデコーダーを展開するのではなく、
zcash_addressのような公式RustクレートをWebAssemblyへコンパイルしてください。
- 手書きのパーサーには注意してください。 学習目的で作成する場合は、学習プロジェクトとして扱い、何かに信頼して使用する前に以下の公式ベクトルでテストしてください。
公式仕様および参照実装
用語集
| 用語 | 意味 |
|---|
| 統合アドレス(UA) | 複数の受信者プールを束ねた単一のアドレス文字列です。 |
| 受信者 | 特定の支払い先タイプです(公開型、Sapling、またはOrchard)。 |
| Bech32m | UA文字列に使用されるテキストエンコーディング方式です。 |
| HRP | 人間可読部、またはネットワークプレフィックスです(uまたはutest)。 |
| F4Jumble | アドレスの完全性を保証する、可逆的な難読化アルゴリズムです。 |
| タイプコード | ペイロード内の受信者タイプを定義する、各エントリーの番号です。 |
| 可鍛性 | 検出されずにアドレスのバイトを不正に変更できる性質です。 |
関連項目: ビューイングキー