ミナプロトコル

はじめに

ミナプロトコルは、ブロックチェーンの本来の目的である「真の分散化、スケール、セキュリティ」を実現するために設計されたレイヤー1プロトコルです。

ミナはエレガントな解決策を提供します。それは、ブロックチェーンを簡単に検証可能な一貫したサイズの暗号証明に置き換えることです。これにより、各ユーザーがダウンロードするデータ量が劇的に削減されます。チェーン全体を初期から検証する代わりに、参加者は再帰的ゼロ知識証明(zk-SNARKs)を使用してネットワークとトランザクションを完全に検証します。ノードはその後、小さな証明のみを保存すればよく、チェーン全体を保持する必要はありません。この一貫したサイズのおかげで、ミナは多くのユーザーにスケールし、何年分ものトランザクションデータを蓄積してもアクセス可能な状態を維持します。

ミナプロトコル

ミナプロトコルには以下の3つの公開ネットワークがあります:

  1. mainnet -本番ネットワーク

  2. devnet - mainnetと同じソフトウェアバージョンに基づくテストネットワーク

  3. berkeley - 新機能が試験される開発ネットワーク

ネットワークの識別は以下のGraphQLクエリで確認できます:

query MyQuery {
networkID
}

このセクションでは、ミナプロトコルの動作について説明します。

ノードオペレーター

ノードオペレーターでは、ミナネットワーク上でミナノードを運用する方法について説明します。ミナノードは、ネットワーク内で異なる役割を果たします。

ノード開発者

ノード開発者では、開発者がミナノードに機能を追加したり改善したりする方法について説明します。

エクスチェンジオペレーター

エクスチェンジオペレーターでは、ミナネットワークに接続し、MINAトークンへのアクセスを提供する方法について説明します。

プルーフ・オブ・ステーク

ミナに実装されているプルーフ・オブ・ステークのコンセンサスメカニズムは、Ouroboros Praosプロトコルを基にしたもので、簡潔なブロックチェーンに適応するために拡張および若干の修正が加えられています。本ドキュメントでは、Ouroborosプルーフ・オブ・ステークの仕組みについて概要を説明し、変更点や追加部分について詳しく述べます。Ouroborosプロトコルの詳細な説明については、オリジナルのOuroboros論文やPraos論文をご参照ください。

Praos、Genesis、およびミナに関する注記

正式には、ミナのOuroboros実装はPraosの拡張版です。ただし、Praosをさらに拡張した最新の論文「Ouroboros Genesis」が存在します。この拡張版は、ロングフォーク攻撃に関する脆弱性を修正しますが、同論文で説明されているような簡潔なブロックチェーンプロトコルでは実装できません。その代わりに、ミナではロングフォーク攻撃を防ぐための新しい簡潔な方法を導入しています。

Praosへの追加部分

エポック台帳の最適化

Ouroborosでは、ノードはVRF(検証可能ランダム関数)評価を行うために、前エポックの開始時点の台帳を構築/保持する必要があります。これは、単にVRF出力を持つだけでは、ブロックが正当に提案されたことを証明するには不十分だからです。ブロックが提案したノードによって正当に勝ち取られたことを確認するには、VRF出力が提案者のステークに基づく閾値以下である必要があります。このステークは台帳内の通貨総量に比例します。

ミナのような簡潔なプロトコルでは、このような過去の台帳を構築/保持することは容易ではありません。非簡潔なブロックチェーンとは異なり、ノードは過去のチェーンの一部を任意に要求して興味のある情報を再構築することができません。そのため、この機能を単純に実装すると、任意の時点で台帳全体の3つのコピーを保持する必要があり、また情報にアクセスするために少なくとも2つのエポック分オンラインで待つ必要が生じます。しかし、工夫をすることで、このプロセスを大幅に最適化できます。

ブロックの構成要素

ブロックは、ネットワークの状態を拡張するトランザクションとコンセンサス情報の集合です。ミナのブロックには、ネットワークの現在の状態が完全に有効であることを証明するプローフが含まれています。

ミナのブロックは以下で構成されます:

ノードがピアからブロックを受信すると、まずそのブロックが検証され、既存の状態に適用され、ノードの遷移フロンティアに追加されます。そのブロックがコンセンサスルールに基づいてブロックチェーンの長さを増加させる場合、ノードの最良のチップが更新され、遷移フロンティアのルートが移動して、遷移フロンティアにk個のブロックのみを保持するようにします。

チップ

ミナでは、ブロックは「遷移(トランジション)」と同義です。この遷移(ブロック)がピアから受信された場合、それは外部遷移と呼ばれます。一方、ローカルで生成および適用されたものは内部遷移と呼ばれます。

プロトコル状態

プロトコル状態は、前回のプロトコル状態ハッシュと以下を含むボディで構成されます:

各ブロックには前回のブロックのプロトコル状態ハッシュが含まれており、それによってブロックが連結され、改ざん不可能なチェーンを形成します。

プロトコル状態ハッシュは、前回の状態およびボディのハッシュから計算され、各ブロックのユニークな識別子として機能します。

ジェネシス状態ハッシュ

ジェネシス状態ハッシュは、ジェネシスプロトコル状態のプロトコル状態ハッシュを指します。

ブロックチェーン状態

ブロックチェーン状態は以下で構成されます:

  • ステージド・レジャー・ハッシュ

  • ジェネシス・レジャー・ハッシュ

  • 元帳証明書

  • タイムスタンプ

  • ボディリファレンス

Ledger proof statement

The ledger proof statement is comprised of:

  • スナークド・レジャー・ハッシュ

  • 署名付き金額

  • 未決コインベーススタック

  • 手数料の過剰分

  • Sokダイジェスト

  • ローカル状態

コンセンサス状態

コンセンサス状態は、以下で構成されています:

  • ブロックチェーンの長さ

  • エポックカウント

  • 最小ウィンドウ密度

  • サブウィンドウ密度

  • 最後のVRF出力

  • 総通貨量

  • 現在のグローバルスロット

  • ジェネシス以来のグローバルスロット

  • ステーキングエポックデータ

  • 次エポックデータ

  • 同じチェックポイントウィンドウ内に祖先が存在するか

  • ブロックステークの勝者

  • ブロック作成者

  • コインベース受領者

  • スーパーチャージコインベース

コンセンサス定数

コンセンサスのパラメータを定義する定数:

  • k

  • デルタ

  • スロット/サブウィンドウ

  • スロット/ウィンドウ

  • サブウィンドウ/ウィンドウ

  • スロット/エポック

  • 猶予期間スロット

  • 猶予期間終了

  • 年間のチェックポイントウィンドウスロット

  • チェックポイントウィンドウサイズ

  • ブロックウィンドウ期間(ミリ秒)

  • スロット期間(ミリ秒)

  • エポック期間

プロトコル状態証明

プロトコル状態証明は、ブロックプロデューサーによって生成された新しいプロトコル状態が有効であることを証明するブロックチェーン証明です。再帰的SNARKsの使用により、このプロトコル状態証明はチェーン全体の履歴が有効であることを証明します。

ステージドレジャー差分

ブロックプロデューサーがスロットを獲得してブロックを生成する際、トランザクションおよびSNARKプールから必要なSNARK作業を選択します。次に提案されたブロックチェーンの次の状態を作成し、ステージドレジャーの差分を作成します。この差分には以下が含まれます:

  • ブロックに含まれるトランザクション

  • 追加された以前のトランザクションのためにSNARKワーカーによって生成されたSNARK証明のリスト

  • 保留中のコインベース

