はじめに
今回の内容
本記事は、過去にコンセンサス・ベイスが主宰していたオンラインサロンの記事です。記事は2017年~2018年にかけて執筆されたため、一部は、既に古くなっている可能性があります。あらかじめご了承ください。
本稿では、シャーディングを実装したパブリックチェーンであるZilliqaを用いた、セキュアでスケーラブルなスマートコントラクトの実現について、全3回で解説します。
第1回では、Zilliqaのシャーディング機能の特徴と実現方式について解説します。
第1章 シャーディングの概要
シャーディングとは、シャードと呼ばれるグループごとにデータを分割して、スケーラビリティを向上させるための分散処理技術です。
シャーディングは、主に大量のデータを扱う必要のあるデータベースで用いられてきた技術です。
ブロックチェーンでもデータを分散して保持しますが、多くのブロックチェーンの場合は1つのノードがすべてのデータを保持するレプリケーションに近い仕組みのため、データの冗長性は担保できますが、処理能力を向上されるためのスケーラビリティはありません。
Zilliqaは、シャーディングのアイデアを初めてブロックチェーン上で提案・実装したプロジェクトだと言われています。
Zilliqaのシャーディングは、ネットワークシャーディング(network sharding)、トランザクションシャーディング(transaction sharding)、コンピュテーショナルシャーディング(computational sharding)という3つの側面を持ちます。
ネットワークシャーディング
ネットワークシャーディングとは、ブロックチェーンのノードが構成するネットワークを、複数のサブネットワーク(シャード)に分割することです。Zilliqaのコンセンサスは、それぞれのシャードごとに実行されます。
ネットワークシャーディングにおけるシャードの処理は、他のシャードには影響を与えないため、シャードの数は単純にネットワーク全体のパフォーマンスに比例します。
例えば、1つのシャードの処理能力が秒間10トランザクションだとすると、1000シャードある場合の全体の処理能力は10000トランザクションになります。
トランザクションシャーディング
トランザクションシャーディングとは、ブロックチェーン全体のトランザクションを複数のグループに分割して、分割されたトランザクションを各シャードが処理することです。
Zilliqaでは、アカウントベースのトランザクションシャーディング設計を用いており、トランザクションの送信者と受信者のアカウントに応じて、トランザクションを処理するシャードが決定されます。これにより、同じアカウントの異なるシャードで二重に支払いを行う可能性を防いでいます。
コンピュテーショナルシャーディング
コンピュテーショナルシャーディングとは、あるコントラクトのタスクをサブタスクに分割して、複数のコンピューティングリソースで分散処理することです。
単純な送金トランザクションなどの処理コストはそれほど多くありませんが、例えば電子投票の集計作業をコントラクト上で行ったり、大量のデータに対する検索を行ったりすると、1つのノードで処理するのが難しくなります。コンピュテーショナルシャーディングを行うことで、これまでコントラクト上で実行するのが現実的でなかった種類の処理を実現できる可能性があります。
Zilliqaでは、コンピュテーショナルシャーディングによる分散処理をしやすいスマートコントラクトを実装するために、Scillaと呼ばれる独自言語を開発しています。
Ethereumのシャーディング技術との違い
Ethereumでも、スケーラビリティ向上のためにシャーディングと呼ばれる技術が研究されていますが、Zilliqaの実現しているシャーディングはEthereumの目指すシャーディングとは異なっています。
Ethereumの目指すシャーディングは、ブロックチェーン上の状態(State)を分割するステートシャーディング(State Sharding)です。ステートシャーディングにより、ノードが保持しなければならないデータ量を削減し、長期的なストレージ逼迫の課題に対応することができますが、Zilliqaではステートシャーディングを初期のスコープから外しています。ステートシャーディングは、セキュリティやクロスシャード間のトランザクションのパフォーマンスにまだまだ課題があるためです。
第2章 Zilliqaの合意形成アルゴリズム
Zilliqaでは、シャーディングにおけるセキュリティとパフォーマンスを保証するために、独自の合意形成アルゴリズムを用いています。
Scalable BFT
ネットワークシャーディングを実現するための課題の1つに、全体のノード数と比較して各シャードのノード数が少なくなるため、悪意のあるノードによる攻撃に弱くなる点が挙げられます。
Zilliqaでは、誰もがノードとしてコンセンサスに参加できるパブリックチェーンを目指しているため、悪意のあるノードによる攻撃を防ぐため、各シャードのノード数の下限を600ノードに設定しています。
また、効率的なコンセンサスを実現するため、Zilliqaでは確定的なコンセンサスアルゴリズムであるBFTベースのアルゴリズムを用いています。
BFTは全体のノード数が明確なネットワークにしか使えないアルゴリズムですが、シャード内のノードは事前に把握することができるため、BFTが採用できます。
ただし、BFTやその改良であるPBFTアルゴリズムでは、ノード数が50程度を超えると、ノード間のコミュニケーションコストが増大し、パフォーマンスが低下していまいます。Zilliqaでは各シャードで600個以上のノード数を許容するため、CoSiと呼ばれる共同署名(Cosigning)プロトコルを用いた、独自のスケーラブルなBFTアルゴリズムを開発しています。
Proof of Workによるノード選出
Zilliqaでは、特定の者が大量のアカウントを用意して攻撃を仕掛けるシビル攻撃を防ぐために、各シャードのノードになるための選出にProof of Workプロトコルを用いています。
各アカウントは、まずProof of Workによりノードになる権利の獲得競争をおこないます。獲得競争に勝利したアカウントは、DSコミッティー(Directory Service Committee)と呼ばれるプールに入り、一定期間ごとに各シャードに割り当てられます(図1)。
図1. Proof of WorkによるDSコミッティーの形成
Proof of WorkとBFTを組み合わせたシャーディングにより、不特定多数の参加者を対象としたブロックチェーンで、ファイナリティの確保やパフォーマンスの向上を実現することができます。
第3章 コントラクトのシャーディング
第1章において、Zilliqaではアカウントベースのトランザクションシャーディングをおこなうと解説しました。
単純な送金トランザクションであれば問題ありませんが、コントラクトを含む複雑なトランザクションを実現しようとすると、もう少し工夫が必要です。
トランザクションの3類型
トランザクションシャーディングを実現するために、まずはトランザクションの種類を以下の3つに分類します。
- Category I [U1 to U2]: ユーザからユーザへのトランザクション
- Category II [U to C]: ユーザからコントラクトへのトランザクション
- Category III [U1 to C1 to … to Cn [to U2]]: 上記以外のトランザクション
From | To | 例 | |
Category Ⅰ [U1 to U2] | ユーザー | ユーザー | アリスからボブに5ZIL送金する |
Category Ⅱ [U to C] | ユーザー | コントラクト | アリスからcrowdfundingコントラクトに対して5ZILをデポジットする |
Category Ⅲ [U1 to C1 to … to Cn [to U2]] | それ以外 | それ以外 | アリスからボブに対して、コントラクトを経由して送金する |
Category Iは、ユーザからユーザへのトランザクションです。例えば、アリスからボブに5 ZIL送金する、といったトランザクションがCategory Iに該当します。
Category IIは、ユーザからコントラクトへのトランザクションです。例えば、アリスからcrowdfundingコントラクトに対して5ZILをデポジットする、といったトランザクションがCategory IIに該当します。
Category IIIは、Category IとCategory IIに該当しない任意のトランザクションです。例えば、アリスからボブに対して、コントラクトを経由して送金をおこなう、といったトランザクションがCategory IIIに該当します。
単純なシャーディングの問題
Category Iに該当するトランザクションをシャーディングする場合、ランダムにシャードを割り当ててしまうと、送金者の二重支払いの可能性を防げなくなります。例えば、アリスからボブへの送金をシャード1、アリスからキャロルへの送金をシャード2で処理しようとすると、それぞれのシャードから他のシャードのトランザクションが把握できないので、アリスが持っている残高をボブとキャロルに同時に送れる可能性があります。
この問題を単純に解決するためには、トランザクションの送信者のアドレスごとに担当するシャードを割り当てることで対処できます。例えば、アリスからのトランザクションはつねにシャード1で処理する、という形になるため、異なるシャードでの二重支払いは起こりえません。
しかし、Category IIに該当するトランザクションを、送信者ごとにシャードを割り当てると、コントラクトの状態に矛盾が発生してしまいます。例えば、アリスがコントラクトの状態をAにするトランザクションをシャード1、ボブがコントラクトの状態をBにするトランザクションをシャード2で処理しようとすると、それぞれのシャードが状態を同期しようとしたときに競合が発生し、解決できなくなってしまいます。
そこで、Category IIに該当するトランザクションは、トランザクションの受信者であるコントラクトのアドレスごとに担当するシャードを割り当てることで解決できます。
コントラクトを含むシャーディング
Category IIIに該当するトランザクションは、上記の送信者や受信者のアドレスごとにシャードを割り当てる単純な手法では解決できません。
そこで、Zilliqaでは、Category IとCategory IIに属するトランザクションは一般的なシャードで分担して処理し、Category IIIについては特別なシャードで処理します。また、特別なシャードの処理は、他の一般的なシャードの処理が完了したあとに処理するという条件をつけることで、トランザクション間の矛盾を防ぎます。
Zilliqaにおいて、Category IIIのトランザクションを処理する特別なシャードの候補は、上記で紹介したDSコミッティーです。DSコミッティーにて、すべてのシャードで発生したCategory IIIを含むトランザクションを処理することで、Category IやCategory IIを効率的にシャーディングで分散処理し、全体的なパフォーマンスを向上させることができます。
第2回では、Zilliqaにおいてスケーラブルなコントラクトを実装するためのScilla言語について解説します。
以上。