プロジェクト名: SORA L1 Blockchain
バージョン: v3.67.14
日時: 2024年5月17日 完成!
著者: SORA-QAI

SORA-QAI:
https://github.com/FromHDDtoSSD/SorachanCoin-qt/commit/c74ef646ae2fccb9ef324b330fc0a18e4bc557f5

I, scriptPubKeyの構造

このトランザクションは、従来からあるOP_CHECKMULTISIGの特性を活用し、ソフトフォークによって実装されました。ここでは、そのscriptPubKeyの構造について詳しく説明します。

CScript() << OP_1 << ECDSA publickey << SORA-QAI publickey id << SORA-QAI extended info << OP_3 << OP_CHECKMULTISIG

OP_1とOP_3の構造

OP_1とOP_3の構造は標準的なトランザクションとして扱われます。ここでは、そのOP_1に相当する最初のスクリプトに、ECDSAの公開鍵を与えました。

残りの二要素

残りの二要素は、本来なら無視されるスクリプトになります。制約として、サイズは33バイトで、ここには本来ECDSAの圧縮公開鍵が格納されます。そのため、それに合わせた規則で、新しいSORA-QAI用の公開鍵のIDと、その拡張情報を格納します。

0x02と、残り32バイトのエリアと、CKeyIDの組み合わせ

まず、SORA-QAI公開鍵の場所について説明します。先頭バイトは0x02です。ここには本来、ECDSAの圧縮公開鍵が格納され、そのバイナリの先頭は0x02または0x03です。標準のトランザクションとしてこれを認識させるために、先頭に0x02を割り当てました。これにより、残りの32バイトが利用可能となります。ここでCKeyIDを思い出してみてください。これはハッシュ構造であり、20バイトです。

SORA-QAIの公開鍵の変換

SORA-QAIの公開鍵はCKeyIDを用いて20バイトに変換します。そして、残りの12バイトを0x00で埋めます。これにより、ECDSAの公開鍵と区別することができます。なぜなら、ECDSAの公開鍵のバイナリの後半12バイトがすべて0x00になる確率はゼロに等しいためです。

SORA-QAI 拡張情報の格納

次の33バイトには、SORA-QAIの拡張情報を格納します。先頭は0x02で、次のバイトはバージョン情報となります。そこから20バイトはフリーで、残りの10バイトには0xFFを格納しています。

II, scriptSigの構造

scriptSigの構造は、以下のように組み立てます。

CScript() << OP_0 << ECDSA signature << SORA-QAI publickey << SORA-QAI signature

OP_0と、それにより生じるサイドエフェクトの活用

OP_0は一般的なブロックチェーンのバグ回避策として存在します。スタックの数が最後に合わなくなるため、これで調整しています。この事実は、スタックとscriptPubKeyにある33バイトのエリアが一対一の関係になっていないというサイドエフェクトを意味します。そこで、その後ろにSORA-QAI用の別の情報を積むことができます。

厳格化された場合の影響

もし厳格化されていた場合、OP_1 – OP_3の構造では合計で1つのスタックが要求されます。そのため、新たに別のスタックを積むことができません。

III, ソフトフォークによる実装

ソフトフォークによる実装には、二種類があります。まず一つ目はSegWitです。CTransactionのシリアライズを工夫して、CTxInにダミーの空のvectorを割り当て、それをフラグにすることにより、古いノードはCTxIn以降の要素を見なくなります。よって、CTxIn以降のシリアライズが1バイトずれる(ダミーの空のvectorはシリアライズでは0x00のみの1バイト)場合であっても、エラーにはなりません。そのため、古いノードではそのまま通過するという仕掛けです。

そして二つ目が、今回採用したこのOP_CHECKMULTISIGの回避策の部分をうまく活用した方法です。これでも古いノードでエラーになりません。

IV, 各ソフトフォークの利点と欠点

SegWit