ステージドレジャーは、まだSNARKが利用可能でないトランザクション(支払い、コインベース、証明手数料の支払い)が適用された保留中アカウントデータベースと見なすことができます。ステージドレジャーは、アカウント状態(台帳)と、SNARK証明のないトランザクションのトランザクションキューであるスキャン状態で構成されます。

デルタ遷移チェーン証明

新しく生成されたブロックをネットワーク全体にブロードキャストまたはゴシップする際、悪条件下のネットワークに対応するための許容遅延があります。デルタ遷移チェーン証明は、ブロックが割り当てられたスロット時間内に生成されたことを証明します。

例のブロック

{
"external_transition": {
  "protocol_state": {
    "previous_state_hash": "3NLKJLNbD7rBAbGdjZz3tfNBPYxUJJaLmwCP9jMKR65KSz4RKV6b",
    "body": {
      "genesis_state_hash": "3NLxYrjb7zmHdoFgBrubCN8ijM8v7eT8kvLiPLc9DHt3M8XrDDEG",
      "blockchain_state": {
        "staged_ledger_hash": {
          "non_snark": {
            "ledger_hash": "jxV4SS44wHUVrGEucCsfxLisZyUC5QddsiokGH3kz5xm2hJWZ25",
            "aux_hash": "UmosfM82dH5xzqdckXgA1JoAvJ5tLxch2wsty4sXmiEPKnPTPq",
            "pending_coinbase_aux": "WLo8mDN6oBUTSyBkFCy7Fky7Na5fN4R6oGq4HMf3YoHCAj4cwY"
          },
          "pending_coinbase_hash": "2mze7iXKwA9JAqVDC1MVvgWfJDgvbgSexKtuShdkgqMfv1tjATQQ"
        },
        "ledger_proof_statement": {
          "connecting_ledger_right": "jwbhUympjiAFLPi7w3Cg9GEeNCfACEpPxrTp45XZt3BDA5UNV8S",
          "sok_digest": null,
          "target": {
            "local_state": {
              "full_transaction_commitment": "0x0000000000000000000000000000000000000000000000000000000000000000",
              "call_stack": "0x0000000000000000000000000000000000000000000000000000000000000000",
              "token_id": "wSHV2S4qX9jFsLjQo8r1BsMLH2ZRKsZx6EJd1sbozGPieEC4Jf",
              "excess": {
                "sgn": [
                  "Pos"
                ],
                "magnitude": "0"
              },
              "success": true,
              "stack_frame": "0x0641662E94D68EC970D0AFC059D02729BBF4A2CD88C548CCD9FB1E26E570C66C",
              "will_succeed": true,
              "account_update_index": "0",
              "supply_increase": {
                "sgn": [
                  "Pos"
                ],
                "magnitude": "0"
              },
              "ledger": "jw6bz2wud1N6itRUHZ5ypo3267stk4UgzkiuWtAMPRZo9g4Udyd",
              "failure_status_tbl": [],
              "transaction_commitment": "0x0000000000000000000000000000000000000000000000000000000000000000"
            },
            "pending_coinbase_stack": {
              "state": {
                "init": "4Yx5U3t3EYQycZ91yj4478bHkLwGkhDHnPbCY9TxgUk69SQityej",
                "curr": "4Yx5U3t3EYQycZ91yj4478bHkLwGkhDHnPbCY9TxgUk69SQityej"
              },
              "data": "4QNrZFBTDQCPfEZqBZsaPYx8qdaNFv1nebUyCUsQW9QUJqyuD3un"
            },
            "first_pass_ledger": "jwbhUympjiAFLPi7w3Cg9GEeNCfACEpPxrTp45XZt3BDA5UNV8S",
            "second_pass_ledger": "jwbhUympjiAFLPi7w3Cg9GEeNCfACEpPxrTp45XZt3BDA5UNV8S"
          },
          "fee_excess": [
            {
              "token": "wSHV2S4qX9jFsLjQo8r1BsMLH2ZRKsZx6EJd1sbozGPieEC4Jf",
              "amount": {
                "sgn": [
                  "Pos"
                ],
                "magnitude": "0"
              }
            },
            {
              "amount": {
                "sgn": [
                  "Pos"
                ],
                "magnitude": "0"
              },
              "token": "wSHV2S4qX9jFsLjQo8r1BsMLH2ZRKsZx6EJd1sbozGPieEC4Jf"
            }
          ],
          "source": {
            "second_pass_ledger": "jwbhUympjiAFLPi7w3Cg9GEeNCfACEpPxrTp45XZt3BDA5UNV8S",
            "local_state": {
              "account_update_index": "0",
              "supply_increase": {
                "sgn": [
                  "Pos"
                ],
                "magnitude": "0"
              },
              "stack_frame": "0x0641662E94D68EC970D0AFC059D02729BBF4A2CD88C548CCD9FB1E26E570C66C",
              "ledger": "jw6bz2wud1N6itRUHZ5ypo3267stk4UgzkiuWtAMPRZo9g4Udyd",
              "transaction_commitment": "0x0000000000000000000000000000000000000000000000000000000000000000",
              "token_id": "wSHV2S4qX9jFsLjQo8r1BsMLH2ZRKsZx6EJd1sbozGPieEC4Jf",
              "excess": {
                "sgn": [
                  "Pos"
                ],
                "magnitude": "0"
              },
              "call_stack": "0x0000000000000000000000000000000000000000000000000000000000000000",
              "will_succeed": true,
              "full_transaction_commitment": "0x0000000000000000000000000000000000000000000000000000000000000000",
              "failure_status_tbl": [],
              "success": true
            },
            "first_pass_ledger": "jwbhUympjiAFLPi7w3Cg9GEeNCfACEpPxrTp45XZt3BDA5UNV8S",
            "pending_coinbase_stack": {
              "data": "4QNrZFBTDQCPfEZqBZsaPYx8qdaNFv1nebUyCUsQW9QUJqyuD3un",
              "state": {
                "curr": "4Yx5U3t3EYQycZ91yj4478bHkLwGkhDHnPbCY9TxgUk69SQityej",
                "init": "4Yx5U3t3EYQycZ91yj4478bHkLwGkhDHnPbCY9TxgUk69SQityej"
              }
            }
          },
          "supply_increase": {
            "sgn": [
              "Pos"
            ],
            "magnitude": "0"
          },
          "connecting_ledger_left": "jwbhUympjiAFLPi7w3Cg9GEeNCfACEpPxrTp45XZt3BDA5UNV8S"
        },
        "body_reference": "b94b2580ca80f27c9655289579a0d71df0b7604dfa7c404e6c309cccf7730d2f",
        "snarked_ledger_hash": "jx9171AbMApHNG1guAcKct1E6nyUFweA7M4ZPCjBZpgNNrE21Nj",
        "genesis_ledger_hash": "jxX6VJ84HaafrKozFRA4qjnni4aPXqXC2H5vQLKSryNpKTXuz1R",
        "snarked_next_available_token": "2",
        "timestamp": "1611691710000"
      },
      "consensus_state": {
        "blockchain_length": "3852",
        "epoch_count": "1",
        "min_window_density": "1",
        "sub_window_densities": [
          "3",
          "1",
          "3",
          "1",
          "4",
          "2",
          "1",
          "2",
          "2",
          "4",
          "5"
        ],
        "last_vrf_output": "g_1vrXSXLhvn1e4Ap1Ey5e8yh3PFMJT0vZyhZLlTBAA=",
        "total_currency": "167255800000001000",
        "curr_global_slot": {
          "slot_number": "12978",
          "slots_per_epoch": "7140"
        },
        "global_slot_since_genesis": "12978",
        "staking_epoch_data": {
          "ledger": {
            "hash": "jxX6VJ84HaafrKozFRA4qjnni4aPXqXC2H5vQLKSryNpKTXuz1R",
            "total_currency": "165950000000001000"
          },
          "seed": "2vb1Mjvydod6sEwn7qpbejKCfRqugMgyG3MHXXRKcAkwQLRs9fj8",
          "start_checkpoint": "3NK2tkzqqK5spR2sZ7tujjqPksL45M3UUrcA4WhCkeiPtnugyE2x",
          "lock_checkpoint": "3NK5G8Xqn1Prh3XoTyZ2tqntJC6X2nVwruv5mEJCL3GaTk7jKUNo",
          "epoch_length": "1769"
        },
        "next_epoch_data": {
          "ledger": {
            "hash": "jx7XXjRfJj2mGXmiHQmpm6ZgTxz14udpugyFtw4DefJFpie7apN",
            "total_currency": "166537000000001000"
          },
          "seed": "2vavBR2GfJWvWkpC7yGJQFnts18nHaFjdVEr84r1Y9DQXvnJRhmd",
          "start_checkpoint": "3NLdAqxtBRYxYbCWMXxGu6j1hGDrpQwGkBDF9QvGxmtpziXQDADu",
          "lock_checkpoint": "3NL4Eis1pS1yrPdfCbiJcpCCYsHuXY3ZgEzHojPnFWfMK9gKmhZh",
          "epoch_length": "2084"
        },
        "has_ancestor_in_same_checkpoint_window": true,
        "block_stake_winner": "B62qpBrUYW8SHcKTFWLbHKD7d3FqYFvGRBaWRLQCgsr3V9pwsPSd7Ms",
        "block_creator": "B62qpBrUYW8SHcKTFWLbHKD7d3FqYFvGRBaWRLQCgsr3V9pwsPSd7Ms",
        "coinbase_receiver": "B62qpBrUYW8SHcKTFWLbHKD7d3FqYFvGRBaWRLQCgsr3V9pwsPSd7Ms",
        "supercharge_coinbase": true
      },
      "constants": {
        "k": "290",
        "slots_per_epoch": "7140",
        "slots_per_sub_window": "7",
        "delta": "0",
        "genesis_state_timestamp": "1609355670000"
      }
    }
  },
  "protocol_state_proof": "<opaque>",
  "staged_ledger_diff": "<opaque>",
  "delta_transition_chain_proof": "<opaque>",
  "current_protocol_version": "1.1.0",
  "proposed_protocol_version": "<None>"
}
}

