ブロックチェーンのコンセンサスメカニズムについて
pow、pos、poa、poh
Solana の全方位紹介 —— コンセンサス、ウォレット、エコシステム、契約 | 登链社区 | ブロックチェーン技術コミュニティ
ガイド:Anchor を使用して Solana プログラムを構築する | 登链社区 | ブロックチェーン技術コミュニティ
3 分で Solana チェーン上にトークンを作成するチュートリアル:コード不要の発行プラットフォーム PandaTool がサポート | 登链社区 | ブロックチェーン技術コミュニティ
新しいコンセンサスメカニズム —— 歴史的証明メカニズム(PoH)
世界で最も速い「高速公チェーン」——Solana
Solana の基本的なコンセンサスは PoS(すなわち一般的なステーク証明メカニズム)であり、簡単に言えば、ユーザーが保有する通貨の量と時間(コインエイジ)に基づいて利息の額を決定する制度です。
歴史的証明メカニズム(PoH)は、Solana の巧妙で非常に実用的な革新です。従来のブロックチェーン、例えばビットコインやイーサリアムは、時間と状態を結びつけており、新しいブロックが誕生することでのみ全体の一貫した状態が生成されます。Solana は、ハッシュに基づく時間チェーンと状態を巧妙に分離し、各ブロックのハッシュをリンクするのではなく、ネットワーク内の検証者がブロック内のハッシュ自体をハッシュするというメカニズム、これが PoH(Proof of history)です。
Solana 開発学習ノート (一)——Hello World から始める | 登链社区 | ブロックチェーン技術コミュニティ
Solana プログラミングモデル:Solana 開発入門 | 登链社区 | ブロックチェーン技術コミュニティ
クラスター#
Solana アーキテクチャの核心であり、一群の検証者が共同で取引を処理し、単一の台帳(ledger)を維持します。Solana にはいくつかの異なるクラスターがあり、それぞれ特定の用途があります:
ローカルホスト:デフォルトポート 8899 のローカル開発クラスター。Solana コマンドラインインターフェース(CLI)には、開発者の個々のニーズに応じてカスタマイズ可能な内蔵テスト検証者があり、エアドロップなしで速度制限がありません。
開発ネットワーク Devent:テストと実験のための無価値環境
テストネットワーク Testnet:コアチームが新しい更新や機能を実験する場所であり、性能テストも行えます。
メインネットベータ版 Mainnet Beta:リアルタイムで、許可なしに、実際の通貨取引を生成する場所。
Solana アカウント#
- データを保存するアカウント
- 実行可能なプログラムを保存するアカウント
- ネイティブプログラムを保存するアカウント
機能に基づいて区別できます:
- 実行可能アカウント
- 実行不可アカウント(コードを含まない)
各実行不可アカウントには異なるタイプがあります:
- 関連トークンアカウント - 特定のトークン情報、その残高および所有者情報を含むアカウント(例:アリスは 10USDC を保有)
- システムアカウント - システムプログラムによって作成され、所有されるアカウント
- ステーキングアカウント - トークンを検証者に委任して潜在的な報酬を得るためのアカウント
アカウント構造#
pub struct AccountInfo<'a> {
pub key: &'a Pubkey,
pub lamports: Rc>,
pub data: Rc>,
pub owner: &'a Pubkey,
pub rent_epoch: Epoch,
pub is_signer: bool,
pub is_writable: bool,
pub executable: bool,
}
アカウントはそのアドレス(key)によって識別され、これは一意の 32 バイトの公開鍵です。
lamports フィールドは、このアカウントが保有するlamportsの数を保存します。1 lamport は Solana のネイティブトークン SOL の 10 億分の 1 に相当します。
data は、このアカウントが保存する生データのバイト配列を指します。これは、デジタル資産のメタデータからトークン残高など、あらゆるものを保存でき、プログラムによって変更可能です。
owner フィールドには、このアカウントの所有者が含まれ、プログラムアカウントのアドレスで表されます。アカウント所有者に関するいくつかのルールがあります:
- アカウントの所有者のみがそのデータを変更し、lamports を引き出すことができます
- 誰でもアカウントに lamports を預けることができます
- アカウントの所有者は、アカウントのデータがゼロにリセットされることを前提に、新しい所有者に所有権を移転できます
rent_epoch フィールドは、このアカウントが次のエポック期間に家賃を支払う必要があることを示します。エポックは、リーダーがスロットをスケジュールする数です。オペレーティングシステムの従来のファイルとは異なり、Solana 上のアカウントは lamports で表される寿命を持っています。アカウントの持続的な存在は、その lamport 残高に依存しており、これにより家賃の概念が導入されます。
is_signer フィールドは、トランザクションが関与するアカウントの所有者によって署名されたかどうかを示すブール値です。言い換えれば、これはトランザクションに関与するプログラムアカウントに、アカウントが署名者であるかどうかを伝えます。署名者であることは、アカウントが公開鍵に対応する秘密鍵を保持し、提案されたトランザクションを承認する権限を持つことを意味します。
is_writable フィールドは、アカウントのデータが変更可能かどうかを示すブール値です。Solana は、トランザクションがアカウントを読み取り専用として指定することを許可し、並行処理を促進します。実行時は、異なるプログラムが同時に読み取り専用アカウントにアクセスすることを許可しますが、トランザクション処理の順序を使用して潜在的な書き込み競合を処理します。これにより、競合のないトランザクションのみが並行処理されることが保証されます。
executable フィールドは、アカウントが命令を処理できるかどうかを示すブール値です。これは、プログラムがアカウントに保存されていることを意味し、次のセクションで詳しく説明します。まず、家賃の概念を紹介する必要があります。
家賃(Rent)#
アカウントをアクティブに保ち、検証者のメモリ内にアカウントを保持するために発生するストレージコスト。家賃の徴収はエポックの評価に依存し、これは時間単位で定義された時間の単位です。
- 家賃徴収 - 家賃は各エポックごとに一度徴収されます。アカウントがトランザクションで参照されるときにも家賃が徴収されることがあります
- 家賃配分 - 徴収された家賃の一部は破棄され、これは流通から永久に除去されることを意味します。残りは各スロット後に投票アカウントに配分されます
- 家賃支払い - アカウントが家賃を支払うのに十分な lamports を持っていない場合、そのデータは削除され、アカウントは「ガベージコレクション」と呼ばれるプロセスで解放されます
- 家賃免除 - アカウントが 2 年間の家賃支払いの最低残高を維持している場合、そのアカウントは家賃免除となることができます。すべての新しいアカウントは、この家賃免除の閾値を満たす必要があり、これはアカウントのサイズに依存します
- 家賃回収 - ユーザーはアカウントを閉じて残りの lamports を回収できます。これにより、ユーザーはアカウントに保存されている家賃を回収できます
特定のアカウントサイズの家賃を推定するには、getMinimumBalanceForRentExemption RPC エンドポイントを使用できます。Test Driveは、usize 内のアカウントデータの長さを受け入れることでこのプロセスを簡素化します。Solana rent CLI サブコマンドも、アカウントが家賃免除になるために必要な最低 SOL 額を推定するために使用できます。例えば、この記事を書いている時点で、コマンド solana rent 20000 を実行すると、家賃免除の最低値:0.14009088 SOL が返されます。
Solana アドレス#
Solana には 2 種類の「タイプ」アドレスがあります。
Solana はed25519を使用しており、これはSHA-512(SHA-2)とCurve22519楕円曲線を使用した EdDSA 署名スキームでアドレスを作成します。 32 バイトの公開鍵が生成され、これらは主要なアドレス形式として直接使用でき、ハッシュされていません。
アドレスを有効にするためには、ed25519曲線上の点でなければなりません。しかし、すべてのアドレスがこの曲線から派生する必要はありません。プログラム派生アドレス(PDA)は曲線の外で生成され、これはそれらに対応する秘密鍵がなく、署名にも使用できないことを意味します。PDA はシステムプログラムによって作成され、プログラムがアカウントを管理する必要があるときに使用されます。
Solana アカウントとイーサリアムアカウントの違い#
イーサリアムには 2 種類のアカウント(EOA、コントラクトアカウント)があり、コントラクトアカウントにはコントラクトコードが管理され、トランザクションを開始できません。
Solana の任意のアカウントはプログラムになる可能性があります。コードとデータは分離されています。
Solana には状態がなく、さまざまなデータアカウントと相互作用し、冗長なデプロイなしで行います。異なるプログラム間での相互作用には資産の移転は必要ありません。
Solana は家賃を支払う必要があり、アクティブな状態を維持するために最低残高が必要です。使用されないか、資金が不足していると回収されます。
プログラムとは何か#
プログラムは、BPF Loaderが所有する実行可能なアカウントです。これらはSolana Runtimeによって実行され、このランタイムはトランザクションとプログラムロジックを処理するように設計されています。
Solana プログラミングモデルの特徴:コードとデータは分離されています。プログラムには状態がなく、状態を保存しません。すべてのデータはアカウントに存在し、トランザクションを通じて参照の形でプログラムに渡されます。
Solana プログラムの能力:
- 追加のアカウントを所有する
- 他のアカウントから資金を取得 / 読み取る
- データを変更 / 所有するアカウントから差し引く
プログラムの 2 種類
- オンチェーンプログラム - Solana 上にデプロイされたユーザーが作成したプログラム。これらはそのアップグレード権限によってアップグレード可能で、アップグレード権限は通常プログラムをデプロイしたアカウントです
- ネイティブプログラム - これらのプログラムは Solana コアに統合されています。これらは検証者の実行に必要な基本機能を提供します。ネイティブプログラムは、ネットワーク全体のソフトウェア更新を通じてのみアップグレード可能です。一般的な例にはシステムプログラム、BPF Loader プログラム、および投票プログラムが含まれます。
通常、プログラム開発には Rust 言語が使用され、開発フレームワーク:Anchor を利用してプログラム作成を簡素化します。
トランザクションとは何か#
チェーン上の活動の柱です。プログラムを呼び出し、状態変更を実施するメカニズムです。Solana 上のトランザクションは、一連の命令のバンドルです。
トランザクションの構成:
- 読み取りまたは書き込みを行うアカウントの配列
- 1 つ以上の命令
- 1 つ以上の署名
Solana トランザクション構造は、ネットワーク処理と検証操作に必要な情報を提供します。
pub struct Transaction {
pub signatures: Vec,
pub message: Message,
}
signatures フィールドは、シリアライズされた Message に対応する一連の署名を含みます。各署名は、Message の account_keys リスト内の 1 つのアカウントキーに関連付けられており、fee payer から始まります。fee payer は、トランザクション処理中にトランザクション手数料を支払う責任を持つアカウントです。これは通常、トランザクションを開始するアカウントです。必要な署名の数は、メッセージの MessageHeader 内で定義された num_required_signatures に等しいです。
message 自体は、Message 型の構造体です。以下のように定義されています:
pub struct Message {
pub header: MessageHeader,
pub account_keys: Vec,
pub recent_blockhash: Hash,
pub instructions: Vec,
}
header は、必要な署名の数(すなわち num_required_signatures)、読み取り専用署名者の数、および読み取り専用非署名者の数の 3 つの符号なし 8 ビット整数を含みます。
account_keys フィールドは、トランザクションに関与するすべてのアカウントアドレスを列挙します。読み書きアクセス権を要求するアカウントが最初に表示され、その後に読み取り専用アカウントが続きます。
recent_blockhash は、最近のブロックハッシュで、32 バイトの SHA-256 ハッシュを含みます。これは、クライアントが最後に台帳を観察した時間を示し、最近のトランザクションのライフサイクルとして機能します。検証者は、古いブロックハッシュを持つトランザクションを拒否します。さらに、最近のブロックハッシュの含有は、重複トランザクションを防ぐのに役立ちます。以前と完全に同じトランザクションは拒否されます。何らかの理由で、トランザクションがネットワークに提出される前に長時間署名が必要な場合、永続トランザクション nonceを使用して最近のブロックハッシュの代わりにすることで、ユニークなトランザクションであることを保証できます。
instructions フィールドは、1 つ以上の CompiledInstruction 構造を含み、各構造はネットワーク検証者に特定の操作を実行するよう指示します。
命令#
命令は、単一の Solana プログラム呼び出しに対する指示です。これは、プログラム内で実行されるロジックの最小単位であり、Solana 上で最も基本的な操作単位です。プログラムは、命令から渡されたデータを解釈し、指定されたアカウントに対して操作を行います。Instruction 構造は以下のように定義されています:
pub struct Instruction {
pub program_id: Pubkey,
pub accounts: Vec,
pub data: Vec,
}
program_id フィールドは、実行するプログラムの公開鍵を指定します。これは、命令を処理するプログラムのアドレスです。この公開鍵によって示されるプログラムアカウントの所有者は、プログラムの初期化と実行を担当するローダーを指定します。ローダーはデプロイされると、チェーン上の Solana バイトコード形式(SBF)プログラムを実行可能としてマークします。Solana のランタイムは、実行可能としてマークされていないアカウントを呼び出そうとするトランザクションを拒否します。
accounts フィールドは、命令が読み取ったり書き込んだりする可能性のあるアカウントを列挙します。これらのアカウントは AccountMeta 値として提供する必要があります。命令によってデータが変更される可能性のあるアカウントは、書き込み可能として指定する必要があります。そうでない場合、トランザクションは失敗します。これは、プログラムが所有していないアカウントに書き込むことができないためです。これは、アカウントの lamports を変更する場合にも当てはまります:プログラムが所有していないアカウントから lamports を減算することはトランザクションの失敗を引き起こしますが、任意のアカウントに lamports を追加することは許可されています。accounts フィールドは、プログラムが読み取ったり書き込んだりしないアカウントを指定することもできます。これは、実行時にプログラムの実行をスケジュールするために影響を与えますが、これらのアカウントは無視されます。
data は、プログラムに渡される入力として使用される 8 ビット符号なし整数の一般的なベクターです。このフィールドは重要であり、プログラムが実行するエンコードされた命令を含みます。
Solana の命令データの形式は不明ですが、bincode および borsh(ハッシュ用のバイナリオブジェクト表現シリアライザー)に対するサポートが組み込まれています。シリアル化は、複雑なデータ構造を一連のバイトに変換して転送または保存できるようにするプロセスです。データのエンコード方法の選択は、デコードのオーバーヘッドを考慮する必要があります。これらすべてはチェーン上で発生します。通常、Borshシリアル化が好まれ、bincodeよりも安定した仕様を持ち、JavaScript 実装があり、一般的により効率的です。
プログラムは、命令の構築をサポートするために補助関数を使用します。例えば、システムプログラムは SystemInstruction::Assign 命令を構築するための補助関数を提供します:
pub fn assign(pubkey: &Pubkey, owner: &Pubkey) -> Instruction {
let account_metas = vec![AccountMeta::new(*pubkey, true)];
Instruction::new(
system_program::id(),
&SystemInstruction::Assign { owner: *owner },
account_metas,
)
}
この関数は、処理時に指定されたアカウントの所有者を提供された新しい所有者に変更する命令を構築します。
単一のトランザクションには複数の命令を含めることができ、これらの命令は順次実行され、原子性を持ちます。これは、すべての命令が成功するか、すべてが失敗することを意味します。命令の順序は重要である可能性があります。プログラムは、潜在的な利用を防ぐために、任意の命令シーケンスを安全に処理できるように強化される必要があります。
例えば、去初期化中に、プログラムはその lamport 残高をゼロに設定することでアカウントを去初期化しようとするかもしれません。これは、Solana ランタイムがそのアカウントを削除することを前提としています。この仮定はトランザクション間では有効ですが、命令間やプログラム呼び出しを跨いでは無効です(プログラム呼び出しについては後の記事で説明します)。プログラムは、去初期化プロセス中の潜在的な欠陥を強化するために、アカウントのデータを明示的にゼロにする必要があります。そうしないと、攻撃者は後続の命令を発出して仮定された削除を利用することができ、トランザクションが完了する前にそのアカウントを再利用することができます。