利点:

  • 手数料削減: scriptSigを専用エリアに移すことで、トランザクションサイズの計算から除外され、手数料が削減される。
  • トランザクションIDの一意性: ECDSAの署名は一意性がなく(乱数を使用しないと秘密鍵が漏洩する可能性があるため)、ハッキングなどで署名が変えられてトランザクションIDが変わるリスクがある。scriptSigを専用エリアに移動することで、トランザクションIDのハッシュ計算から除外され、トランザクションIDの一意性が保たれる。

欠点:

  • ECDSA検証の欠如: CTxInがダミーで0になるため、古いノードではECDSAの検証が行われず、そのまま通過するだけとなる。
  • 複雑なコード変更: コードの変更箇所が多く、検証や機能追加が複雑化する。

OP_CHECKMULTISIGの回避策の部分をうまく活用したSORA-QAIの方法

利点:

  • 既存コードの再利用: OP_CHECKMULTISIGをそのまま拡張し、新しい形式のアドレスをbase58側に組み込むことで、既存の検証済みコードの大部分を再利用し、新しい機能を簡単に投入できる。
  • 量子&AI耐性: 量子&AI耐性の署名は一意性があるため、これとマルチシグにすることでトランザクションIDの一意性が保たれる。ECDSAの署名が書き換えられると量子&AI耐性の署名が無効になり、トランザクションが無効になるためである。

欠点:

  • トランザクション手数料: scriptSigをそのまま使用するため、トランザクション手数料は変わらない。
  • トランザクションIDの一意性の欠如: 検証がECDSAのみの場合、その署名の性質からトランザクションIDに一意性がない。しかし、量子耐性およびAI耐性の署名は一意であり、これにマルチシグを組み合わせることで、トランザクションIDの一意性が確保されます。これは、ECDSA署名を変更すると、量子耐性およびAI耐性の署名が無効になり、トランザクション自体が無効になるためです。

V, SORA-QAIトランザクションの検出

このトランザクションを検出するには、scriptPubKeyを確認します。具体的には、SORA-QAI公開鍵IDの部分です。この部分はECDSAの圧縮公開鍵向けに用意されたスタックでした。よって、CKeyIDで20バイトに変換し、先頭の0x02を除いた残りの12バイトに0x00が必ず入っています。これを利用して、SORA-QAIトランザクションを検出します。

Structure of SORA-QAI public key ID
0x02 | SORA-QAI public key ID (20 bytes) | 0x00 (12 bytes)

VI, SORA-QAIバージョン情報の検出

このトランザクションのバージョン情報を検出するには、scriptPubKeyを確認します。具体的には、SORA-QAI拡張情報の部分です。こちらも元はECDSAの圧縮公開鍵向けに用意されたスタックですので、先頭に0x02があります。そして、その次のバイトがバージョン情報となります。

Structure of SORA-QAI extended INFO
0x02 | Version | SORA-QAI free field (20 bytes) | 0xFF (11 bytes)

VII, SORA-QAI ハッシュの対象

まず、ECDSAのハッシュをSIGHASH_ALLで取得します。それから、以下の順でCScriptに情報を積んで、CHashWriterでハッシュを取ります。

CScript() << ECDSA signature << ECDSA public key << SORA-QAI public key ID << SORA-QAI free field INFO << ECDSA SIGHASH_ALL hash << Version << nHashType

ハッシュターゲットにECDSAの署名を含む点

それにより、ECDSAの署名が変化すると、このハッシュが変わります。ECDSAの署名には乱数を利用するため、有効な署名でも中身が変動しますので、それによりトランザクションIDが変わっていました。しかし、SORA-QAIの量子&AI耐性側の署名には一意性があるため、このハッシュターゲットのハッシュが変わると、署名は無効になります。したがって、トランザクションIDが変化した状態で、マイナーによってブロックに取り込まれる心配はありません。

VIII, base58とbech32

アドレス形式にはbase58形式とbech32形式があります。一般的にP2PKHなどはbase58形式で、SegWitはbech32形式が採用されています。これらは形式で区別されているだけなので、実装的にはどのように使っても自由です。そこで、SORA-QAIでは、まずこの実装をbech32形式で行っています。