ブロックプロデューサー

Minaにおけるブロックプロデューサーの役割は、合意形成を達成し、ブロックチェーンにセキュリティを提供することです。ブロックプロデューサーは、ネットワーク上でブロードキャストされた最近のトランザクションと、チェーンの現在の状態が有効であることを証明するブロックチェーン証明を含む新しいブロックを作成する責任を負います。

Minaでは、誰でもブロックプロデューサーになることができます。ブロックを生成するチャンスは、賭けた資金に比例しており、参加者の数に制限はありません。資金はロックされず、スラッシングの対象にもなりません。

資金をステークし、必要なブロックチェーン証明を生成する見返りとして、生成されたブロックが正規チェーンに含まれる場合、コインベースやトランザクション手数料という形で報酬が与えられます。ただし、必要なトランザクションSNARK作業を購入するために支払った手数料は差し引かれます。

ブロックを成功裏に生成するためには、ブロックプロデューサーはブロックチェーンの現在の状態を保持している必要があります。また、プロデューサーはスロット時間内にブロックチェーンSNARKを生成するのに十分な計算能力を持ち、生成されたブロックをネットワーク合意パラメータで定義された許容遅延内にピアにブロードキャストできる必要があります。

ブロックプロデューサーを選択

スロットでブロックを生成する機会は、検証可能な乱数関数(VRF)によって決定されます。この関数を宝くじのようなものと考えてください。各ブロックプロデューサーは独立してこのVRFを各スロットに対して実行し、その出力がプロデューサーのステークに比例した閾値を超えた場合、そのスロットでブロックを生成するチャンスを得ます。

このプロセスは秘密裏に行われるため、プライベートキーの保有者だけがVRFの出力を知ることができ、自分がいつブロックを生成するかを把握できます。この選択プロセスはセキュリティを向上させます。たとえば、特定のスロットで知られたブロックプロデューサーを標的としたサービス拒否(DoS)攻撃やターゲット攻撃を行うことが不可能だからです。その結果、複数のプロデューサーが同じスロットで選択されることがあります。同じスロットで複数のプロデューサーが有効なブロックを生成した場合、短い範囲のフォークが生成され、合意ルールに基づいて最長のチェーンが選択されます。

ステークの分布は、現在のエポック-2の最終ブロックにおけるSNARK化された台帳から決定されるため、最近取得または委任されたステークには遅延があります。例えば、現在のエポックが10の場合、ステーキング分布は8番目のエポックの最終ブロックのSNARK化された台帳に基づいて決定されます。

ログ内でVRFの出力を確認するには、「Checking VRF evaluations」を探してください。

ブロックの生成

ブロックプロデューサーがスロットでブロックを生成するよう選ばれた場合、次の操作を実行します:

  • 新しいブロックを構築するために、トランジションフロンティア(ブロックのローカルストア)から現在の最良のティップを選択します。

トランザクションプールおよびSNARKプールから必要なトランザクションとSNARK作業を選択します。

ブロックプロデューサーは、追加するトランザクションと同量以上のSNARK作業を購入する必要があります。ユーザーのトランザクションに加えて、ブロックプロデューサーは、ブロックを生成する報酬としてコインベーストランザクションおよびSNARKワーカーへの支払いを行う手数料転送を追加する必要があります。

  • ブロックチェーンの提案される次の状態を生成します。

    • アカウント台帳とスキャン状態(まだ証明が行われていないトランザクションのキュー)を含むステージド台帳の差分を作成します。

    • この差分を既存のステージド台帳に適用し、新しい状態を生成します。

新しい状態が有効であることを証明するブロックチェーン証明を作成します。

このSNARKは、以前のプロトコル状態証明も検証します。

  • ネットワーク合意パラメータで定義された許容遅延内にブロックが受信された場合、そのブロックの有効性を証明するデルタトランジションチェーン証明を作成します。

  • 新たに生成された状態をローカルに適用し、既存のトランジションフロンティアに追加します。

  • ブロック(外部トランジションを呼び出す)をピアにブロードキャストします。

ステークの委任

委任された資金は使用できず、いつでも元のアカウントにステークを再委任することで解除することができます。

SNARKワーカー

多くのプロトコルでは、主に1つの主要なノードオペレーターグループ(通常はマイナー、バリデーター、またはブロックプロデューサーと呼ばれる)のみが存在しますが、Minaには2番目のグループであるSNARKワーカーが存在します。

SNARKワーカーは、ネットワーク内のトランザクションのSNARK証明を生成する役割を担い、Minaネットワークの健全性を支える重要な存在です。これらの証明を生成することで、Minaブロックチェーンの簡潔性を維持します。

以下では、SNARKワーカーが必要とされる理由、経済的インセンティブの仕組み、SNARK作業の実行に関する詳細について説明します。

