# コンセンサスアルゴリズム

> Proof-of-Work（PoW）は、新しいブロックを作るときに「特定の条件を満たすまでハッシュ計算を繰り返す」仕組みです。

- URL: https://cardene.dev/articles/1f308510b8cbde
- 公開日: 2025-09-25
- 更新日: 2025-09-25
- トピック: Blockchain
- タグ: solidity, ethereum, smartcontract, zennfes2025free

# はじめに

初めまして。
『DApps開発入門』という本や色々記事を書いているかるでねです。

https://amzn.asia/d/gxvJ0Pw

以下でも情報発信しているので、興味ある記事があればぜひ読んでみてください！

https://cardene.notion.site/EIP-2a03fa3ea33d43baa9ed82288f98d4a9

https://twitter.com/cardene777

https://chaldene.net/

https://qiita.com/cardene

https://cardene.substack.com/

https://mirror.xyz/0xcE77b9fCd390847627c84359fC1Bc02fC78f0e58

今回はコンセンサスアルゴリズムについてまとめていきます。
「**PoW**」、「**PoS**」、「**Tendermint**」、「**PoA**」についてまとめています。

# PoW

Proof-of-Work（PoW）は、新しいブロックを作るときに「**特定の条件を満たすまでハッシュ計算を繰り返す**」仕組みです。
このような条件を満たすことは膨大な計算を必要とするため、ブロックがその条件をクリアしていること自体が「**正しく作られた証拠**」になります。
これにより、誰か特定の管理者に頼ることなく取引の記録を並べ、矛盾のない履歴を維持できます。

## ハッシュ

ここで使われる「**ハッシュ**」とは、データを一定の長さの値に変換する関数のことです。
BitcoinではSHA-256という暗号学的ハッシュ関数を用いており、入力データを少しでも変えると結果の値が大きく変わるという特徴があります。

以下のサイトで動作を確認できます。

https://andersbrownworth.com/blockchain/hash

「**データ**」部分に任意の値を入力すると、「**ハッシュ**」部分の値が全く異なる値になります。

- `Hello`