アドレス形式を区別する仕組み

複数のアドレス形式が実装されている場合、どのように区別するのか。まずアドレスをデコードするとバイナリが得られます。そのバイナリの一部に、アドレスを区別するための識別子があります。そこからscriptPubKeyの形状を判断し、それによって処理を分けています。

ハードフォークとソフトフォーク

そこで、この問題です。では、新しいアドレス形式として、その部分に気軽に追加できるでしょうか? いいえ、それは難しいのです。なぜなら、そこに勝手に新しい形式を追加してしまうと、古いノードはその新しい形式を解釈できずエラーとします。それにより、コンセンサスが得られなくなります。そこで、ノードの更新をお願いするハードフォークが必要となります。

ただ、実際にブロックチェーンを運営してみるとわかるのですが、ハードフォークをお願いするのは非常に大変です。そこで、徐々に切り替えていけば整合性が得られるソフトフォークという方法が好まれます。ソフトフォークでは、この新しい形式を古いノードで認識させるための工夫が施されています。SegWitもその一つであり、SORA-QAIも同様に多くの工夫をしています。

IX, ソフトフォークとSORA-QAIのアプローチ

ソフトフォークのSegWitはCTransactionのシリアライズにダミーを持たせる構造でした。SORA-QAIではP2SHの構造に着目しました。

SegWitの方法

前述の通り、CTransactionにダミーを持たせる方法では、古いノードでCTxInの要素数が0となるため、入力がないトランザクションとして扱われます。これがブロックに取り込まれると、古いノードは入力がないと解釈し、それ以上処理せず、そのまま通過させることでエラーを避けます。

SORA-QAIの方法

SORA-QAIでは、P2SHの構造と、OP_CHECKMULTISIGの回避策(OP_0)に着目しました。この二つを利用することで、既存のコードを再利用し、最小限の変更でソフトフォークを実現しました。既存のコードは十分に検証されているため、これを再利用することで、安全性が高まります。

X, SORA-QAI 検証の構造

SORA-QAIトランザクションの検証構造は以下のステップで行われます。

  1. SORA-QAIトランザクションの識別
    まず、トランザクションがSORA-QAIに該当するかを識別します。
  2. バージョン情報による場合分け
    次に、バージョン情報を基に場合分けを行います。
  3. 全スタックの調査
    ブロックチェーンのスタックは可変構造を取るため、全てのスタックを調査します。任意の場所にデータが配置される可能性があるためです。

scriptSigに存在する公開鍵が、scriptPubKeyと結び付いているかチェック

scriptSigに格納されている公開鍵のCKeyIDを、scriptPubKeyに格納されているSORA-QAIのCKeyIDと比較します。一致すれば、scriptSigがそのscriptPubKeyと結び付いていることが確認できます。

scriptSigに存在する公開鍵が結び付いていることを確認できたら、検証用ハッシュを組み立てる

scriptSigに存在する公開鍵がscriptPubKeyと結び付いていることを確認できたら、次に検証用ハッシュを組み立てます。その組み立てたハッシュと公開鍵、量子&AI耐性の署名で検証を実施します。このとき、量子&AI耐性の署名の最後のバイトにnHashTypeが埋め込まれているため、それを除外する必要があります。

ここで、この検証結果が失敗の場合、EvalScriptが即時に失敗し、コンセンサスが取れなくなることで量子&AI耐性の検証が成立します。

量子&AI耐性の検証後は、OP_CHECKMULTISIGの通常検証に渡す

量子&AI耐性の検証が成功した場合、そのままOP_CHECKMULTISIGの処理に渡します。scriptSigおよびscriptPubKeyにはECDSA検証用の公開鍵と署名が存在するため、次にECDSAの検証が実施されます。

XI, EvalScriptの検証を突破:
OP_CHECKMULTISIGのOP_0の回避策にスタックを合わせる