注記:

このセクションでは、zk-SNARKの理論については触れません。SNARKに関する深い知識は必須ではありませんが、一般的な仕組みや用途を理解していると役立ちます。「zk-SNARKとは?」という入門資料で詳しく学ぶことができます。

なぜSNARKワーカーが必要なのか?

Minaの特徴は、その簡潔なブロックチェーンにあります。各ブロックプロデューサーは、新しいブロックをネットワークに提案する際、そのブロックとともにzk-SNARKを含める必要があります。これにより、ノードは最終決定された履歴データをすべて破棄し、SNARKのみを保持することが可能になります。Minaプロトコルを初めて学ぶ方には、この動画がおすすめです。

しかし、MinaにおいてブロックプロデューサーがブロックのSNARK証明を生成するだけでは不十分です。トランザクションもSNARK化する必要があります。その理由は、ブロックチェーンのSNARKが、新しいブロックに含まれるトランザクションの有効性については何も主張しないためです。

たとえば、ブロックチェーンの現在の先端が状態ハッシュ a6f8792226... であり、新しいブロックが状態ハッシュ 0ffdcf284f... を持つ場合を考えてみましょう。このブロックには、ブロックプロデューサーが選択したすべてのトランザクションと関連するメタデータが含まれます。また、次の声明を検証するためのSNARKも付属しています。

「状態ハッシュが 0ffdcf284f のブロックが存在し、これは状態ハッシュ a6f8792226 を持つ以前のベストティップを拡張するブロックチェーンである。」

この声明では、新しいブロックに含まれるトランザクションの有効性については何も言及されていません。このSNARKを信じ、それ以上の検証を行わない場合、悪意のあるブロックプロデューサーがこのブロックを送信して騙そうとする可能性があります。幸いにも、生のブロックが存在するため、各トランザクションを確認してその有効性を確認できます。しかし、各ブロックを検証せずに証明のみを受け取りたいと考えるネットワーク内のノードについてはどうでしょうか?

トランザクションのSNARK化

Minaブロックチェーンにおいて、信頼に依存せずにノードが動作するためには、各ノードがトランザクションを再生することなくチェーンの状態を検証できることが重要です。この仕組みを実現するには、ブロックチェーンのSNARKだけでは不十分です。トランザクションも有効であることを保証する必要があります。SNARKはその目的に非常に適していますが、素朴な提案として、各トランザクションが到着した際に個別にSNARKを生成し、それらを結合するという方法が考えられます。

しかし、SNARK証明を生成するのは計算コストが非常に高い作業です。各トランザクションに対して逐次的にSNARKを計算しなければならない場合、スループットは非常に低くなり、ブロック生成時間が大幅に延びてしまいます。さらに、現実世界ではトランザクションは非同期的に到着するため、次に処理すべき作業を予測するのは難しいです。

幸いなことに、SNARKには以下の2つの特性があります:

  1. 証明のマージ - 2つの証明を結合してマージ証明を生成することが可能です。

  2. マージの結合性 - マージ証明は結合の順序に関係なく同一になります。

これらの2つの特性により、並列処理を活用できるようになります。証明が統合でき、どのように統合されるかは重要でないので、SNARK証明は並列で生成できます。最初に完成した証明は、進行中の証明と後で統合できます。これは、下の行(葉)が個別の取引証明で構成され、各親行がそれぞれの統合証明のセットである二分木として視覚化できます。これらをすべてルートまで統合でき、ルートはすべての取引を適用することによって実行された状態更新を表します。

さらに、SNARK証明は互いに依存せず、並列処理を活用できるため、誰でも作業を行うことができます!最終的には、分散型作業プールは許可なしで利用できます。余剰計算リソースを持っている誰でもSNARKワーカーとしてネットワークに参加し、SNARK化が必要な取引を観察して計算リソースを提供できます。そしてもちろん、その作業には「スナークマーケット」と呼ばれる場所で報酬が支払われます。

注意: このSNARK作業スキームがどのように進化したかの詳細を学びたい場合は、こちらのビデオ「High Throughput with Slow Snarks」をぜひご覧ください。また、関数型プログラミングやスキャン状態(上記で説明したツリー構造)の詳細に興味がある方には、技術的な詳細を解説したビデオもご用意しています。

スナークマーケット

SNARK作業における重要な動的要素は次の通りです:

ブロックプロデューサーは、自らのブロック報酬を用いてSNARKワークをSNARKワーカーから購入します。

SNARKの価格設定にはプロトコルが関与しておらず、SNARKワーカーがSNARKを生成するためのプロトコルレベルの報酬もありません。このインセンティブは完全にピアツーピアであり、公の市場、つまりスナークマーケット(Snarketplace)内で動的に確立されます。

では、なぜブロックプロデューサーがSNARKを購入する必要があるのでしょうか?当然の疑問です。その理由は、前述の通り、Minaブロックチェーンの先頭の状態が有効であることを確実に知るためには、トランザクションがSNARK化される必要があるからです。しかし、トランザクションを追加するだけでSNARK化を同じ速度で行わなければ、未完了の作業が蓄積されてしまいます。安定した均衡状態を達成するためには、新たに追加される作業と同じ速度で作業を処理する必要があります。

ブロックプロデューサーは、トランザクション手数料やコインベーストランザクションを通じてブロックにトランザクションを含めることで利益を得るため、その分、完了済みのSNARK作業を購入してトランザクションを相殺する責任があります。これにより、SNARK作業への需要が生まれます。ただし、ブロックプロデューサーの目標は、スナークマーケットで可能な限り低価格でSNARK作業を購入することです。一方、SNARKワーカーは利益を最大化しながら、SNARK作業を販売することを目指します。この2つの役割が市場の両側として機能し、時間とともにSNARK作業の市場価格で均衡を確立していきます。

SNARK作業の価格設定方法

スナークマーケット(Snarketplace)は、需要と供給の単純な法則に従って動的に再バランスされることが予想されます。個々のSNARK作業は異なるトランザクションに対応しますが、大局的に見れば、SNARK作業は主にコモディティ(どのSNARKワーカーが生成しても同じ結果になる)とみなされます。しかし、いくつかのニュアンスがあるため、価格戦略における指針を以下に挙げます:

  • 市場価格がXの場合の戦略 市場価格がXである場合、運営コストを差し引いて利益が出るなら、Xよりもわずかに低い価格(例:X - 1)でSNARK作業を販売することが効果的です。

  • ブロックプロデューサー(BP)は、同じSNARKワーカーからより多くのSNARK作業ユニットを購入するよう奨励されています。理由は、ブロックに含める必要がある手数料転送トランザクションが1つだけで済むからです。

    • 基本的に、ブロックプロデューサー(BP)がSNARKワーカーに支払いを行う方法は、「手数料転送」と呼ばれる特別なタイプのトランザクションを通じて行われます。BPのインセンティブは手数料転送の数を最小限に抑えることであり、それぞれの手数料転送がブロックに追加される個別のトランザクションであるためです(その結果、さらに多くのSNARK作業で相殺する必要があります)。したがって、最善のケースは、同じSNARKワーカーからSNARK作業をまとめて購入することです。
  • 一部のSNARK作業は他の作業よりも優先して完了することが重要になります。なぜなら、それによってツリー全体分のメモリを解放できるからです(詳細は上記の動画を参照してください)。これを可能にするのが、さまざまな作業選択方法です。現在、ネイティブでサポートされている方法は以下の3つです:順次方式、ランダム方式、およびランダムオフセットを伴う順次方式。ただし、これらの方法は動的市場を活用していません。この分野はMinaコミュニティが解決策を開発できる改善の余地がある領域です。

