前章で「トランザクションに署名する」ところまで来ました。では署名したものを送ったあと、何が起きるのか

ここで「ブロック」という単位が出てきます。名前のとおりブロックチェーンの中核ですが、なぜそんな単位が必要なのかは、意外と説明されないまま流されがちです。

まず実物を見る

本書のアプリで CPU 戦を 1 局遊んだときの記録です。6 手打って決着しました。

6 つのブロックが横に並び、それぞれの中に自分の 1 件と他人の数十〜百数十件が入っている図。ブロック同士は parentHash で繋がっている

ブロック 11526526  全 85 件   ← 自分の着手 1 件 + 他人の取引 84 件
ブロック 11526527  全 137 件  ← 自分の着手 1 件 + 他人の取引 136 件
ブロック 11526529  全 116 件
ブロック 11526531  全 167 件
ブロック 11526532  全 165 件
ブロック 11526536  全 77 件   ← ここで決着

読み取れることが 3 つあります。

1 手 = 1 トランザクション = 1 ブロック でした。1 手ごとに署名し、1 手ごとに 1 つのブロックへ入っています。

ブロックの中身の 99% は他人の取引です。 85 件中 84 件、167 件中 166 件は、まったく無関係な人の、まったく無関係な操作です。同じ 12 秒間にたまたま世界中から送られてきたものが、一緒に封をされています。

ブロック番号が飛んでいます。 11526528、11526530、11526533〜35 は欠番です。それらのブロックも作られていますが、自分の取引が入らなかっただけです。

トランザクションとブロックの違い

トランザクションブロック
誰が作るかあなた(署名する)バリデータ(束ねる)
何個の指示が入るか1 つ数十〜数百
中身を選べるか選べる選べない(相乗り)
順番を選べるか選べない選べない
費用ガス代を払う

トランザクションは「1 通の手紙」、ブロックは「12 秒ごとに郵便ポストを開けて、中身をまとめて封印したもの」だと思ってください。封印には前回の封印番号(parentHash)が書いてあります。

なぜ 1 件ずつではいけないのか

ここが本題です。素朴に考えれば、取引を 1 件ずつ確定していけばよさそうに見えます。なぜまとめるのか。

上段に取引 6 件を 1 つずつ鎖にした図、下段に 6 件を 1 つの束にまとめた図。上段は合意 6 回、下段は合意 1 回

理由 1: 順序の合意が高くつくから

分散システムで一番難しいのは、「どちらが先か」を全員で一致させることです。

世界中のノードが同時に取引を受け取っていて、到着順はバラバラです。ネットワークの遅延も違う。それでも全員が同じ順序に同意しないと、残高の計算が食い違ってチェーンが分裂します。

そして、この合意には時間と通信コストがかかります。1 件ごとにやっていたら、秒間 1 件も処理できません。

だから 12 秒分をまとめて「この束を、この順番で」と一度に決める。100 件を 1 回の合意で確定できれば、1 件あたりのコストは 1/100 です。ブロックとは、合意の回数を減らすための単位です。

まるばつで言えば、「X が 4 に置いた」と「O が 4 に置いた」が同時に届いたとき、どちらを先に処理するかを決めないとゲームが壊れます。それを取引 1 件ごとに世界規模で調停するのは現実的ではない、ということです。

理由 2: 鎖を短くできるから

第 17 章で見る parentHash の仕組みです。各ブロックが 1 つ前のブロックのハッシュを抱えることで、過去を書き換えると以降が全部壊れる、という性質が生まれます。

取引 1 件ずつを鎖にすると、鎖の輪が取引の数だけ必要になります。 100 件を 1 個の輪にまとめれば、輪の数は 1/100 で済む。検証する側の負担も、それだけ軽くなります。

理由 3: 時計になるから

ブロックは約 12 秒ごとに作られます(Base のような L2 は約 2 秒)。この規則正しさが、チェーン上の時計として使われます。

本書のコントラクトが「持ち時間 1 時間」を実現できるのも、block.timestamp があるからです。そしてその値は、後の章で見るようにスロットの時刻に完全に一致することがプロトコルで要求されています。ブロックという単位があるから、時間が刻めます。

自分では選べないもの

ここは実務で効いてきます。取引を送ったあと、あなたに選べないものが 3 つあります。

  • どのブロックに入るか。 混んでいれば次のブロックに回されます
  • ブロックの中で何番目か。 上の実測でも、自分の取引の位置は毎回違います
  • そもそも入るかどうか。 ガス代が安すぎれば後回しにされ続けます

3 番目が、第 11 章で扱う持ち時間の設計に直結します。「自分は着手したのに、ブロックに入らないまま時間切れになる」ということが原理的に起こりうる。だから持ち時間は、チェーンが混雑する時間より十分に長く取る必要があります。

そして 2 番目は MEV の入口です。ブロックを組む側は順番を選べるので、「あなたの取引の直前に自分の取引を入れる」ことができます。本書のゲームでは実害がほぼありませんが、金額の大きい取引では現実的な脅威です(第 18 章)。

ブロックの中身

参考までに、ブロックのヘッダに入っている主なものを挙げておきます。

フィールド意味
number何番目のブロックか
parentHash1 つ前のブロックのハッシュ
timestampこのブロックの時刻
transactionsRoot中の全取引をまとめた Merkle 木の根
stateRootこのブロック適用後の、全アカウントの状態の根
gasUsed / gasLimit使ったガスと上限

transactionsRoot が効いています。 ブロックのハッシュは、この根を経由して中の全取引に依存します。だから取引を 1 件でも差し替えると、ブロックのハッシュが変わり、次のブロックの parentHash と合わなくなる。「取引 → ブロック → 鎖」という 3 段構えで、改竄が伝播して露見します。

第 5 章で「トランザクションハッシュは生バイト列を keccak256 したもの」と確かめました。その先はこう繋がります。

生バイト列 → keccak256 → トランザクションハッシュ
                             ↓ 他の取引と一緒に Merkle 木へ
                        transactionsRoot
                             ↓ ヘッダの一部として
                        ブロックのハッシュ
                             ↓ 次のブロックの parentHash として
                        鎖

根拠を辿っていくと、最後は自分の手元の計算に着地します。 誰かを信じる必要がない、というのはこの構造のことです。

まとめ

  • トランザクション = あなたが署名した 1 つの指示。ブロック = それを他人の分と一緒に束ねたもの
  • 実測では 1 手 1 ブロック、しかし中身の 99% は他人の取引
  • ブロックが必要な理由は 3 つ。合意の回数を減らす / 鎖を短くする / 時計になる
  • どのブロックに入るか、何番目か、そもそも入るかは選べない
  • 取引・ブロック・鎖の 3 段でハッシュが繋がっているので、改竄はどこで起きても露見する