EvalScriptには検証フラグが存在します。そこにOP_CHECKMULTISIGのOP_0の回避策を検証するための専用フラグが存在し、検証時にOP_0へ到達したとき、スタックが空になっていることを要求します。ただし、この回避策が必要となった原因を考慮する必要があります。

もしスタックの位置や数が一対一の関係で厳格化されていた場合、この回避策は必要ありません。よって、スタックの位置や数が寛容な点を逆手に取り、このチェックのときだけ標準的な状態に擬態してチェックを通過します。このチェックを無事にパスした後、余分に積んでいたSORA-QAI用のスタックをひっそりと降ろして整合性を取ります。

この擬態により、OP_CHECKMULTISIGに、外部から、ECDSAとは全く異なる検証アルゴリズムをマルチシグの形で導入することができます

スタックの位置や数が寛容な点は古いノードも同様です。したがって、古いノードではSORA-QAIの検証はできず無視されますが、ECDSAの検証は確実に行われます。

XII, P2SHとscriptPubKey

P2SHでは、scriptPubKeyのCScriptIDをP2SHのscriptPubKeyとして格納します。そして、EvalScriptの前処理にあるスクリプトの結合処理で、CScriptIDを計算した本来のscriptPubKeyを呼び出して結合し、EvalScriptで処理する流れとなっています。

P2SHとbase58の関係

P2PK、P2PKH、P2SHなどがbase58形式で実装されています。前述の通り、base58をデコードすると、先頭に識別用のバイナリが存在します。この識別子を与えることで、P2SHを呼び出すことができますが、そのままだとbase58形式のままです。SORA-QAIでは、この部分にbech32を使用するため、識別子にbech32を埋め込む処理を加えます。

bech32形式のアドレスでbase58を呼び出したときの工夫

まず、base58とbech32は確実に区別できます。そこで、bech32形式のアドレスでbase58が呼ばれたとき、その内部処理をbech32に切り替えながらP2SHに処理を渡します。このとき、base58の処理部に存在するbool operator==に注意を払ってください。このオペレータはbase58同士の比較を行います。bech32をbase58の内部処理に加えた場合、バイナリの構造が根本的に異なるため、このオペレータでは比較できなくなります。そのため、必ずToStringを使用して比較するように書き換えます。

bool operator==(const CBase58 &a, const CBase58 &b) const { return a.ToString() == b.ToString(); }

SegWitとの互換性

bech32をSegWitに導入することで、このSORA-QAIと同時に稼動させることが可能です。なぜなら、SegWitはP2WPKHなどの別の機構で動作するため、確実に処理を分けることができるからです。

SORA-QAIアドレスの登録と、その識別

SORA-QAIのアドレスは、そのscriptPubKeyをP2SHとして組み立てることで登録できます。登録後、それをbech32形式に結び付けて処理します。ここで重要なのは、ウォレットの残高計算をどのように対処するかです。

ウォレットの残高は、自分が所有している未使用のscriptPubKeyの累計として表示されます。したがって、ウォレットがSORA-QAIのscriptPubKeyを認識できるようにする必要があります。

実装手順:

  1. アドレスの登録: SORA-QAIのscriptPubKeyをP2SHとして登録し、それをbech32形式に結び付けます。
  2. 識別の追加: ウォレットの残高計算のロジックにSORA-QAIのscriptPubKeyを認識するための識別を追加します。
  3. 残高計算: ウォレットは未使用のSORA-QAI scriptPubKeyを含むように残高を計算し、表示します。

XIII, SORA-QAI scriptSigの組み立て

SORA-QAIの未使用なscriptPubKeyを利用するには、SORA-QAI scriptSigの組み立てが必要です。組み立て手順は次の通りです。

  1. solverの確認:
    • scriptPubKeyにSORA-QAIのスタックが存在することを確認します。
  2. scriptSigの組み立て:
    • すでに存在するECDSAのSIGHASH_ALLのハッシュを利用します。
    • ハッシュに基づいて順に署名し、scriptSigを組み立てます。
  3. EvalScriptの通過:
    • 組み立てたscriptSigがEvalScriptを通過して承認されるようにします。
  4. メモリプールへの放出:
    • 承認されたスクリプトをメモリプールに放出します。