SNARKおよび価格に関するすべてのデータは公開されているため、スナークマーケットプレイスを確認する方法はいくつかあります。一例としてGraphQL APIを使用する方法があり、他の選択肢としてCLIを使用する方法や、SNARKメンプール内のSNARKを追跡するカスタムソリューションを構築する方法があります。

スキャンステート

スキャンステートは、トランザクションSNARKの生成をブロックプロデューサーからSNARKワーカーに切り離すことを可能にするデータ構造です。

ブロックプロデューサーがトランザクションSNARKを生成する必要がないため、トランザクションスループットに関係なくブロック生成時間を一定に保つことができます。スキャンステートのデータ構造は、トランザクションSNARKの証明生成を並列化し、複数の競合するSNARKワーカーによって完了できるようにします。

スキャンステートは完全二分木の森で構成されており、木の各ノードがSNARKワーカーによって完了されるべきジョブとなっています。スキャンステートは定期的に木の頂点から単一の証明を返し、それが木の基底にあるすべてのトランザクションの正確性を証明します。

ブロックプロデューサーは、この台帳証明を彼らが生成するブロックチェーンSNARKに含めます。このSNARKは、チェーンの現在の状態が有効であることを証明し、SNARK化された台帳に含まれるすべてのトランザクションの有効性を保証します。

その結果、トランザクションスループットに関係なく、ブロック生成時間を一定に保つことができます。スキャンステートは、目的のトランザクションスループットに対応するよう調整する能力を備えています。

ヒント

定常状態では、すべてのスロットが埋まり、必要なすべての証明が完了すると、各ブロックごとに台帳証明が生成されます。

トランザクションの追加

ブロックを構築する際、ブロックプロデューサーはスキャン状態の定数で定義された最大数までトランザクションを追加できます。ブロックプロデューサーは利用可能なトランザクション手数料を取得し、トランザクションを含めることでコインベース報酬を受け取ることができます。追加された各トランザクションは新たなベースジョブに変換され、スキャン状態に追加されます。

追加された各トランザクションに対して、ブロックプロデューサーはスキャン状態にすでに存在するジョブに対応する完了済みSNARK作業を同等の量だけ含めなければなりません。これらの完了済みジョブがスキャン状態に追加されると、新しいマージジョブが生成されます。ただし、ルートノードの場合、その証明が結果として返されます。

ブロックプロデューサーは自ら作業を完了する代わりに、SNARKプール(スナークマーケットプレイス)内の入札から任意のSNARKワーカーから完了済みの作業を購入することができます。

スキャン状態の定数

以下の定数がスキャン状態の構造と挙動を決定します:

‘‘‘transactioncapacitylog_2’’’

work_delay

transaction_capacity_log_2 定数は、ブロックに含めることができる最大トランザクション数を定義します。

maxnooftransactions = 2^{transactioncapacitylog2}

work_delay は、SNARK作業がSNARKワーカーによって完了するための十分な時間を確保します。ブロックプロデューサーは、完了済みの証明が利用可能でない場合、トランザクションを含めることはできません。work_delay によって、スキャン状態内に存在し得る最大ツリー数は次のように定義されます:

maxnumberoftrees = (transactioncapacitylog2 + 1) * (work_delay + 1) + 1

ブロックごとに含めることができる最大証明数は次のように定義されます:

maxnumberofproofs = 2^{transaction\capacitylog2 + 1} - 1

これらのスキャン状態の制約により、次が保証されます:

  • ブロックごとに出力される証明は1つだけであること

  • 子ノードに対応する証明が追加された後に更新されるマージノードは常に空であること

最大トランザクション数は固定される可能性がありますが、この数はトランザクションスループットに応じて動的に調整されます。このため、スキャン状態は無制限のトランザクションスループットを処理できますが、トランザクション証明の遅延が対数的に増加するコストが伴います。

max_no_of_transactions = 4 および work_delay = 1 のスキャン状態を考えます。この場合、完了すべき最大作業量は7に等しく、最大7個のツリーが存在できます。ジェネシス時点では、スキャン状態は空です。

ブロック1: ブロックプロデューサーがスキャン状態に4つのトランザクション(B1)を追加します。これらのトランザクションは、最初のツリーの基底部分を埋めます。

ブロック2: 2番目のブロックでは、ブロックプロデューサーがさらに4つのトランザクション(B2)を追加します。これらは2番目のツリーに追加され、再び基底部分が埋まります。この段階では、作業遅延が1ブロックであるため、証明は必要ありません。

ブロック3: 3番目のブロックでは、ブロックプロデューサーが3番目のツリーに4つのトランザクション(B3)を追加しますが、最初のツリーの4つの証明を含める必要があります。これらの基底証明を追加した結果、2つの新しいM3マージジョブが作成されます。

tip

BまたはMは、それぞれ基底ジョブまたはマージジョブを表し、数字はスキャン状態に追加された順序を示します。

ブロック4: 4番目のブロックでは、ブロックプロデューサーが4つのトランザクション(B4)を4番目のツリーの基底部分に追加します。ブロック2で追加された作業に対応する4つの証明を含める必要があります。その結果として、再び2つのM4マージジョブが作成されます。

tip

保留中の作業(オレンジ色で表示)は、SNARKワーカーが完了する必要のある作業です。SNARKワーカーは完了した作業をSNARKプールに提出します。複数のSNARKワーカーが作業を完了できますが、SNARKプールには最も低い料金の作業のみが残り、ブロックプロデューサーが購入できます。

ブロック5: 5番目のブロックでは、さらに4つのトランザクション(B5)が追加され、ツリー5の基底部分が埋まります。また、6つの証明(B3およびM3)が含まれます。M3マージジョブの結果、最初のツリーに対する最終的な保留中のマージジョブ(M5)が作成されます。

ブロック6: 6番目のブロックでは、さらに4つのトランザクション(B6)が追加され、ツリー6の基底部分が埋まります。6つの証明(B4およびM4)が含まれ、その結果として3つの新しいマージジョブ(M6)が作成されます。

ブロック7: 7番目のブロックでは、ブロックプロデューサーがさらに4つのトランザクション(B7)を追加し、7番目のツリーの基底部分を埋めます。スキャン状態の定数に従い、最大で7つのツリーが存在できます。このブロックには、最大数の証明(7つ、B5およびM5)が含まれています。これらの証明が含まれることで、3つの新しいマージジョブ(M7)が作成され、さらに最上位のM5証明がスキャン状態から出力されます。

最初のツリーから出力された証明は、ブロック1で追加されたトランザクションに対応する台帳証明です。ツリーの内容はその後削除され、新しいトランザクションを追加するスペースが作られます。

ブロック8: 8番目のブロックでは、ブロックプロデューサーが2つのトランザクション(B8)を追加し、4つの証明(B6)を含めます。これらの証明により、2つの新しいマージジョブ(M8)が作成されます。2つのトランザクションを追加する場合、4つの証明だけが必要です。

tip

SNARK作業は、通常、2つの作業IDを含む作業パッケージにまとめられます。ただし、ツリーの最終的なルート証明は例外です。トランザクションごとの割り当てられる作業は2つの証明であり、トランザクションの数と購入されるSNARK作業の量が等しくなるように保証されます。

