區塊就是共用筆記本裡一頁封了印的紙
想像一本由成千上萬個陌生人共用的筆記本。每隔幾分鐘,就有人在一頁新紙上寫滿一批付款,接著用蠟印把這頁封起來,並在角落抄下「上一頁」的印記。任何人都能一頁一頁往回翻,每頁都指向前一頁,一路翻到有史以來寫下的第一頁。這種封了印、又回指前頁的紙,就是一個區塊;而整本裝訂好的筆記本就是區塊鏈——一本沒有任何單一個人擁有的分散式帳本。
打開一個區塊,你會發現裡頭恰好分成兩部分。標頭(header)是那枚小小的摘要印章——在比特幣裡它正好是 80 位元組,無論這個區塊有多繁忙都一樣。本體(body)則是真正的交易清單,可能有一百萬位元組甚至更多。被拿去做雜湊、被串成鏈、被下一個區塊引用的,是標頭;而本體則是它所擔保的內容。這篇導覽會把兩者都解剖一遍,最後再看那個把一個區塊連到上一個區塊的單一欄位。
標頭:封印整個區塊的 80 位元組摘要
區塊標頭是整個設計的核心,因為被拿去做雜湊、用來決定區塊身分的,是標頭而不是本體。一個比特幣標頭把六個欄位塞進固定的 80 位元組裡:
- 版本 version(4 位元組)——這個區塊遵循哪一套共識規則。
- 前一區塊雜湊 previous block hash(32 位元組)——父區塊標頭的雜湊。這就是那條向後的連結(第四節)。
- Merkle 根 Merkle root(32 位元組)——本體裡每一筆交易的單一指紋(第三節)。
- 時間戳記 timestamp(4 位元組)——礦工建出這個區塊的時間,以 Unix 紀元秒數表示。
- bits / 目標 target(4 位元組)——區塊雜湊必須低於的難度目標。
- 隨機數 nonce(4 位元組)——礦工不斷試湊、好讓雜湊滿足目標的那個一次性數字。
{
"version": 1,
"prevBlockHash": "0000000000000000000000000000000000000000000000000000000000000000",
"merkleRoot": "4a5e1e4baab89f3a32518a88c31bc87f618f76673e2cc77ab2127b7afdeda33b",
"timestamp": 1231006505, // 2009-01-03 18:15:05 UTC
"bits": "0x1d00ffff", // the difficulty target
"nonce": 2083236893
}
// SHA-256(SHA-256(header)) ->
// 000000000019d6689c085ae165831e934ff763ae46a2a6c172b3f1b60a8ce26f一個區塊的身分就是它的標頭雜湊。比特幣的算法是對 80 位元組的標頭做兩次SHA-256。注意這代表什麼:本體從來不會被直接雜湊進區塊的身分裡——它只透過那 32 位元組的 Merkle 根進入。本體裡只要改一點,根就變;根(或任何其他標頭欄位)一變,整個區塊雜湊就跟著變。這一連串環環相扣的後果,正是全部安全性的核心想法,而接下來兩節就要把它追蹤出來。
本體:許多交易,一個根
本體是一份有序的交易清單。在比特幣裡,第一筆很特別——是憑空鑄出區塊獎勵的 coinbase(鑄幣)交易——後面接著礦工選入的每一筆普通付款。一個繁忙的區塊裡會有好幾千筆。本階後面的導覽會把一筆交易逐欄拆開;在這裡,我們只關心標頭如何替整份清單做摘要。
標頭裡並不會把交易一筆筆列出——它只存一個 32 位元組的 Merkle 根,一口氣替全部交易做指紋。你在上一階就親手建過這種東西:把交易雜湊兩兩配對、各自雜湊、不斷往上爬,直到頂端只剩一個雜湊。只要把任何一筆交易重新排序、新增、刪除或更動,這個根算出來就會徹底不同。於是一個永遠只有 80 位元組的固定標頭,就能忠實地對任意大小的本體做出承諾。
向後的連結:一條鏈如何誕生
現在來看那個替整個結構命名的欄位。每個標頭都帶著前一區塊雜湊——也就是它父區塊的標頭雜湊。順著這些指標走,你就一個區塊接一個區塊地往回穿越歷史。這是一個單向鏈結串列,只是那個指標並不是一個你能悄悄改指向的記憶體位址;它是一枚被烤進標頭、因而也被烤進區塊自身身分裡的密碼雜湊。
這條鏈到哪裡為止?到創世區塊——也就是第 0 號區塊——它沒有父區塊,所以它的前一區塊雜湊欄位被填成全零。比特幣的創世區塊在 2009 年 1 月 3 日被挖出,並在它的 coinbase 裡著名地嵌入了一則報紙頭條:「The Times 03/Jan/2009 Chancellor on brink of second bailout for banks」。世上存在的每一個區塊,最終都回指向那唯一一個硬寫死的根。
Block 100 Block 101
+---------------------------+ +---------------------------+
| prevHash: <hash of #99> | | prevHash: 0xA1B2... <-----+--+
| merkleRoot: 0x7f3c... | | merkleRoot: 0x9d04... | |
| timestamp, bits, nonce | | timestamp, bits, nonce | |
+---------------------------+ +---------------------------+ |
hash = 0xA1B2... --------------------------------------+
(this block's id becomes the next block's prevHash)為什麼動手腳一定露餡:骨牌效應
重點來了。假設有個攻擊者想悄悄竄改一筆舊交易——比方說在第 100 區塊裡,把一筆付款改到自己名下。看著骨牌一張張倒下:
- 改了那筆交易就改了本體,於是第 100 區塊的 Merkle 根不再吻合——重新算出來會是另一個根。
- 為了讓標頭一致,他們得把新的根寫進標頭——可這一改就改了標頭,於是第 100 區塊的雜湊也跟著變了。
- 第 101 區塊的 prevHash 欄位裡存的是第 100 區塊的舊雜湊。現在對不上了。鏈在第 101 處明擺著斷掉。
- 要修補這個斷點,他們得改寫第 101 區塊的 prevHash,這會改掉第 101 區塊的雜湊,又會弄斷第 102 區塊——於是傷害一路串連,直達鏈的最前端。
- 而且每個被改寫的標頭,它的雜湊仍然必須落在難度目標之下,所以他們得替從第 100 區塊到最前端的每一個區塊重做工作量證明——而且要比誠實的網路繼續延伸的速度還快。
這串連鎖正是人們口中區塊鏈的不可竄改性。但要說精確:光憑結構,只能讓竄改一改就露餡,而非不可能。任何重新核對雜湊的人,立刻就會看到這條鏈兜不攏。真正讓改寫變得昂貴的,是共識再加上累積的工作量——攻擊者得在算力上勝過整個誠實網路,這就是經典的 51% 攻擊,能讓他們在一次鏈重組中改寫近期的區塊。一個區塊被埋得越深,壓在它上面的工作量越多,它就越是實質上永久。這正是我們要等待「確認數」的原因。
兩個真實的標頭:比特幣 vs. 以太坊
「標頭封印本體、標頭連結父區塊」這個模式是通用的,但標頭本身各有不同。比特幣的標頭只承諾一件事:一個涵蓋區塊內所有交易的 Merkle 根。以太坊的標頭則更豐富,帶著三個根:交易根、收據根,以及狀態根。每一個都是某棵 Merkle-Patricia 字典樹的根,而且它們是用 Keccak-256 而非 SHA-256 來雜湊。
最重要的差別是狀態根(state root)。它承諾的不是區塊內的交易,而是區塊執行之後的整個全域狀態——每一個帳戶的餘額、每一位元組的合約儲存,全部摺疊進一個 32 位元組的雜湊裡。所以一個以太坊區塊不只是證明發生了哪些付款;它證明了整個系統的精確狀態,這正是讓輕用戶端與智慧合約變得可行的關鍵。
// An Ethereum header (post-Merge, abridged)
{
"number": 17000000,
"parentHash": "0x...", // the link backward — same idea as Bitcoin
"stateRoot": "0x...", // root of the entire world state
"transactionsRoot":"0x...", // root over this block's transactions
"receiptsRoot": "0x...", // root over execution receipts
"timestamp": 1681338455,
"difficulty": "0", // PoW retired at the Merge
"nonce": "0x0000000000000000", // now unused
"mixHash": "0x..." // now carries beacon-chain randomness (prevRandao)
}也誠實地交代一下歷史:自 2022 年 9 月的合併之後,以太坊不再挖礦,所以它的 difficulty 與 nonce 欄位成了凍結的遺跡,而 mixHash 則被改派去傳遞來自共識層的隨機值。但最要緊的兩個想法——標頭對本體做承諾,而 parentHash 把每個區塊串到前一個——與比特幣一模一樣。正是這副共用的骨架,才是「區塊鏈」這個詞真正指的東西。