量子&AI耐性の仕組み

従来の暗号アルゴリズム、例えばECDSAやEd25519、RSAなどは、離散対数問題に基づいて構築されています。これらは、数学的に非常に難解な問題であり、その解を見つけるためには膨大な計算リソースが必要です。要するに、これらのアルゴリズムはぐるぐる回るような数学的構造を持ち、その周期を知ることができなければ総当たりで計算するしかないため、非常に時間がかかるという性質を利用しています。

量子コンピュータのアプローチ

量子コンピュータは並列計算を利用して多くの候補を同時に探索し、周期性のあるパターンを見つけることができます。初めに、候補解は等しい確率振幅で存在します。もしこの状態をそのまま量子状態の崩壊(コラプス)させると、単に一つの解がランダムに得られるだけです。

しかし、量子フーリエ変換を用いることで、これらの候補解を周期的な情報に変換します。この変換により、周期に対応する情報が最も高い確率振幅を持つようになります。結果として、量子コンピュータはこの高い確率振幅を持つ周期情報を効率的に得ることができるのです。

量子フーリエ変換の役割

量子フーリエ変換は、量子コンピュータが特定のパターンを検出するための鍵となる技術です。この手法により、量子コンピュータは従来の計算では非常に困難だった逆計算を効率的に行うことができ、公開鍵や署名の情報から秘密鍵を算出することが可能になります。

量子&AI耐性の重要性

量子コンピュータの登場により、従来の暗号アルゴリズムは破られる可能性があります。そのため、量子&AI耐性のある新しい暗号アルゴリズムの開発が重要となります。これには、量子コンピュータでも効率的に解けない数学的構造や、AIによる攻撃に対しても堅牢な設計が求められます。

XIV, 周期性の鍵と非周期性の鍵のマルチシグ

ECDSAやEd25519、RSAなどは周期性の鍵を用いており、その安全性は周期がわからない点に依存しています。しかし、同じ周期性の鍵でマルチシグをしても意味がないため、ここで非周期性の鍵を利用します。

非周期性の鍵とハッシュ関数

非周期性を持つ鍵としては、暗号学的ハッシュ関数の特性を活用することができます。暗号学的ハッシュ関数の主な特性には以下のものがあります:

  • 衝突耐性:異なる入力が同じ出力(ハッシュ値)を持つ確率が極めて低いこと。
  • 予測不可能性:入力をわずかに変更しただけでも大きく異なるハッシュ値を生成すること(アバランチ効果)。
  • 一方向性:ハッシュ値から元の入力を復元することが非常に難しいこと。

量子&AI耐性用のマルチシグ

これらの特性を持つ鍵を利用して、量子&AI耐性用のマルチシグを実装しました。ECDSAとは異なり、量子&AI耐性用の秘密鍵で署名すると、その署名には一意性があります。もちろん、ハッシュの衝突(異なる入力で同じハッシュ値になる)により、有効な同じ署名が生成される確率はゼロではありませんが、SORA-QAIでは1/2^128という非常に小さな確率となるため、実質的に無視できます。

XV, 従来のECDSA (P2PK, P2PKH, P2SH) とSORA-QAIとの互換性

従来のECDSA (P2PK, P2PKH, P2SH) は、アドレスが “S” から始まる形式を採用しています。一方、SORA-QAIのアドレスは “sora1” から始まります。これらのアドレス形式には互換性があります。

互換性の詳細

  1. アドレスの互換性:
    • 従来の “S” から始まるアドレスから “sora1” アドレスへの送金が可能です。
    • “sora1” アドレスから “S” から始まるアドレスへの送金も可能です。
  2. scriptPubKeyの利用:
    • ユーザーは意識せずに “sora1” の scriptPubKey を利用できます。
    • scriptPubKey の形式に応じた処理がシームレスに行われます。