ブロック9: 9番目のブロックでは、ブロックプロデューサーが3つのトランザクション(B9)を追加します。現在未完成のツリー内のスロットを埋めるために、3つの証明(M6)が必要です。前のブロックで4つの証明が追加されたため、最大作業量(7)を考慮すると、あと3つの証明だけが必要です。ツリー2のM6証明が台帳証明として返されます。3つ目のB9トランザクションは空になったツリーに入り、2つのB7証明が追加されます。

ブロック10: 10番目のブロックでは、ブロックプロデューサーが4つのトランザクションを追加し、その結果、7つの証明(B7、M7、2つのB8)が含まれます。

ブロック11: 11番目のブロックでは、ブロックプロデューサーが3つのトランザクション(B11)を追加し、5つの証明(B9、B9、M8、M8、M9)を順番に完了します。また、4番目のツリーからM9台帳証明が返されます。

tip

スキャン状態の内容を表示するには、次のコマンドを実行します:

mina advanced snark-job-list

SNARKプールとの統合

スキャン状態に新たに追加されたジョブは、SNARKワーカーが完了させるべき保留中のジョブとして扱われます。SNARKワーカーは必要なトランザクションSNARKを完了し、完了した作業の入札を提出します。ノードがこの作業を受信し、検証した際、その作業が有効であり、必要な作業に対して最も低い手数料である場合、SNARKワーカーはその作業をローカルのSNARKプールに追加します。この作業はネットワーク内の他のピアにも広められます。

tip

複数のSNARKワーカーが同じ作業を完了させることができますが、SNARKプールには最も手数料が低い作業のみが含まれます。

ブロックプロデューサーがブロックに完了した証明を含めることで、追加したトランザクションを相殺する場合、対応する作業をSNARKプールから購入できます。先の例を続けると、次のブロック(12番目)では、ブロックプロデューサーがコインベース、ユーザーペイメント、SNARKワーカーへの手数料送金を含む3つのトランザクションを追加したい場合、3つの完了したSNARK作業を購入する必要があります。これは7番目のツリーからの6つの証明(B9、B10、M9、M10)に対応し、それぞれのSNARK作業は2つの作業IDを含んでいます。

ブロック生成時、SNARKプールには完了した作業と必要なジョブに対する最良の入札(例: 0.025、0.165、0.1、0.5)が含まれる場合があります。ブロックプロデューサーは、

トランザクションを選択する前に利用可能な作業の価格を考慮します。

  • ブロックプロデューサーが最初に追加するトランザクションはコインベーストランザクションであり、これにはコインベース報酬が伴います。

  • トランザクション手数料が、それらを含めるために必要なSNARK作業の手数料を上回らない場合、そのトランザクションは追加されません。

経済的でない場合、ブロックプロデューサーは作業を購入しません。

  • 必要な順序で購入可能な完了したSNARK作業がない場合、対応するトランザクションはブロックに含まれません。この状況では、空のブロックが生成される可能性があります。また、コインベーストランザクションを含めたトランザクションを一切追加できない場合、ブロックプロデューサーには報酬がありません。

現在のSNARKプールを表示するには、以下を使用します:

  • GraphQL API

  • Mina CLI: mina advanced snark-pool コマンド

時間ロックアカウント

時間ロックアカウントは、残高がブロックの高さに依存する最小値を下回るような支払いを許可しません。

時間ロックを作成するには、新しいアカウントを作成する際に設定を指定する必要があります。これは、ネットワークの開始時にジェネシスレジャー内でのみ行えます。このセクションでは、時間ロックの仕組みと、時間ロックアカウントとのやり取り方法を探ります。

tip

現在のリリースでは、時間ロックアカウントの値はサインアップした順番に基づいて割り当てられています。

時間ロックの理解

時間ロックには以下のフィールドが含まれます:initial_minimum_balance(初期最低残高)、cliff(崖時間)、vesting_period(権利確定期間)、およびvesting_increment(権利確定増加量)。

アカウントに時間ロックがかかっている場合でも、十分な資金があれば引き続き利用可能です。ネットワークの開始時点では、時間ロックされた資金の額はinitial_minimum_balanceとして設定されます。ネットワークがcliffに等しいブロック高さに達すると、時間ロックされた額はvesting_periodごとにvesting_incrementの量だけ減少し始めます。

このプロセスに関するより技術的な説明については、RFC-0025を参照してください。この文書にはより詳細な概要が記載されています。

流動残高の詳細

特定の期間における権利確定アカウントの流動残高を公開したい場合、以下の関数によって管理されます(注: これはアカウントのロック部分を計算します):

(
  uint32 global_slot -- the "clock" it starts at 0 at the genesis block and ticks up every 3minutes.
  uint32 cliff_time -- the slot where the cliff is (similar to startup equity vesting)
  uint32 cliff_amount -- the amount that unlocks at the cliff
  amount vesting_increment -- unlock this amount every "period"
  uint32 vesting_period -- the period that we increment the unlocked amount
  balance initial_minimum_balance -- the total locked amount until the cliff
)
let min_balance_at_slot ~global_slot ~cliff_time ~cliff_amount ~vesting_period
  ~vesting_increment ~initial_minimum_balance =