![ハッシュの図](https://storage.googleapis.com/zenn-user-upload/b38ea84c87ce-20250923.png)

```bash
Hello

185f8db32271fe25f561a6fc938b2e264306ec304eda518007d1764826381969
```

- `Hello `

`Hello`の後ろにスペースを入れているだけですが、ハッシュ値は`Hello`とは全く異なる値になっています。

![ハッシュの図](https://storage.googleapis.com/zenn-user-upload/0485b17d605b-20250923.png)

```bash
Hello

2ec5a3f0c2fc3e6dcee0f6f3a5735a6c69d2056579a5452095b75802094043a8
```

- `Hello World`

![ハッシュの図](https://storage.googleapis.com/zenn-user-upload/867fe40b8e0c-20250923.png)

```bash
Hello World

a591a6d40bf420404a011733cfb7b190d62c65bf0bcda32b57b277d9ad9f146e
```

## PoWの仕組み

PoWの仕組みについて詳しく説明していきます。

### 1. ブロックヘッダの組み立て

前ブロックのハッシュ、トランザクションから計算したマークルルート、タイムスタンプ、nBits、nonceを入れてブロックヘッダを作成します。

- **前ブロックのハッシュ（Previous Block Hash）**
    直前のブロックのヘッダをdoubleSHA-256した値。
    この値を含めることで、ブロック同士が鎖のようにつながり、ひとつでも改ざんすると以降すべてが無効になります。
- **マークルルート（Merkle Root）**
    そのブロックに含まれる全トランザクションをMerkle Treeにまとめ、そのルートにあたるハッシュ。
    任意のトランザクションがブロック内に含まれているかどうかを効率よく検証できます
    coinbase（マイナーが報酬を受け取るトランザクションで、ブロック内の最初に格納される）を変えるとマークルルートが変わり、ヘッダ全体も変わります。

- **Timestamp**
    ブロックを作成したおおよその時刻（UNIX時刻形式）。
    ノードの時刻に依存するため、正確な時刻を保証するわけではないです。
    ネットワークが「**未来すぎる時刻**」や「**過去すぎる時刻**」のブロックを拒否するため、だいたいの時間順序を守る役割を持ちます。

- **nBits（難易度の圧縮表現）**
    難易度を短く表した値で、ここから「**target（閾値）**」を復元します。
    targetが小さいほど条件は厳しく、必要な計算量が増えます。
    つまり「**どれだけ先頭に0を揃えなければならないか**」を決める基準です。
    :::message
    `nBits` は「**難易度調整アルゴリズム**」によって決まります。
    Bitcoin の場合、2016ブロックごと（約2週間に一度）にネットワーク全体で計算力に合わせて自動的に更新されます。
    1. 前の2016ブロックがどのくらいの時間で生成されたかを測る
    2. 目標時間（2016 ブロック × 10分 = 2週間）と比べる
    3. もし速すぎれば `target` を小さくして難易度を上げる
    4. 遅すぎれば `target` を大きくして難易度を下げる
    
    1回の調整で難易度が4倍以上変わらないよう制限もかけられています
    調整後の `target` が `compact` 形式（32ビット）に変換され、次のブロック以降の `nBits` として使われます。
    :::

- **nonce（ナンス）**
    32bitのカウンタ。
    PoW計算のために総当たりで値を変えていく部分。
    使い切ったらcoinbase内のextraNonce（coinbase内の任意データを入れられる領域。ここを更新するとマークルルート全体が変わるので、`nonce`を使い切った後も新しい探索空間を得られる）を更新してマークルルートを変え、探索空間を広げます。

:::message
マークルルートはトランザクション集合の要約なので、coinbaseを変えるとマークルルートも変わり、結果としてヘッダ全体のハッシュも変わります。
:::

### 2. 目標値（target）を決めます

ブロックヘッダには `nBits` というフィールドがあり、これは難易度を短く表した圧縮形式です。
マイナーはこの `nBits` を展開し、実際に使う256ビットの数値である **target** を計算します。

:::message
#### `nBits` の構造

`nBits` は4バイト（32ビット）で構成されています。

* **最上位1バイト**
    指数部（`exponent`）
* **下位3バイト**
    仮数部（`coefficient`）

この2つを組み合わせて `target` を計算します。

#### target の計算式

以下の式で `target` を復元します。

$$
\text{target} = \text{coefficient} \times 256^{(\text{exponent} - 3)}
$$

* coefficient
    `nBits` の下位3バイトをそのまま整数にした値
* exponent
    `nBits` の上位1バイトを整数にした値

#### 計算例

例えば、Bitcoinジェネシスブロックの `nBits` は **0x1d00ffff** です。

* exponent = 0x1d = 29
* coefficient = 0x00ffff = 65535

計算式に当てはめると以下のようになります。

$$
\text{target} = 65535 \times 256^{(29 - 3)}
$$

\= 65535 × 256^26
\= 0x00000000FFFF0000000000000000000000000000000000000000000000000000

これが `target` です。
:::

PoWにおける判定条件は非常に単純で、ヘッダ全体をSHA-256で二回ハッシュ化した結果（digest）が、この `target` 以下であるかどうかです。

この「`digest ≤ target`」という条件は、「**ハッシュの先頭にいくつゼロが並んでいるか**」を確認することと同じ意味になります

:::message
`target` と完全に一致する必要はなく、それ以下の値であれば十分な理由。

#### ハッシュはランダムな数として扱われる

ハッシュ関数（SHA-256）の出力は256ビットの整数で、ほぼ一様にランダムに分布します。
つまりPoWは「**このランダムな数が十分小さいかどうか**」をチェックしているだけです。

#### 条件は「閾値以下」だから

`target` は「**これ以下ならOK**」という**閾値**を意味しています。
もし「**digest = target だけが有効**」だったら、その確率は $1/2^{256}$ に等しく、難易度が跳ね上がり見つけるのが非常に困難になります。
実際には `target` の範囲内に多くの整数値があるので、以下のようになり、`target` の値に応じて現実的な確率でブロックが見つかる仕組みになっています。

$$
\text{成功確率} = \frac{\text{target}}{2^{256}}
$$

#### 「`target` 以下」にすることで難易度を調整可能

* `target` が大きい → 範囲が広い → `digest` がその中に収まりやすい → 難易度が低い
* `target` が小さい → 範囲が狭い → `digest` が入りにくい → 難易度が高い

もし「**`target` と完全一致**」しか認めなければ、難易度の調整ができず、実質的にブロック生成は不可能になります。
:::

`target` が小さくなるほど、より多くのゼロを揃えなければならず、達成が難しくなります。
例えば先頭に20ビットのゼロが必要であれば、成功確率は 1/2^20 であり、平均して 2^20 回の試行が必要です。
ゼロの数が1ビット増えるたびに、必要な計算量はおおよそ2倍に膨らんでいきます。

:::message
ハッシュ値（digest）は256ビットのランダムな整数と考えられます。
PoWではこの整数が `target` 以下になるまで試行を繰り返します。

#### 例 1：target が大きい場合

仮に `target` が以下だったとします。

```
0x0000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF
```

* 先頭 16 ビット（2 バイト）は必ず `0` でなければなりません。
* それ以降のビットは自由なので、成功確率は **1 / 2^16 ≈ 1/65,536** です。
* 平均すると約 65,000 回の試行で成功します。

### 例 2：target が小さい場合

次に `target` を以下のように小さくします。

```
0x0000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF
```

* 今度は先頭 `28` ビット程度が `0` でなければなりません。
* 成功確率は **1 / 2^28 ≈ 1/268,435,456** です。
* 必要な試行回数は数億回に跳ね上がります。
:::

### 3. ナンス探索を行います

マイナーはこの条件を満たすブロックを探すため、ブロックヘッダをハッシュ化し続けます。
具体的には、ヘッダ内の **nonce** という32ビットのカウンタを少しずつ増やしながらdoubleSHA-256を計算し、その結果が `target` 以下になるかどうかを何度も試みます。
`nonce` をすべて試しても条件を満たす結果が得られなければ、coinbaseトランザクション内の**extraNonce**を変更します。
これによりマークルルートが変わり、ブロックヘッダ全体も変化するため、新しい探索空間で再び計算を続けられます。

この探索作業は純粋に確率に依存しているため、いつ条件を満たせるかは予測できません。
ただし `target` が小さいほど、つまり難易度が高いほど、成功までに必要な試行回数は指数的に増えていきます。

:::message
以下でハッシュ化し続ける処理を確認できます。

https://andersbrownworth.com/blockchain/block

デフォルトだと以下のように表示されていると思います。

![3. ナンス探索を行いますの図](https://storage.googleapis.com/zenn-user-upload/e326d1c21ead-20250924.png)

例えば、データ部分に「`Hello`」と入力します。

![3. ナンス探索を行いますの図](https://storage.googleapis.com/zenn-user-upload/03dbac8f3385-20250924.png)

そうすると、ブロックハッシュが変わるので、正しいハッシュを見つけるためにノンスの値を変更する必要があります。

![3. ナンス探索を行いますの図](https://storage.googleapis.com/zenn-user-upload/04de4fa686bc-20250924.png)

「**掘る**」（Miningを行う）をクリックすると、ノンスの値が変更されて先頭4桁が0になるような値を見つけます。

「**ハッシュの先頭4桁（16ビット）が0であること**」という条件は、数学的には `target` を小さい値にすることで表現できます。

具体的には以下のような値になり、「**先頭16ビットが必ず0で、それ以降はどんな値でもよい**」という範囲を意味します。

```
target = 0x0000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF
```

検証条件は 「`digest ≤ target`」で、`digest` がこの `target` より小さい数になるためには、**先頭16ビット（16進4桁）が0** である値を探索します。
:::

### 4. 条件達成でブロックを伝播します

PoWの条件を満たすブロックヘッダが得られたら、そのブロック（ヘッダ＋トランザクション本体）をネットワークに伝播させます。
マイナーは報酬を得るため、自分が発見したブロックをできるだけ早く全体に広めようとします。
並行して他のマイナーもマイニングを行っているため、同じタイミングで複数のブロックが発見されることがあり、その場合はチェーンが一時的にフォークすることになります。

### 5. 他ノードによる即時検証

ブロックを受け取った他ノードは、そのヘッダに対してdoubleSHA-256を計算し、ハッシュ値（digest）が `target` 以下であるかを確認します。
このチェックは1回のハッシュ計算で済み、非常に軽量です。

一方で、ブロックを生成するには膨大な試行（nonceの探索）が必要です。
この「**生成は計算量的に困難だが検証は容易**」という性質こそがPoWの特徴です。
これにより、全体の正当性を低コストで確かめながら、不正に改ざんされたブロックを容易に排除できる仕組みが成立します。

### ブロック生成

以下のサイトで実際にブロック生成の流れを確認できます。
デフォルトでは以下のようになっています。

![ブロック生成の図](https://storage.googleapis.com/zenn-user-upload/b3c701910a6b-20250924.png)

例えば、ブロック2のデータを書き換えたとします。

![ブロック生成の図](https://storage.googleapis.com/zenn-user-upload/b150e9bfa363-20250924.png)

そうすると、ブロック2のブロック8種が変化し、後続のブロック内の「**前ブロックのハッシュ値**」が変わってしまい、そのブロックのハッシュ値も変わります。
そうすると、後続の全てのブロックのハッシュ値が変わってしまうため、再度計算し直す必要があります。

![ブロック生成の図](https://storage.googleapis.com/zenn-user-upload/00fcb9644966-20250924.png)

![ブロック生成の図](https://storage.googleapis.com/zenn-user-upload/49da94e7ab4a-20250924.png)

![ブロック生成の図](https://storage.googleapis.com/zenn-user-upload/673e7134d08e-20250924.png)

![ブロック生成の図](https://storage.googleapis.com/zenn-user-upload/12fbe313f5cb-20250924.png)

そのため、上記のように１つずつブロックをマイニングして、ブロックハッシュを再計算していくことで正しいチェーンとして扱うことができます。

## 検証の容易さ

PoWの検証は、計算量がほぼ一定で計算処理が軽量で済みます。
以下の手順で、ブロックヘッダだけを使ってPoWが満たされているかを確認します。

1. **ブロックヘッダを受け取る**
    ブロックの基本情報（`version`／`prev-hash`／`merkle-root`／`timestamp`／`nBits`／`nonce`）を含むヘッダを取得します。
    本体のトランザクションは使わず、ヘッダだけでPoW検証が可能です。
2. **ヘッダにdoubleSHA-256を実行**
    受け取ったヘッダをSHA-256を2回適用して要約値（`digest`）を得ます。
    入力を1ビットでも変えると結果が大きく変わるため、改ざんの有無がすぐ分かります。
3. **得られた値が `target` 以下か比較**
    計算した `digest` が `target`（nBitsから復元される閾値）以下なら、PoWの検証は成功と判定します。
    `target` が小さいほど条件は厳しく、達成が難しくなります。

このように検証側は「**ハッシュ計算1回＋整数比較1回**」程度のコストで済みます。
一方で生成側（マイナー）は`nonce`等を変えながら多数回試行する必要があり、ここに計算量の非対称性（生成コストは重いが検証コストは軽い）が生まれます。
結果として、ネットワーク全体は低コストで不正なブロックを速やかに排除できます。

## 改ざんへの耐性

ブロックチェーンでは、各ブロックが直前のブロックのハッシュを含んでいるため、過去の1つのブロックを改ざんするとその後ろに続くすべてのブロックを作り直さなければなりません。

攻撃者が改ざんを成功させるには、正直なノードと同じ速度で新しいブロックをマイニングしつつ、さらにそれを追い越す必要があります。

しかし正直なノードが計算力の過半数を持っている限り、攻撃者が追いつく可能性は新しいブロックが追加されるたびに急速に下がっていきます。
結果として、ブロックが積み重なるほど過去の記録を改ざんすることは現実的に不可能になります。

![改ざんへの耐性の図](https://storage.googleapis.com/zenn-user-upload/14462b6a6a86-20250924.png)

上記のように、特定のブロックを改ざんした場合、改ざんしたブロック以降のブロックのハッシュを全て計算し直す必要があります。
十分な時間があればこの処理を行うことはできますが、その間、他のマイナーはどんどん新しいマイニングをして新しいブロックを生成していきます。
そのため、数ブロックはたまたま普通より早く生成できても、最新のブロックにはいつまで経っても追いつくことは理論上難しいです。

## 合意形成

ノードは常に、累積PoW（chainwork：これまで積み上がった計算量の合計）が最大のチェーンを正しいとして認識します。
一般に「**最長チェーン**」と言いますが、厳密には計算の重みが最も大きいチェーンを指します。
各ノードは新しいブロックを受け取るたびに、そのチェーンの累積PoWを自分の手元で再計算し、より大きければ自分が追従するチェーンの先頭（tip）を切り替えます。

ノードはチェーンの切替え時にPoW（`digest ≤ target`）の妥当性だけでなく、各トランザクションの正当性（署名・二重支払いの有無など）も検証します。
無効なブロックは累積PoWが大きくても採用されません。

### 全体が揃う仕組み

* **競合（フォーク）の発生**
    複数のマイナーがほぼ同時にブロックを見つけると、一時的に先頭が分かれます。
* **収束の仕組み**
    どちらか一方に新しいブロックが先に追加されて累積PoWが上回ると、ノードはそちらへ乗り換えます。
    これが繰り返され、最終的に1本に収束します。
* **取引の「確からしさ」**
    あるブロックに後続ブロック（confirmations）が重なるほど、そのブロックを含むチェーンの累積PoWが増え、巻き戻り（reorg）の確率が下がります。
    そのため、一定数の確認（例：Bitcoinでは目安として6ブロック）を待ってから、その取引を実質的に確定したものとして扱うのが一般的です。

## 難易度の調整

Bitcoinでは、ブロックごとに `nBits` という値が入っています。
これは本来256ビットの大きさを持つ `target` を短く表したもので、検証時には「**ブロックヘッダをSHA-256を2回かけた値（digest）が `target` 以下か**」を見ます。
`target` が小さいほどより小さなハッシュを要求することになり、見つけにくくなり難易度が上がるという関係です。
難易度（difficulty）は以下のように表され、`target` が小さくなるほど `difficulty` は大きくなります。

$$
\text{difficulty}=\frac{\text{max\_target}}{\text{target}}
$$

ただし、マイナーの計算力（ハッシュレート）は常に一定ではありません。
マイナーが増えたりマシンが高速化すると、放っておけばブロックはどんどん早く見つかります。
逆に離脱や故障が増えれば遅くなります。
そこで、**平均およそ10分に1ブロック**という目標ペースに近づけるため、Bitcoinは2016ブロックごと（約2週間）に `target` を調整します。
やり方はシンプルで、「**直近2016ブロックに実際にかかった時間（actual\_timespan）**」を測り、目標の14日（`target\_timespan`）との差に応じて `target` を比例的に更新します。

$$
\text{new\_target}=\text{old\_target}\times\frac{\text{actual\_timespan}}{\text{target\_timespan}}.
$$

計算が速すぎた（`actual\_timespan` が短い）なら `new\_target` は小さくなり、次回からはより厳しい条件になります。
遅すぎた場合は `new\_target` が大きくなり、条件がゆるくなります。
さらに一度の調整で極端に振れないよう、`actual\_timespan` は 1/4〜4倍の範囲に丸められます（難易度の急変を防ぐため）。
この新しい `target` を `compact` 形式に戻したものが次のブロックの `nBits` になります。

### 例

ネットワーク全体の計算力がほぼ2倍になれば、同じ2016ブロックをマイニングするのにかかる時間は約半分になります。
式に従って `new_target` も約半分になり、その結果 `difficulty（max\_target / target）` はおおむね2倍へ。
逆に計算力が半分になれば `new_target` は約2倍、`difficulty` は半分に下がります。
こうして、計算力が増減しても10分前後でのブロック生成が保たれるように調整され続けます。

## Reference

- https://bitcoin.org/files/bitcoin-paper/bitcoin_jp.pdf
- https://developer.bitcoin.org/reference/block_chain.html
- https://learnmeabitcoin.com/technical/block/bits

# PoS

## 概要

EthereumのPoSは、ETHを `stake`（担保として預け入れ）した `validator`（検証者）がブロックを提案し、他のvalidatorが `attestation`（投票）を行うことでチェーンにblockを追加する仕組みです。
時間は `slot`（12秒）と `epoch`（32slot ≒ 約6.4分）で区切られます。
各 `slot` ごとに `RANDAO`（検証可能な乱数生成）によって `block proposer`（その `slot` でブロックを提案する `validator`）と `committee`（その `slot` の `attestation` を担当する `validator` のグループ）が選ばれます。

:::message
### RANDAO

#### RANDAOの仕組み

- **commit-reveal による乱数生成**
    各 `validator` は、ある時点で秘密値を `hash` 化したもの（commit）を事前に提出します。
    `slot` に到達すると、その秘密値（reveal）を公開します。
    これを順に XOR（排他的論理和）で組み合わせることで、予測困難な乱数が得られます。
    これを「RANDAO」と呼びます。
-  **乱数の強化（VDF / mix）**
    EthereumのBeacon Chainでは、得られた乱数に追加の処理をかけて「**予測・操作のしづらさ**」を高めています。
    例えば「`randao_mix`」として保存され、後続の `epoch` の選出に使われます。

#### 選出の流れ

各 `epoch` のはじめに、その `epoch` 全体の `randao_mix` が計算されます。
この乱数をもとに、以下の割り当てが行われます。

* **`block proposer`**
    各 `slot` ごとに、アクティブな `validator` のリストから乱数を使って1名が選ばれる。
* **`committee`**
    同じく乱数を使って、各 `slot` に参加する `validator` の小グループが決定される。

#### 例：RANDAOによる乱数生成

1. 3人の `validator` がいます。
    * V1
    * V2
    * V3
2. 各 `validator` は秘密の値（例：乱数種）を持っています。
    * V1 → `s1 = 0xA3F...`
    * V2 → `s2 = 0xC9D...`
    * V3 → `s3 = 0x117...`
3. 各 `validator` は事前に `hash(s1)`・`hash(s2)`・`hash(s3)` を提出して「**コミット**」します。
4. `epoch` に入ると順番に `s1`・`s2`・`s3` を公開（reveal）します。
5. それらを順に **XOR** で混ぜ合わせます。
    ```
    randao_mix = s1 ⊕ s2 ⊕ s3
    ```
    
    例：
    
    ```
    s1 = 1010 (2進数)
    s2 = 1100
    s3 = 0111
    
    randao_mix = 1010 ⊕ 1100 = 0110
    randao_mix = 0110 ⊕ 0111 = 0001
   ```

    `randao_mix = 0001` がこの `epoch` の乱数になります。

#### `proposer` の選出

アクティブな `validator` が 1000人いるとします。

* このリストにインデックス（0〜999）を振る。
* `randao_mix` を使って「`mod 1000`」を計算する。

```
randao_mix = 0001 (＝1)
1 mod 1000 = 1
```

インデックス `1` の `validator` が、その `slot` の `block proposer` に選ばれます。

#### `committee` の選出

`committee` には 128人が必要だとします。

* `randao_mix` を元に乱数列を生成し、`0〜999` の範囲でランダムに `validator` を選ぶ。
* 例えば以下のような結果になる。

  ```
  [5, 217, 398, 777, ...]
  ```

この 128人がその `slot` の `committee` となり、`attestation` を担当します。
:::

:::message
### aggregator の選出

`committee` の全員が `attestation` を出すとデータが膨大になるため、さらに `committee` の中から少数の `validator` が `aggregator` として選ばれます。

各 `committee` メンバーは乱数に基づき「**自分が `aggregator` か**」を判定します。

選ばれた `aggregator` は他メンバーの `attestation` を集約（aggregate）し、1つの署名付きメッセージとしてネットワークに流します。

これにより 数百人分の `attestation` が、集約された少数のメッセージとしてブロックに取り込まれるため、効率的にコンセンサスに利用できます。
:::

PoSのconsensusは　`LMD-GHOST` と `Casper FFG` で構成されています。
`LMD-GHOST` はfork choice ruleとして「**どのブロックを先頭とみなすか**」を決定し、`Casper FFG` はチェックポイントに基づいて `justification` と `finality` とみなします。
Ethereumのfinalityは、提案された直後のブロックではなく複数のブロックを経たチェックポイントで成立します。

:::message
### justification

チェックポイント（各 `epoch` の最初のブロック）に対し、`validator` の `attestation`（投票）を集計し、その重みが `active validator` のステーク合計の **2/3以上** に達した場合、そのチェックポイントを `justified` とみなします。
この時点で、そのチェックポイントは「**次に `finalized` とみなせるかを判定する段階へ進むための条件を満たした**」状態です。
:::

:::message
### finality

あるチェックポイントが `justified` になった後、次のチェックポイントも同様に `active validator` のステーク合計の **2/3以上** の重みで支持されて `justified` になると、先に `justified` だったチェックポイントを `finalized` とみなします。
`finalized` とみなされたチェックポイントより前の履歴は、プロトコル上reorg（チェーンの巻き戻し）の対象にしません。

`finality` に到達するまでの目安は **2 `epoch`** です。
`epoch` は32 `slot`（1 `slot`＝12秒）なので、2 `epoch` は **64 `slot`＝約12分48秒** です。
そのため、`finality` に到達するまで大体 **約12.8分** と理解しておけば実務上は十分です。 
:::

## PoSの仕組み

PoSでは、`stake` したETHの重みに応じて合意が決まります。
`validator` はルールどおりに `block proposal`（ブロック提案）と `attestation`（投票）を実行すると `reward`（報酬）を受け取ります。
違反がある場合は以下のように扱われます。

* 軽度の違反
    `attestation` を出さない・遅れる等 ⇒ `penalty`（報酬の減少や小さな減点）が課されます。
* 重大な違反
    同じ `slot` で2つのブロックを提案する `double proposal`、互いに矛盾する投票を出す `double vote`、過去の自分の投票を包含する形の矛盾投票である `surround vote` など ⇒ `slashing`（`stake` の削減と強制 `exit`）の対象になります。

`finality`（後で説明）が長時間「**成立しない**」状態が続く場合には、`inactivity leak` が作動します。
これは、参加していない `validator` の重み（`balance`）を段階的に小さくし、参加している `validator` が最終的に2/3を占められるようにすることで、`finality` が再び成立するように設計された仕組みです。

:::message
### Inactivity Leak

Ethereum のコンセンサスレイヤーには、チェックポイントを `finality` させる仕組みがありますが、もし長期間にわたって `finality` が得られない状態が続いた場合、通常の報酬／罰則ルールだけでは回復できなくなる恐れがあります。
そこで、非常事態対応の仕組みとして **inactivity leak** が設けられています。

Inactivity leak は、`finality` が成立しない期間が続くと起動するモードで、オフラインあるいは投票できない `validator` の `stake` を段階的に減らすことで、残った参加中の `validator` がtotal stakeの2/3を占める状態をつくり、最終的に `finality` を再開できるようにする目的があります。

#### 発動条件

Ethereum の仕様では、4 連続の `epoch` で `finality` が得られなかったときに、`inactivity leak` モードが起動します。
これは「**チェックポイント間の支持が十分集まらず`finality` にできない**」ことを意味します。

#### 動作メカニズム

一旦 `inactivity leak` モードが始まると、以下のような処理が適用されます。

| 処理 | 内容 |
| ---- | --- |
| `attestation` 報酬| `attestation` に対する報酬は支払われません。 |
| 非参加 `validator` へのペナルティ | 非参加（または正しく投票できない）`validator` に対して、ペナルティを追加で課します。得られるペナルティは時間経過とともに大きくなります。 |
| `proposer` / `committee` の報酬 | proposer（ブロック提案者）や `committee` 参加者への報酬は、`inactivity leak` 時にも基本的には継続されます。 |

非参加 `validator` の `stake`（特にeffective balance）が徐々に減少していき、その影響力が弱まると、残っている `validator` だけで `stake` の2/3を超える体制を築けるようになります。
そうしたとき、`finality` は再び成立できるようになります。

また、`inactivity leak` 中でもペナルティが続き、モード終了後（`finality` が再開した後）も一定期間は影響を残すようになっている点も仕様に組み込まれています。
:::

### フォーク選択（`LMD-GHOST`）

各 `validator` は定期的に `attestation` を発行します。
これには「**自分が見ている `head`**」が含まれます。
Ethereumのノードは、それぞれが受け取った全 `attestation` のうち、最新のもの（Latest Message Driven）だけを取り出して重み付けします。
この重みは `stake` に応じて決まります。

* 集計結果として、重みが最も大きい枝の先にあるブロックが「**このノードが次に延長すべきチェーン**」とされます。
* このブロックを「`head`」と呼び、`head` はあくまで「**いま現在の先頭**」であり、後続の `attestation` によって変わる可能性があります。

言い換えると、`LMD-GHOST` は「**常に最新の投票を反映して、どのブロックをチェーンの先頭とみなすかを決める**」ルールです。

### チェックポイントと `finality`（`Casper FFG`）

EthereumのPoSでは、すべてのブロックが同じ重みで確定していくわけではなく、`epoch` ごとに「**チェックポイント**」を設け、そこを基準にして `finality` を与えます。

- **チェックポイントの定義**
    各 `epoch`（= 32 `slot` ≒ 6.4分）の最初のブロックが、その `epoch` のチェックポイントです。
- **justification**
    あるチェックポイントに対して、`active validator` の `effective balance` の合計の **2/3以上** が `attestation` で支持すると、そのチェックポイントは `justified` とみなされます。
    `effective balance` は投票の重みを計算するための基準値で、各 `validator` ごとに最大 `32 ETH` が上限です。
    残高がそれ以上あっても投票重みは増えません。
- **finalization**
    `justified` となったチェックポイントの次の `epoch` のチェックポイントも、同じく 2/3 以上の支持を得て `justified` になると、最初のチェックポイントが `finalized` とみなされます。
- **finality の意味**
    `finalized` になったチェックポイントより前のブロックは、Ethereumのコンセンサスルール上、`reorg`（ブロックの差し替えや巻き戻し）の対象から外れます。
    つまり、これ以降は「**確定した履歴**」として扱われます。

### 時間的な目安

* 1 `epoch` = 32 `slot` = 384秒（6分24秒）

通常、`finality` に達するまでに **2 `epoch`（= 約12分48秒）** が必要です。
実際には、トランザクションがどのタイミングでブロックに含まれるかによって、チェックポイントに達するまでの待ち時間が加わるため、平均すると **約2.5 `epoch`（≒ 15分程度）** と考えられます。

## PoSの流れ

![PoSの流れの図](https://storage.googleapis.com/zenn-user-upload/3b183668b502-20250924.png)

```mermaid
sequenceDiagram
    participant VP as Validator (Proposer)
    participant VC as Validator (Committee)
    participant AG as Aggregator (Validator)
    participant BN as Beacon Node (Consensus)
    participant EX as Execution Client (EVM)
    participant GS as Gossip network

    Note over BN: RANDAOに基づいて<br/>ProposerとCommitteeを選定

    VP->>EX: トランザクションを収集
    VP->>BN: 新しいブロックを生成
    VP->>GS: ブロックをブロードキャスト

    VC->>BN: 受け取ったブロックを検証
    VC->>GS: attestationを送信（head, source, target）

    AG->>VC: attestationを収集
    AG->>BN: 集約attestationを生成
    AG->>GS: 集約attestationをgossip

    BN->>BN: フォーク選択（LMD-GHOST）<br/>headブロックを決定
    BN->>BN: チェックポイント処理（Casper FFG）<br/>justification / finalization

    BN->>VP: rewardまたはpenaltyを適用
    BN->>VC: rewardまたはpenaltyを適用
    BN->>AG: rewardまたはpenaltyを適用
```

:::message
### Gossip network

Ethereum では **Gossip プロトコル**と呼ばれる通信方式を使って、PoSのコンセンサスに必要な情報を全ノードに伝播させています。
ノードは少数の隣接ピアとだけ直接通信し、受け取った情報をさらに他のピアへ転送していくため、最終的にネットワーク全体へと広がります。
口コミのように伝わる仕組みなので「**Gossip**」と呼ばれています。

この通信方式を通じて、`proposer` が作成したブロックや、`committee` が提出する `attestation`、`aggregator` がまとめた `aggregated attestation` など、合意形成に必要なデータが共有されます。
各メッセージは受け取るたびに検証されて不正なものは破棄されるため、改ざんや欠落を防ぎながら全体に行き渡るのが特徴です。

結果として、EthereumのPoSにおいて Gossipは「**ブロックや投票情報を全ノードに効率的に行き渡らせる仕組み**」であり、フォーク選択や `finality` 判定といった上位のコンセンサスプロセスを支える基盤になっています。
:::

1. **役割の決定（`assignment`）**
    各 `epoch` の開始時に、`RANDAO` を入力とする `shuffling` によって、各 `slot` の `block proposer` と `committee`（内部で `aggregator` も）が決まります。
    1 `slot` は 12 秒、32 `slot` で 1 `epoch`（約6分24秒）です。
2. **`block proposal`**
    決まった `block proposer` がトランザクションを集めて新しいブロックを生成し、`broadcast` します。
    `block proposal` はその `slot` の `randao_reveal` をブロックに含めます。
    :::message
    `randao_reveal` は、`validator` が毎回の `block proposal` に含める乱数の公開値です。
    各 `validator` は事前に秘密値のハッシュを提出しておき、`proposal` の時にその秘密値を公開します。
    公開された値（reveal）は `RANDAO` に混ぜ合わされ、次の `slot` や `epoch` で使う乱数（`randao_mix`）が更新されます。
    :::
4. **`attestation` 提出**
    その `slot` の `committee` が受け取ったブロックに対する `attestation` を作成・送信します。
    `attestation` には以下が含まれます。

    * 現在の先頭ブロック（`head`）
    * 直近の `justified` チェックポイント（`source`）
    * 現在のターゲットとなるチェックポイント（`target`）
     `aggregator` が複数の `attestation` を集約してネットワークに送信し、これらは次の `slot` 以降のブロックに取り込まれます（最短 1 `slot` 遅延）。
5. **フォーク選択（`LMD-GHOST`）**
    各ノードは最新の `attestation` を重み（`effective balance`）付きで集計し、重みが最大のチェーンの先端にあるブロックをその時点の `head` として採用します。
    `head` はあくまで「**今延長すべき先頭**」で、後続の `attestation` により変化します。
6. **チェックポイント処理（`Casper FFG`）**
    各 `epoch` の境界でチェックポイントへの `attestation` を集計し、`active validator` の `effective balance` の **2/3 以上** の支持があれば、そのチェックポイントを `justified` とみなします。
    次の `epoch` のチェックポイントでも同条件が満たされると、先に `justified` だったチェックポイントを `finalized` とみなします。
    `finalized` より前の履歴はEthereum上の `reorg` の対象にしません。
    `finality` に到達する目安は **2 `epoch`（= 64 `slot` ≒ 約 12 分 48 秒）** です。
7. **インセンティブ計算**
    `validator` が正しく `block proposal` を行い、`attestation` を期限どおりに提出した場合は `reward` が支払われます。
    `attestation` を提出しない／遅れるなどの不参加があれば `penalty` になります。
    さらに、同一 `slot` で2つのブロックを提案する `double proposal`、矛盾する投票を出す `double vote`、過去の自分の投票を包含する矛盾投票である `surround vote` などの重大な違反は `slashing`（`stake` の削減と強制 `exit`）の対象です。
    加えて、`finality` が長時間成立しない異常状態では `inactivity leak` が発動し、非参加側の `effective balance` を段階的に減少させて参加中の `validator` が 2/3 を満たせるようにし、`finality` を再び成立させます。

## Reference

- https://ethereum.org/ja/developers/docs/consensus-mechanisms/pos
- https://ethereum.org/ja/staking
- https://ethereum.org/ja/developers/docs/consensus-mechanisms/pos/block-proposal
- https://ethereum.org/ja/developers/docs/consensus-mechanisms/pos/gasper
- https://ethereum.org/ja/developers/docs/consensus-mechanisms/pos/pos-vs-pow
- https://github.com/ethereum/consensus-specs

# Tendermint

## 概要

Tendermintは、ブロックチェーンにおける**BFT（Byzantine Fault Tolerance：ビザンチン耐性）コンセンサスアルゴリズム**です。

:::message
### BFT

ビザンチン将軍問題は、分散した参加者がメッセージだけで合意する状況を抽象化した説明です。
複数の将軍（参加者）が行動を一致させる必要がある一方で、連絡手段は伝令（ネットワークメッセージ）のみで、遅延・欠落・改ざんが起こり得ます。
さらに一部の将軍は裏切り者（任意の不正挙動を行うノード）として、相手ごとに矛盾した指示を送ったり沈黙したりできます。
この条件下で「**正直な将軍が必ず同じ行動にまとまれるか**」が問題です。
重要なのは、どちらの行動が正しいかではなく、正直な参加者が矛盾なく同じ決定に到達できるかどうか、という点です。

理論的には、ノード総数を $n$、不正ノード数を $f$ とすると、合意の安全性（Safety：正直な参加者同士が矛盾する決定を採用しない）と活性（Liveness：通信が続く限り最終的に決定に到達する）を同時に満たすための代表的な境界は以下です。

$$
n \ge 3f + 1
$$

つまり、不正が全体の3分の1未満であれば、正直な参加者は最終的に同じ決定に達することができます。
多くのBFT系コンセンサスでは2/3以上の同意を閾値として用い、この閾値を満たした時点で決定を最終確定（finality）とみなします。
:::

特徴は以下の3つです。

- **即時ファイナリティ**
    PoWのような「**確率的ファイナリティ**」ではなく、バリデータの投票が2/3以上集まれば即座にファイナライズします。
- **マイニング不要**
    計算競争は行わず、合意形成は投票によって決まります。
- **ABCIによる汎用性**
    Tendermintはコンセンサス処理を担う仕組みを提供し、アプリケーションはABCI（Application Blockchain Interface）を通じて連携します。
    これにより、開発者はコンセンサスの実装を自ら行う必要がなく、ABCIを介してトランザクションの検証や状態管理といったアプリケーション固有の処理を実装できます。

:::message
### ABCI（Application Blockchain Interface）

#### 概要

**ABCI（ApplicationBlockchainInterface）**は、Tendermintが提供する「**コンセンサス層**」と「**アプリケーション層**」を接続するための汎用インターフェースです。

TendermintCoreは、ブロック提案と投票による合意形成のみを担当します。
一方、ブロック内トランザクションに対する検証規則の適用（署名・nonce・手数料・サイズ等の検査）や、手数料計算・権限制御・トランザクション処理に伴うステート遷移はアプリケーション側で実装します。
ABCIは、このコンセンサス層—アプリケーション層のインターフェースを規定します。
これにより、コンセンサスの仕組みを変更せずに、アプリケーション側のルールやステート遷移ロジックを差し替え・拡張できます。

#### 仕組み

Tendermint Coreとアプリケーションはプロセスとして分離され、ソケット通信（通常はgRPC）を通じてABCIメッセージをやり取りします。
Coreがブロックやトランザクションを処理する時には、その内容をABCI経由でアプリケーションに渡し、アプリケーションは検証・実行結果を返します。
これにより、コンセンサスとステート遷移の責務が明確に分離されます。

ABCIには大きく分けて以下のようなメソッド群があります。

* **CheckTx**
    Mempool受け入れ前の検証を行います。
    署名の有効性、サイズやガス上限、手数料、シーケンス番号（nonce）、二重送付（double-spend）禁止ルールなどを確認します。
* **DeliverTx**
    ブロックに含まれるトランザクションを実際に実行してアプリケーションのステートを更新します。
* **Commit**
    ブロック処理の最後に呼ばれ、アプリケーションの新しいステートルートを返します。
* **BeginBlock / EndBlock**
    ブロックの開始時・終了時に呼ばれ、ブロック単位で必要な処理（スラッシングや報酬計算など）を行います。

#### 役割

ABCIの役割は「**コンセンサスを行う層**」と「**アプリケーションロジックを実装する層**」を切り離すことにあります。
これにより、開発者は任意のプログラミング言語でアプリケーションを実装でき、Tendermint Coreと組み合わせてブロックチェーンを構築できます。
Cosmos SDKが典型例で、ABCIを通じてTendermintと連携しながらステーキングやガバナンスといったモジュールを実装しています。
:::

## Tendermintの仕組み

### バリデータ

Tendermintでは、投票権（votingpower）を持つノードをバリデータと呼びます。
投票権はステーク量などの基準で割り当てられ、不正ノードが全体の1/3未満であれば、Safety（安全性）＝正直な参加者同士で矛盾するファイナリティが同時に成立しないこと、Liveness（活性）＝通信が継続する限りいずれブロックがファイナリティに到達することの両方を満たします。
各バリデータは署名付きメッセージで投票し、その重みは投票権に比例します。

### Height／Round

ブロック番号をHeightと呼び、各Heightで1つのブロックをファイナライズします。
ファイナライズに至らない可能性を考慮し、同じHeightの中で**Round**を順に進めます。
各Roundには提案者（Proposer）が1名割り当てられ（通常は投票権に基づく重み付きローテーション）、提案が間に合わない・不正である・2/3以上の同意に届かない場合はタイムアウトにより次のRoundへ移行します。
提案を受け取れないときや不正を検出したとき、バリデータは「**ブロックなし**」を意味する`nil`に投票できます。

### 投票フェーズ

各Heightでは、同じHeight内で次の手順を順に実行してブロックをファイナライズします。

1. **Propose**
    提案者（Proposer）がMempoolからトランザクションを収集し、ブロック案を提示します。
2. **Prevote**
    各バリデータは提案されたブロックを検証し、正しく処理可能と判断すれば該当ブロックに投票して不正または条件を満たさないと判断すれば `nil` に投票します。
3. **Precommit**
    特定ブロックに**2/3以上**のPrevoteが集まったことを観測したバリデータは、そのブロックにPrecommitを投じます。
    条件が満たされなければ次のRoundへ進みます。
4. **Commit**
    同一ブロックに**2/3以上**のPrecommitが集まった瞬間、そのブロックをファイナライズして各ノードはチェーンへ追加します。

Tendermintには**ロック（lock）規則**があります。
ロック規則とは、あるラウンドで特定ブロックにPrecommitしたバリデータが、後続ラウンドでも同じブロックの支持を継続する義務を課す仕組みです。
別ブロックへの切り替えは、その別ブロックに対するPrevoteが2/3以上集まった（polkaを満たした）ことを観測し、ロック更新または解除の条件を満たした場合にのみ許可されます。
これにより、競合ブロックで矛盾するファイナリティが同時成立することを防ぎます。

:::message
#### Polka

**Polka**は、あるラウンドにおいて1つのブロックがバリデータのPrevoteで2/3以上の支持を集めた状態を指します。

Prevoteは「**このブロックを候補として次の段階に進めることに同意する**」という一次投票です。
Prevoteが複数のブロックに分散すると、どのブロックも優勢とは言えません。
これに対し、1つのブロックに対して2/3以上のPrevoteが集まった場合、そのブロックは次の段階へ進むに十分な支持を得たとみなされ、この状態をPolkaと呼びます。
:::

### 即時ファイナリティ

Tendermintは、同一Round内で**2/3以上**のPrecommitが観測された時点で、そのブロックにファイナリティを与えます。
ファイナライズ後はプロトコル上の巻き戻しが起きないため、reorgを前提に待機する必要はありません。
ネットワーク遅延などで1回のRoundで2/3以上が揃わない場合は次のRoundへ進み、条件が整い次第、同じHeightで即時ファイナライズします。

## Tendermintの流れ

![Tendermintの流れの図](https://storage.googleapis.com/zenn-user-upload/b865884078ed-20250925.png)

1. **Proposer selection（weighted round-robin）**
    各Heightの各Roundで1名のProposerを割り当てます。
    割り当ては投票権（voting power）に基づく重み付きローテーションで行われます。
    Proposerが提案を間に合わせない、不正な提案をする、あるいは以後の投票で条件に達しない場合、Roundのタイムアウトにより次のRoundへ移行し、新しいProposerが選ばれます。
    ここまでで合意は成立していませんが、「**誰が提案するか**」が決まります。

:::message
### 重み付きローテーション

Tendermintでは、各Height/各Roundの**Proposer（提案者）** を、バリデータの**voting power（投票権）** に比例する頻度で選びます。
頻度だけでなく、短期的な偏り（同じノードに連続で偏ること）も抑える必要があります。
そこで用いるのが**重み付きローテーション（Weighted Round-Robin）** で、Tendermintでは内部的に**Proposer Priority**という値を各バリデータに持たせて実現します。

各バリデータ `i` に、固定の `voting_power[i]` と可変の `priority[i]` を持たせます。
各Height/各Roundで次の処理を行います。

1. **累積**
    すべてのバリデータについて `priority[i] += voting_power[i]` を加算します。
    投票権が大きいほど優先度が速く増えます。
2. **選出**
    `priority[i]` が最大のバリデータを **Proposer** に選びます。
    最大値が同じ場合は、全ノードが同じ結果に到達できるよう、事前に定めた一意の順序（例：バリデータのアドレスを昇順で比較）で一人を選びます。
3. **調整**
    選ばれたバリデータ `k` に対して `priority[k] -= total_voting_power`（全バリデータの `voting_power` 合計）を行います。
    これで選ばれた直後は優先度が大きく下がり、次回以降は他のバリデータが選ばれやすくなるようにバランスをとります。

この3手順を各Roundごとに繰り返すと、長期的には「**選出回数 ≈ voting power の比率**」になり、短期的には極端な偏りが出にくくなります。

#### 例

バリデータA/B/Cの `voting_power` を `A=50, B=30, C=20`、初期 `priority=0` とします。
`total=100` です。

* 1回目の累積後
    `priority = [50, 30, 20]` → 最大はA → Aを選出 → Aの`priority -= 100` → `[-50, 30, 20]`
* 2回目の累積後
    `[0, 60, 40]` → 最大はB → B選出 → `[0, -40, 40]`
* 3回目の累積後
    `[50, -10, 60]` → 最大はC → C選出 → `[50, -10, -40]`
* 4回目の累積後
    `[100, 20, -20]` → 最大はA → A選出 → `[0, 20, -20]`

このように、A\:B\:C ≈ 5:3:2 の比で巡回し、連続選出が抑制されます。

#### バリデータ集合の変化への対応

バリデータの追加・削除・投票権変更は、`EndBlock` でアプリケーションが提案し、次のブロックHeight（H の次である H+1）で反映されます。
つまり、Height = H のブロック処理中に提案された変更は、**Height = H+1** 以降の `Proposer` 選出や `voting power` に適用されます。
:::

2. **Block proposal（from Mempool）**
    選ばれたProposerは、Mempoolからトランザクションを収集してブロック案を生成し、ネットワークへブロードキャストします。
    ヘッダには前ブロックハッシュ、時刻、Proposer署名などを含みます。
    ブロック案は候補にすぎないため、この後の投票で支持が示されなければ先へ進みません。
    提案が届かない、あるいは明らかな不整合を含む場合、バリデータは次段階で `nil` を選択できます。
3. **Prevote**
    各バリデータは受信したブロック案を検証し、正しく処理可能と判断すればそのブロックにPrevoteを投じ、そうでなければ `nil` に投票します。
    ここで特定のブロックに**2/3以上**のPrevoteが集まった状態を **polka** と呼びます。
    **polka** は、あるラウンドで特定のブロックに対するPrevote が 2/3 以上に到達した状態を指します。
    この状態はPrecommitを行うための前提条件であり、さらにロック更新・解除の判断基準として用いられます。
    Prevoteが2/3以上に達しない場合は、タイムアウト後に次のラウンドへ進みます。
5. **Precommit（lock / polka）**
    バリデータは、自身が観測したPrevoteの集計に基づき、polkaを得たブロックにPrecommitを投じます。
    ロック規則は、あるラウンドで特定ブロックにPrecommitした時点でそのブロックに「**ロック**」され、同じ `Height` の後続ラウンドでは原則としてそのブロックに `Prevote/Precommit` を出し続けることを求める規則です。
    別ブロックへ切り替えるのは、より新しいRoundでその別ブロックが2/3以上のPrevote（= 新しいpolka）を得たことを観測し、ロック更新・解除の条件を満たした場合に限られます。
    これにより、競合候補があってもSafetyを保ちながら進行できます。
6. **Commit（finality）**
    同一ブロックに**2/3以上**のPrecommitが集まった時点で、そのブロックに**ファイナリティ**が与えられ、各ノードはブロックをチェーンに追加します。
    ファイナライズ後はプロトコル上の巻き戻し（reorg）を行いません。
    ネットワーク遅延などで条件に達しない場合は次のRoundへ進み、同じHeightのまま条件が整い次第ファイナライズします。
7. **ABCI processing（DeliverTx → EndBlock → Commit）**
   ファイナライズ後、コンセンサス層はABCIを通じてアプリケーションにブロックを渡します。
   * **DeliverTx**
       各トランザクションを実行し、署名・nonce・手数料規則・権限制御などの検査を行いながらステートを更新します。
   * **EndBlock**
       二重投票などのEvidence処理、スラッシング、報酬・罰則の計算など、ブロック末尾の処理を行います。
   * **Commit**
       新しいステートルートを返し、次のHeightへ移行します。

```mermaid
sequenceDiagram
    autonumber
    participant P as Proposer
    participant V1 as Validator A
    participant V2 as Validator B
    participant V3 as Validator C
    participant N as Gossip Network
    participant A as Application (ABCI)

    Note over V1,V3: Height = H, Round = r

    %% Proposer selection
    V1->>V1: Proposer selection（重み付きローテーション）
    V2->>V2: 同期して同一のProposerを計算
    V3->>V3: 同上
    V1-->>P: Proposer = P（最大が同一のときは、事前定義の一意の並びで一者に決定）

    %% Proposal
    P->>P: Mempool から Tx を収集
    P->>N: Block proposal（ヘッダ・Tx・署名）をブロードキャスト

    %% Prevote
    N->>V1: Proposal を受信
    N->>V2: Proposal を受信
    N->>V3: Proposal を受信
    V1->>V1: 検証（ヘッダ/署名/整合性）
    V2->>V2: 検証
    V3->>V3: 検証
    V1->>N: Prevote（ブロック or nil）
    V2->>N: Prevote（ブロック or nil）
    V3->>N: Prevote（ブロック or nil）
    V1->>V1: Prevote を集計し 2/3 判定
    V2->>V2: Prevote を集計し 2/3 判定
    V3->>V3: Prevote を集計し 2/3 判定
    Note over V1,V3: 同一ブロックへの Prevote が 2/3 以上 → polka 成立

    %% Precommit + Lock
    alt polka が成立
      V1->>N: Precommit（対象ブロック）
      V2->>N: Precommit（対象ブロック）
      V3->>N: Precommit（対象ブロック）
      V1->>V1: ロック規則：対象ブロックにロック
      V2->>V2: ロック規則：対象ブロックにロック
      V3->>V3: ロック規則：対象ブロックにロック
    else polka 未達（タイムアウト）
      V1->>V1: Round r+1 へ移行
      V2->>V2: Round r+1 へ移行
      V3->>V3: Round r+1 へ移行
    end

    %% Commit (finality)
    V1->>V1: Precommit を集計し 2/3 判定
    V2->>V2: Precommit を集計し 2/3 判定
    V3->>V3: Precommit を集計し 2/3 判定
    Note over V1,V3: 同一ブロックへの Precommit が 2/3 以上
    V1->>V1: ブロックにファイナリティ
    V2->>V2: ブロックにファイナリティ
    V3->>V3: ブロックにファイナリティ

    %% ABCI execution
    V1->>A: DeliverTx（Tx 実行・ステート更新）
    V2->>A: DeliverTx
    V3->>A: DeliverTx
    V1->>A: EndBlock（Evidence/報酬・罰則など）
    V2->>A: EndBlock
    V3->>A: EndBlock
    V1->>A: Commit（新しいステートルート）
    V2->>A: Commit
    V3->>A: Commit

    Note over V1,V3: Height H が完了 → 次のブロック Height（H+1）へ
```

### Tendermintの時間的目安

1ブロックのファイナライズは通常数秒〜十数秒程度で、ネットワークや設定次第では数十秒になる場合があります。
ネットワークが安定していれば1つのRoundで直ちにファイナライズされますが、提案が届かない・不正である・あるいは**2/3以上**に達しない場合は次のRoundへ進むため、その分だけ遅延します。

## Reference

- https://docs.tendermint.com
- https://github.com/tendermint/tendermint
- https://tendermint.com/static/docs/tendermint.pdf

# PoA

## 概要

PoA（Proof-of-Authority／権威証明）は、あらかじめ承認された署名者（=バリデータ。ブロックに署名して正当性を示す役割のノード）だけがブロックを作れる、許可型（参加者や権限を管理するタイプ）のコンセンサスです。
匿名多数の参加を前提とするPoW/PoSと違い、身元や組織的責任を伴う少数ノードに権限を集中させることで、短いブロック時間と高スループットを実現します。
PoAでは「**誰が署名者になるか、またその交代をどう行うか**」という信頼の前提が仕組みのコアです。
実際の運用では、この部分をスマートコントラクトを用いたガバナンスの仕組みとして設計し、透明性を確保しながら管理することが重要です。

## PoAの仕組み

### 署名者集合（Validator/Sealer）

ネットワークは「**署名者リスト**」を管理して、どのノードがブロックを生成できるかを定めます。
多くの実装では、このリストをオンチェーンのコントラクトで保持し、投票によって署名者の追加や削除を決定します。
こうした仕組みにより、匿名のノードが大量に参加して支配しようとするSybil攻撃を防ぎ、署名者の信頼性を保ちます。

### ブロック生成方式（2種類）

PoAには主に以下の2種類があります。

* **スロット型PoA（Clique／Aura）**
    時間を一定間隔のスロットに区切り、各スロットでは順番で選ばれた署名者（`in-turn`：そのスロットの担当署名者）がブロックを作成して署名します。
    `in-turn` がブロックを出せない場合は、別の署名者（`out-of-turn`：代替の署名者）が代わりに生成します。
    Ethereum系クライアントで広く利用され、セットアップも容易です。
* **BFT型PoA（IBFT2／QBFT）**
    BFT（ビザンチン障害耐性）アルゴリズムを用い、ブロック提案→投票→承認の3段階を経て、署名者の3分の2以上の同意を得たブロックはその時点でファイナリティに達し、巻き戻ることはありません。
    HyperledgerBesuでは、この方式（IBFT2/QBFT）が推奨されています。

### Finalityと安全性

* **スロット型**
    フォークが発生するとチェーン選択規則によって分岐が解消されますが、短い巻き戻り（reorg）は発生する可能性があります。
    そのため、ある程度のブロックが積み重なった時点で「**事実上のファイナリティ**」とみなします。
* **BFT型**
    コミット時点でブロックが即時確定します。
    署名者が `n` 人いる場合、そのうち `3f+1` 人の構成で最大 `f` 人が不正や故障を起こしても、2/3以上の署名が集まれば、そのブロックはファイナリティに達して合意の安全性が保たれます。

### ガバナンスと運用

署名者の交代・追加・停止、投票ルールやタイムアウト設定といった運用ポリシーは、オンチェーンのコントラクトや設定で明確に定義されます。
これにより履歴が残り、透明性を確保できます。
不正行為を行った署名者は投票によって除名できます。

### 適用領域

PoAは、参加者を特定できる組織間ネットワークに向いています。
高スループット・低遅延を実現しやすく運用コストも低いことから、コンソーシアムチェーンや開発・テスト環境でよく利用されています。
パブリック用途でも事例はありますが、信頼をどこに置くか、中央集権度をどこまで許容するかといった設計判断が必要です。

## PoAの流れ

### スロット型（Clique／Aura）のフロー

![スロット型（Clique／Aura）のフローの図](https://storage.googleapis.com/zenn-user-upload/2ffd820a0ff0-20250925.png)

```mermaid
sequenceDiagram
    autonumber
    participant V as Validator (in-turn)
    participant O as Validator (out-of-turn)
    participant N as Network (Gossip)
    participant P as Peers

    %% 1. Validator set
    Note over V,O: 署名者リストはオンチェーンで管理される

    %% 2. Slot assignment
    V->>V: スロット割り当て（in-turn決定）

    %% 3. Block proposal
    V->>V: Mempoolからトランザクション収集
    V->>V: ブロックを組み立てて署名

    %% 4. Broadcast & verify
    V->>N: ブロックをGossipで伝達
    N->>P: ピアへブロックを拡散
    P->>P: 署名者の正当性を確認<br/>トランザクションやヘッダーを検証
    P-->>N: 不正なブロックは拒否

    %% 5. Fallback sealing
    alt in-turnが提案しなかった場合
        O->>O: out-of-turnがブロック生成
        O->>N: 代替ブロックを伝達
    end

    %% 6. Fork choice
    alt 同じブロックHeightで複数ブロック
        P->>P: フォーク選択ルールを適用
        Note over P: 1. in-turnブロックを優先<br/>2. 累積難易度を比較
    end

    %% 7. Confirmations
    P->>P: 一定の承認深さまで待機
    Note over P: 十分な深さで実質的にファイナリティに達する
```

#### 1. **Validator set**

ネットワークは、誰がブロックを生成できるのかを示す署名者リストを保持します。
このリストはオンチェーンの管理コントラクトや設定ファイルで管理され、署名者の追加・削除は投票などの手続きで行われます。
これにより、署名者が正しく身元と責任を持った主体であることが保証され、Sybil攻撃（複数の名ノードを作って支配する攻撃）を防ぎます。

#### 2. **Slot assignment**

時間は一定間隔のスロットに区切られ、各スロットごとに**in-turn（当番）署名者**が決まります。
この割り当ては、署名者リストに基づきラウンドロビン（順番に回す方式）や乱数を用いた簡易ルールで決定されます。
これにより、誰が次にブロックを提案するかが予測可能で、無駄な競争が発生しません。

#### 3. **Block proposal**

そのスロットに割り当てられた署名者は、Mempoolにあるトランザクションを収集して新しいブロック案を組み立てます。
ブロックヘッダーには署名者自身の署名が含まれ、これにより「**正規の署名者が生成したブロック**」であることが証明されます。

#### 4. **Broadcast & verify**

提案されたブロックは**Gossipプロトコル**によってネットワーク全体に送信されます。
各ノードは、受け取ったブロックに対して以下を検証します。

* ブロックを作成したのが署名者リストに含まれるノードか
* ブロックの内容（トランザクションやヘッダー）がルールに従っているか

この検証により、不正なブロックはネットワークに広がる前に排除されます。

:::message
Gossipプロトコルは、P2Pネットワークにおけ*情報拡散の仕組みの1つです。
各ノードは自分が受け取ったメッセージを、ランダムに選んだ少数のノードへと伝えます。
受け取ったノードはさらに別のノードへ伝える、という動作を繰り返すことで、最終的にネットワーク全体に情報が行き渡ります。

この方法は、人から人へ噂（gossip）が広がっていく様子に似ているため、Gossipと呼ばれています。
:::

#### 5. **Fallback sealing**

スロットに割り当てられた署名者がその時間内にブロックを提案しなかった場合、**out-of-turn（補欠）署名者**が代わりにブロックを生成します。
この仕組みにより、一部の署名者がオフラインになってもブロック生成が途切れず、ネットワークは継続して稼働できます。

#### 6. **Fork choice**

同じブロックHeightで複数のブロックが生成されるとフォークが発生します。
スロット型PoAでは「**フォーク選択ルール（fork choice rule）**」を適用し、どのブロックを正規のチェーンとして採用するかを決定します。

* `in-turn`（割り当て署名者）が作成したブロックを優先する
* 難易度の累積値を比較して、より累積難易度が大きいチェーンを選ぶ

### 7. **Confirmations**

スロット型PoAは即時のファイナリティ（finality）を持たず、チェーンが短く巻き戻る（reorg）可能性があります。
そのため、実際の利用では「**十分なブロック数が積み重なったら、そのブロックは事実上ファイナリティに達した**」と見なす運用をします。
これが `Confirmations`（承認深さによる実質的なファイナリティ）です。


### BFT型（IBFT2／QBFT）のフロー

![BFT型（IBFT2／QBFT）のフローの図](https://storage.googleapis.com/zenn-user-upload/ae76cc6a6789-20250925.png)

```mermaid
sequenceDiagram
    autonumber
    participant V as Validators
    participant P as Proposer
    participant N as Network (Gossip)

    %% 1. Validator set
    Note over V,P: 署名者リストはオンチェーンで管理される

    %% 2. Proposer selection
    V->>P: ラウンドごとに提案者を選出

    %% 3. Block proposal
    P->>P: Mempoolからトランザクション収集
    P->>P: ブロックを組み立てて署名
    P->>N: ブロックを伝達
    N->>V: 提案ブロックを配信

    %% 4. Prepare phase
    V->>N: Prepareメッセージを送信
    N->>V: 他の署名者にPrepareを転送

    %% 5. Commit phase
    V->>N: Commitメッセージを送信
    N->>V: 他の署名者にCommitを転送

    %% 6. Finality
    alt Commitが2/3以上揃う
        V->>V: ブロックはファイナリティに達する
    else 不足
        V->>P: タイムアウト → 次ラウンドの提案者へ
    end
```

#### 1. **Validator set**

スロット型と同様に、署名者リストをオンチェーンのコントラクトや設定で管理します。
署名者の追加・削除は投票などで決められ、リストに含まれるノードのみが合意に参加できます。

#### 2. **Proposer selection**

ラウンドごとにブロック提案者（Proposer）を決定します。
提案者は署名者リストに基づき、順番や乱数などのルールで選ばれます。

#### 3. **Block proposal**

選ばれたProposerがMempoolからトランザクションを収集し、新しいブロックを作成します。
作成したブロックは署名され、他の署名者に伝達されます。

### 4. **Prepare phase**

ブロックを受け取った署名者は、提案が有効であると判断した場合に**Prepareメッセージ**を送信します。
この段階で、署名者たちは「**このブロックを次の候補として受け入れる**」意思を表明します。

### 5. **Commit phase**

一定数のPrepareメッセージを受け取ると、署名者はそのブロックに対して**Commitメッセージ**を送ります。
`n` 人の署名者のうち `3f+1` 人構成で最大 `f` 人が故障または不正でも、**2/3以上のCommit署名**が集まれば合意は成立します。

### 6. **Finality**

2/3以上のCommit署名が揃った時点で、そのブロックは**ファイナリティに達し**、巻き戻ることはありません。
この即時ファイナリティが、BFT型PoAの大きな特徴です。

## Reference

- https://kando.tech/sites/default/files/2021-10/POA%20Whitepaper.pdf
- https://ethereum.org/developers/docs/consensus-mechanisms/poa
- https://openethereum.github.io/Permissioning
- https://openethereum.github.io/Proof-of-Authority-Chains
- https://besu.hyperledger.org/private-networks/how-to/configure/consensus
- https://besu.hyperledger.org/private-networks/concepts/poa

# 最後に

今回はコンセンサスアルゴリズムについてまとめてきました。

他でも色々記事を書いているのでぜひよろしければ読んでいってください！

https://amzn.asia/d/gxvJ0Pw

https://cardene.notion.site/EIP-2a03fa3ea33d43baa9ed82288f98d4a9EIP-2a03fa3ea33d43baa9ed82288f98d4a9

https://qiita.com/cardene

https://mirror.xyz/0xcE77b9fCd390847627c84359fC1Bc02fC78f0e58

https://chaldene.net/

https://twitter.com/cardene777

https://cardene.substack.com/