let open Unsigned in
if Global_slot.(global_slot < cliff_time) then initial_minimum_balance
else
  match Balance.(initial_minimum_balance - cliff_amount) with
  | None ->
      Balance.zero
  | Some min_balance_past_cliff -> (
      (* take advantage of fact that global slots are uint32's )
      let num_periods =
        UInt32.(
          Infix.((global_slot - cliff_time) / vesting_period)
          |> to_int64 |> UInt64.of_int64)
      in
      let vesting_decrement =
        UInt64.Infix.(num_periods Amount.to_uint64 vesting_increment)
        |> Amount.of_uint64
      in
      match Balance.(min_balance_past_cliff - vesting_decrement) with
      | None ->
          Balance.zero
      | Some amt ->
          amt )

時間ロックアカウントの作成

現在のリリースでは、時間ロックアカウントを作成する唯一の方法はジェネシス・レジャーを使用することです。将来のリリースでは、新しい時間ロックアカウントを作成できるよう、MinaクライアントやGraphQL APIにコマンドを追加する予定です。

支払いの送信

MinaCLIを使用してMINAの支払いを送信する方法

info

注意: このセクションはノード運用者向けです。ノードを稼働せずにMINAを保管、送信、受信したい場合は、「ウォレットのインストール」ページをご覧ください。Minaで利用可能な使いやすいウォレットへのリンクがあります。

このセクションでは、Minaクライアントを使用したトランザクションの送信方法と、ブロックチェーンとのやり取りを開始する方法について簡単に説明します。

オフライン署名トランザクションを使用する場合

ノードを自分で稼働させず、他の誰かが運用しているノードを利用してトランザクションを送信したい場合は、以下の内容を引き続きお読みください。ノードを稼働させて直接トランザクションを送信したい場合は、接続済みノードの使用に進んでください。

Ledgerデバイスを使用する場合

Ledgerデバイス上に秘密鍵がある場合、オフラインで署名済みトランザクションを生成するには、Ledger Offline Modeをご覧ください。

generate-keypairツールで生成したキーペアを使用する場合

より良いツールがまもなく提供予定です: https://github.com/MinaProtocol/mina/issues/8928

現在は、その問題へのコメントに記載された回避策を使用してください。

オフラインのclient-sdkで生成したキーペアを使用する場合

Mina Signerをご利用ください。インストール方法および使用方法は、Mina SignerリポジトリのREADMEをご参照ください。

トランザクションの送信

署名済みトランザクションをブロードキャストするには、ホスティングサービスを使用できます。署名済みトランザクションを送信しても、秘密鍵が漏れることはありません。

Mina Signerで署名されたトランザクションは、以下を使用して送信できます:minascan.io/mainnet/broadcast/payment

Ledgerハードウェアウォレットで署名されたトランザクションは、以下を使用できます:https://minascan.io/mainnet/broadcast/ledger-payment

接続済みノードを使用する場合

このセクションでは、Minaクライアントがシステムにインストールされていることを前提としています。まだMinaがインストールされていない場合は、「Getting Started」をご覧ください。

アカウントをインポートする

ノードが同期されたら、トランザクションに署名し、支払いを受け取るアドレスを生成するために、公開鍵/秘密鍵ペアをインポートする必要があります。セキュリティ上の理由から、鍵は攻撃者にとってアクセスが難しいディレクトリに保存することをお勧めします。

以下のコマンドを実行して、以前に生成したキーペアファイルをインポートします。

mina accounts import --privkey-path ~/keys/my-wallet

アカウント作成時に入力したパスワードが求められます

caution

公開鍵は誰とでも自由に共有できますが、秘密鍵ファイルは非常に慎重に取り扱う必要があります。この秘密鍵を誰にも共有しないでください。これは資金のパスワードと同等のものです。

このコマンドの応答は次のようになります

😄 Imported account!
Public key: B62qjaA4N9843FKM5FZk1HmeuDiojG42cbCDyZeUDQVjycULte9PFkC

また、トランザクションを送受信するために新しいアカウントを生成するには、mina accounts createコマンドを使用できます。

公開鍵は非常に長く覚えにくいので、環境変数として保存してみましょう。前のコマンドの出力から公開鍵を取得し、<YOUR-PUBLIC-KEY>を置き換えて使用してください。

export MINA_PUBLIC_KEY=<YOUR-PUBLIC-KEY>

これで、$MINA_PUBLIC_KEYとしてどこでもアクセスできるようになります。正しく保存されているかどうかは、echo $MINA_PUBLIC_KEYを実行して確認してください。

なお、これらの環境変数は現在のシェルセッションの間のみ保存されます。将来も利用できるようにするには、~/.profileや~/.bash_profileに追加してください。

tip

クラウド仮想マシン上でノードを実行している場合は、鍵ファイルをエクスポートして保存しておくようにしてください。鍵はエクスポート可能です。

mina accounts export --public-key <PUBLIC-KEY> --privkey-path <EXPORT-PATH>

その後、scpを使ってローカルマシンに保存します。

scp <EXPORT-PATH> <LOCAL-DESTINATION>

新しいVMを起動する際には、鍵をアップロードしてインポートできます。

mina accounts import --privkey-path <PRIVATE-KEY-FILE>

作成済みのキーペアを忘れてしまった場合、すべてのキーペアを確認することができます。

mina accounts list

アカウント残高を確認する

以下のコマンドを使用して、すべてのアカウントの残高を確認できます

mina accounts list

アカウントの残高がBalance: 0 minaと表示されることがあります。ネットワークのトラフィック状況によっては、トランザクションが処理されるまでに数ブロックかかる場合があります。

tip

現在のブロック高が更新されているかどうかを確認するには、mina client statusを実行してください。

支払いを行う

ついに最も重要なステップ、最初のトランザクション送信です!支払いを行う前に、アカウントをアンロックする必要があります。

mina accounts unlock --public-key $MINA_PUBLIC_KEY

テスト目的で、送信者と受信者の両方に自分の公開鍵を指定します。これは単に自分自身にトランザクションを送信することを意味します。自分の公開鍵を確認するには、次のコマンドを実行してください。

echo $MINA_PUBLIC_KEY

caution

受信アカウントがこれまでトランザクションを受け取ったことがない場合、トランザクション額から1 MINAのアカウント作成手数料が差し引かれます。

では、Minaを自分自身に送信し、支払いがどのように見えるか確認してみましょう。

mina client send-payment \
--amount 1.5 \
--receiver $MINA_PUBLIC_KEY \
--fee 0.1 \
--sender $MINA_PUBLIC_KEY

上記コマンドで指定した内容について

  • amount: テスト値として1.5 MINAを送信します。これはアカウント作成手数料をカバーするのに十分な額です。

  • receiver: トランザクションを受け取るアカウントの公開鍵(例: B62qjaA4N9843FKM5FZk...)。

  • fee: 0.1 MINAを使用します。

  • sender: トランザクションを送信するアカウントの公開鍵(例: B62qjaA4N9843FKM5FZk...)。

このコマンドが正しくフォーマットされていれば、次のようなレスポンスが表示されるはずです。

Dispatched payment with ID 3XCgvAHLAqz9VVbU7an7f2L5ffJtZoFega7jZpVJrPCYA4j5HEmUAx51BCeMc232eBWVz6q9t62Kp2cNvQZoNCSGqJ1rrJpXFqMN6NQe7x987sAC2Sd6wu9Vbs9xSr8g1AkjJoB65v3suPsaCcvvCjyUvUs8c3eVRucH4doa2onGj41pjxT53y5ZkmGaPmPnpWzdJt4YJBnDRW1GcJeyqj61GKWcvvrV6KcGD25VEeHQBfhGppZc7ewVwi3vcUQR7QFFs15bMwA4oZDEfzSbnr1ECoiZGy61m5LX7afwFaviyUwjphtrzoPbQ2QAZ2w2ypnVUrcJ9oUT4y4dvDJ5vkUDazRdGxjAA6Cz86bJqqgfMHdMFqpkmLxCdLbj2Nq3Ar2VpPVvfn2kdKoxwmAGqWCiVhqYbTvHkyZSc4n3siGTEpTGAK9usPnBnqLi53Z2bPPaJ3PuZTMgmdZYrRv4UPxztRtmyBz2HdQSnH8vbxurLkyxK6yEwS23JSZWToccM83sx2hAAABNynBVuxagL8aNZF99k3LKX6E581uSVSw5DAJ2S198DvZHXD53QvjcDGpvB9jYUpofkk1aPvtW7QZkcofBYruePM7kCHjKvbDXSw2CV5brHVv5ZBV9DuUcuFHfcYAA2TVuDtFeNLBjxDumiBASgaLvcdzGiFvSqqnzmS9MBXxYybQcmmz1WuKZHjgqph99XVEapwTsYfZGi1T8ApahcWc5EX9
Receipt chain hash is now A3gpLyBJGvcpMXny2DsHjvE5GaNFn2bbpLLQqTCHuY3Nd7sqy8vDbM6qHTwHt8tcfqqBkd36LuV4CC6hVH6YsmRqRp4Lzx77WnN9gnRX7ceeXdCQUVB7B2uMo3oCYxfdpU5Q2f2KzJQ46

初回のトランザクションではReceipt chain hashが表示されないことがありますが、以降のトランザクションではレシートチェーンハッシュリストの先頭が表示されます。

ステーキングとスナーク作成

アドレスの作成、MINAの送受信など基本的な操作に慣れたら、Minaネットワーク独自の機能に進むことができます。例えば、コンセンサスへの参加ブロックチェーンの圧縮に貢献することです。

高度な操作

複数のトランザクションを送信する

ステーキングプールを運営している場合など、デリゲーターに報酬を支払うために多くのトランザクションを送信する必要がある場合があります。

3.0.3ビルド時点の情報は以下の通りです。

レート制限

現在、ネットワーク上のノードは特定のノードからのメッセージ受信をレート制限します。3.0.3ビルドでは、ノードがトランザクション送信時にもこのレート制限に従います。具体的には、制限は5分間のウィンドウ内で15秒ごとに最大10件のトランザクションです。
このレートを超えてトランザクションを送信しようとすると、ノードは自動的にトランザクションをキューに追加し、古いトランザクションがウィンドウから外れると順次フラッシュされます。自分でトランザクション送信を調整する必要はありません。

注意: 古いバージョンのMinaデーモンはこのレート制限を実行しません。古いバージョンを使用している場合は、手動でトランザクション数を制限する必要があります。
トランザクションの再ブロードキャストによるオーバーヘッドを考慮し、手動でレート制限する必要がある場合、5分間に50件を超えるトランザクションは送信しないことを推奨します。

ノードがクラッシュまたは切断され、トランザクション送信が完了しなかった場合

Minaデーモンは現在、トランザクションプールを永続化しません。そのため、ノードがクラッシュすると、それまでに送信したトランザクションについては認識しなくなります。3.0.3ビルドでは、すべてのトランザクションを再送信することができ、ネットワーク上で再ブロードキャストされます。

ノードがオンラインのまま一時的にネットワークから切断された場合(つまり、ゴシップネットワークが1つ以上のトランザクションを見逃した場合)、3.0.3ビルドでは、ローカルでトランザクションを再送信することで、ノードが既に共有済みだと認識していても、再びネットワークにブロードキャストされます。

トランザクションのキャンセルと新しい手数料の設定

トランザクションをキャンセルするには、チェーンにコミットされていないトランザクションすべてがローカルのトランザクションメンプールに存在している必要があります。
したがって、ノードがクラッシュした場合(上記参照)、それ以前のトランザクションを再送信する必要があります。

トランザクションをキャンセルするには、キャンセルしたいトランザクションと同じnonceを持つトランザクションをより高い手数料で送信します。
手数料の増分に最低限の制限はありませんが、少しでも高く(ブロックプロデューサーが選択するのに十分な額)設定する必要があります。

支払いのライフサイクル

Minaでは、支払いが検証され完了と見なされるまでにいくつかのステップを経由します。

このドキュメントは、Minaにおける単一の支払いで何が起こるのかを簡単に説明することで、Minaの支払いの仕組みを理解しやすくするものです。完全な技術的概要ではなく、ユーザー向けのシンプルな解説です。

ゴシッププロトコル

Minaはゴシッププロトコルを使用して、メッセージがネットワーク内のすべてのメンバーに確実かつタイムリーに伝達されるようにします。

支払い

支払いはトランザクションの一種であり、あるアカウントから別のアカウントへの価値の転送を要求し、送信者が支払い処理のために支払う手数料も含まれます。

以下は、送信者のボブが受信者のアリスにMINAを送信するシナリオの流れです。

ステップ1: 支払いを作成するために、ボブが「送信」をクリック

ネットワークの任意のメンバーは支払いを作成し、それをMinaネットワークと共有できます。支払いは秘密鍵で暗号的に署名されるため、送信者のアカウントが検証されます。この支払いはネットワーク上のピアに送信され、処理されます。支払いを受け取ったピアは、その支払いをローカルトランザクションプールに保存します。これは、ネットワーク上でそのピアが認識しているすべてのトランザクションを保持するメモリ内ストアです。

ステップ2: ブロックを生成するために、ボブの支払いがTo-Doリストに追加される

ネットワーク上で指定されたタイムスロットに、ブロック生成ノード(ブロックプロデューサー)が選ばれます。アクティブなプロデューサーは、支払い手数料に基づいて進行中の支払いを選択し、それらを処理するリスト(トランジションブロック)に配置します。ブロックプロデューサーは、これらのブロックを生成することでMina報酬を獲得します。プロデューサーは、前のブロックと比較したトランジションブロックの構造を定義するSNARKを生成します(ただし、これらの新しい支払いはまだ検証されていません)。その後、この新しい情報がSNARKワーカーに送信され、処理されます。

ステップ3: SNARKトランザクションを証明するために、ボブの支払いがSNARK署名される

ネットワーク上のSNARKワーカーノードは、新しいトランジションブロックの各ステップに対してSNARK計算を実行し始めます。これらは各支払いの個別の証明であり、その後、隣接する支払いのマージ証明が行われます。最終的には、すべての支払いが検証されます。SNARKワーカーは、これらの証明を生成することで通貨を稼ぐことができ、これはブロックプロデューサーのブロック報酬から支払われます。これらの証明はネットワーク全体に送信されます。

ステップ4: 支払いを検証するために、アリスとボブのアカウントが転送結果を表示

ブロック全体が証明されると、ブロックプロデューサーはトランジションブロックの確認を送信します。その後、ネットワーク上のメンバーノードは、ローカルアカウント残高に変更を適用し、これらの支払いの結果を反映します.

ステップ5: 支払いの信頼レベルを達成し、アリスは転送が完了したと確信

後続の各ブロックによって、受信者は支払いが実際に完了し、ネットワークがそのブロックについて合意しているという確信度が高まります。しかし、他の多くのブロックチェーンと同様に、一定数のブロックが生成された後に支払いは確認済みとされます。これはトランザクションのファイナリティとも呼ばれます。

例として、Bitcoinネットワークでは、6ブロック(約60分)後にトランザクションが確認され、攻撃者が10%以上のハッシュレートを持つ可能性は低いと仮定されます。

Minaでは、スロットの継続時間が3分であり、90%の誠実なステークが存在すると仮定した場合、以下の表はファイナリティのブロック数、対応するブロック生成の平均時間、および支払いが確認される信頼度を示しています。

Finality (in blocks)

Average time for finality

Finality confidence (%)

8

33 mins

96.6709

15

60 mins

99.9231

23

1hr 32mins

99.9965

30

2hrs

99.9998

38

2hrs 32mins

100

平均時間は、エポックごとに埋められるスロット数を決定するコンセンサス定数に基づいて計算されます。現在、この設定は75%に設定されています。

推奨されるトランザクション確認待機時間は15ブロックであり、これによりトランザクションが99.9%の確率で取り消されないことが保証されます。

障害シナリオ

支払いが失敗する理由はいくつかあります。

トランザクションがネットワークに受け入れられない場合

ピアノードと共有されたトランザクションが受け入れられない理由はいくつかあります:

トランザクションが基本的に無効である場合

  • 例: 送信者のアカウントが存在しない、アカウントに十分な資金がない、署名がアカウントと一致しない、またはトランザクション内のnonceがインクリメントされていない。

  • ネットワーク内に悪意のあるノードが存在し、特定の送信者に対するサービスを拒否するために共謀する可能性があります。ただし、この行動は大きな経済的ペナルティが伴うため抑止されており、1つの誠実なノードが存在すればこの問題は防止されます。

トランザクションがブロックに含まれない場合

トランザクションが有効であり、ネットワークが誠実である場合、トランザクションはブロックに含まれる可能性が高いです。しかし、以下のケースでは、トランザクションがトランザクションプールから破棄されることがあります:

  • トランザクションプールが容量制限max_txpool_size)に達した場合 プール内の最小手数料のトランザクションが破棄され、メモリから削除されます。この場合、送信者はその時点の市場動向に基づいてより高い手数料でトランザクションを再送する必要があります。