# 比特幣 白皮書解析 原文連結:[bitcoinn: A Peer-to-Peer Electronic Cash System](https://bitcoin.org/bitcoin.pdf) `(本文參照白皮書內文書寫)` 如果你是初學者,可以搭配我拍的講解影片:[比特幣核心技術解析](https://youtube.com/playlist?list=PLps5WYftESWp1_q0BR6o43uQcmV1SDLoT&si=5nVaCyng206VlPfs) ## 前言 > 這篇文章是在介紹比特幣是怎麼運作的。 > 歡迎對本文章提出指正,直接留言即可,或是可以寄信到我的信箱`ytwhyer@gmail.com` 會想要介紹比特幣,主要的原因是因為,一陣子之前,我也想學習比特幣的技術,但網路上的資源大多都只是在討論比特幣的價格,而不是比特幣的技術,而繁體中文的資料中,更是難以找到關於比特幣、區塊鏈技術的介紹。甚至許多人有買比特幣,但卻從來都不知道,他是怎麼運作的。所以我再自己學習後,決定和大家分享比特幣這個美妙的技術,希望能讓大家更輕鬆地上手。 當然,我不會講一些大家可能早就在新聞上看過的話題,例如: 1. 比特幣就是抗通膨啦 2. 比特幣就像虛擬黃金,礦工可以挖他獲得 3. ~~比特幣就是龐氏變局~~ 如果你進階一點,你可能知道: 1. 比特幣是去中心化的 2. 比特幣高度透明 3. 比特幣由眾多節點維護 4. 比特幣的帳本是不可篡改的 5. 比特幣是可信的,不需要信任單一的第三方 總之如果你真的完全不知道,你可以先稍微了解一下最基本的再回來看,今天我打算藉由當年中本聰的白皮書,把你可能有的零零散散的認知,全部接起來,讓你知道,比特幣是怎麼運作的。內容可能會涉及一些專業知識,但不用擔心,我會提供你關鍵字,你也可以一邊學一邊看,我的目標是讓國中生也看得懂(前提是你真的有興趣啦)。 ## 1. Introduction >為什麼我們需要比特幣? <details> <summary>📘 關鍵詞解釋(點我展開)</summary> - **double-spending(雙重支付)**:指同一筆比特幣被嘗試用於兩次以上的交易,是數位貨幣必須解決的關鍵問題。 - **peer-to-peer(點對點)**:一種去中心化的網路架構,使用者之間可以直接通訊與交易,無需透過中介伺服器。 </details> 用一個例子來說,假如你今天要賣一台二手電腦,你賣出並且寄到消費者手中,但他卻故意說你的電腦有問題,申請退款。這樣怎麼辦?你可能會說,就去申訴啊,但問題是就算你最終拿回了錢,你也浪費了一堆時間,以及額外的錢。而有些商家可能也為了怕顧客亂來,所以為了這個原因,而要求提供一些個資。但其實,假如今天是在菜市場買菜,付現金不就完事了,你把現金交出去就不能撤回了,你也不用因為買個高麗菜就報上自己的名字吧?而比特幣呢,就是想要在電子世界中擔任**現金**這個角色,這裡就點題了:**Electronic Cash** 比特幣-電子的現金。 而且我們目前的線上交易,甚至是紙鈔,我們都是透過一個可信的第三方才得以完成交易,這樣的問題是,假如那個可信的第三方失去信用了,我們可能會因此產生損失。例如你能確保你使用的網路銀行他們不會偷改你的餘額數字嗎?但比特幣可以,因為比特幣是由**peer-to-peer**網路組成,所有節點都擁有一份帳本,不用信任單一個節點,且有人偷竄改,也會馬上被發現。 ## 2. Transactions >比特幣是怎麼記帳的?擁有比特幣是什麼概念? <details> <summary> 📘 關鍵詞解釋(點開查看)</summary> - **UTXO(Unspent Transaction Output)**:尚未被花掉的輸出,是你可支配的「餘額」。 - **公鑰(Public Key)**:代表比特幣接收方的身份,相當於「收款地址」。 - **私鑰(Private Key)**:用來產生數位簽章,證明你擁有某筆比特幣的秘密資訊。 - **數位簽名(Digital Signature)**:用私鑰對交易簽名,讓他人能用公鑰驗證你是合法擁有者。 </details> 我們先討論一個情況: 今天小明起來,媽媽先給他100塊,他花其中50塊買早餐(給了早餐店阿姨),後來早餐店阿姨下班回家,花15塊做公車回家,假設除了媽媽原本有100以外,其他人原本都沒有錢,你會怎麼記帳? 先整理一下交易: 1. 媽媽 →(100塊)→ 小明 2. 小明 →(50塊)→ 早餐阿姨 3. 早餐阿姨 →(15塊)→ 公車司機 我猜你可能會這樣記帳,把大家的交易整理成表格統計: <div style="display: flex; gap: 10px; flex-wrap: nowrap; justify-content: space-between;"> <!-- 媽媽 --> <div style="flex: 1; min-width: 200px; background: #f9f9f9; padding: 10px; border-radius: 8px; border: 1px solid #ccc;"> <h4>📗 媽媽的記帳本</h4> <table> <thead> <tr><th>對象</th><th>金額</th></tr> </thead> <tbody> <tr><td>原本</td><td>100</td></tr> <tr><td>小明</td><td>-100</td></tr> <tr><td>結算</td><td>0</td></tr> </tbody> </table> </div> <!-- 小明 --> <div style="flex: 1; min-width: 200px; background: #f9f9f9; padding: 10px; border-radius: 8px; border: 1px solid #ccc;"> <h4>📘 小明的記帳本</h4> <table> <thead> <tr><th>對象</th><th>金額</th></tr> </thead> <tbody> <tr><td>原本</td><td>0</td></tr> <tr><td>媽媽</td><td>+100</td></tr> <tr><td>早餐阿姨</td><td>-50</td></tr> <tr><td>結算</td><td>50</td></tr> </tbody> </table> </div> <!-- 早餐阿姨 --> <div style="flex: 1; min-width: 200px; background: #f9f9f9; padding: 10px; border-radius: 8px; border: 1px solid #ccc;"> <h4>📙 早餐阿姨的記帳本</h4> <table> <thead> <tr><th>對象</th><th>金額</th></tr> </thead> <tbody> <tr><td>原本</td><td>0</td></tr> <tr><td>小明</td><td>+50</td></tr> <tr><td>公車司機</td><td>-15</td></tr> <tr><td>結算</td><td>35</td></tr> </tbody> </table> </div> <!-- 公車司機 --> <div style="flex: 1; min-width: 200px; background: #f9f9f9; padding: 10px; border-radius: 8px; border: 1px solid #ccc;"> <h4>📕 公車司機的記帳本</h4> <table> <thead> <tr><th>對象</th><th>金額</th></tr> </thead> <tbody> <tr><td>原本</td><td>0</td></tr> <tr><td>早餐阿姨</td><td>+15</td></tr> <tr><td>結算</td><td>15</td></tr> </tbody> </table> </div> </div> --- 然而,比特幣是這樣記帳的: 只記輸入(誰花了多少)和輸出(誰得到了多少) ### 帳本 <div style="display: flex; flex-direction: row; flex-wrap: nowrap; gap: 10px; justify-content: space-between; align-items: stretch;"> <!-- 箭頭 --> <span style="font-size: 2em; display: flex; align-items: center;">➡️</span> <!-- 區塊 #1 --> <div style="background:#f5f5f5; border: 2px solid #bbb; border-radius: 8px; padding: 10px 15px; min-width: 220px; box-shadow: 2px 2px 5px rgba(0,0,0,0.1); flex: 1;"> <h4>⛓️ 區塊 #1</h4> <ul> <li><strong>輸入:</strong><br>100 元 ➝ 媽媽</li> <li><strong>輸出:</strong><br>100 元 ➝ 小明</li> </ul> </div> <!-- 箭頭 --> <span style="font-size: 2em; display: flex; align-items: center;">➡️</span> <!-- 區塊 #2 --> <div style="background:#f5f5f5; border: 2px solid #bbb; border-radius: 8px; padding: 10px 15px; min-width: 220px; box-shadow: 2px 2px 5px rgba(0,0,0,0.1); flex: 1;"> <h4>⛓️ 區塊 #2</h4> <ul> <li><strong>輸入:</strong><br>100 元 ➝ 小明</li> <li><strong>輸出:</strong><br>50 元 ➝ 早餐阿姨<br>50 元 ➝ 小明(找零)</li> </ul> </div> <!-- 箭頭 --> <span style="font-size: 2em; display: flex; align-items: center;">➡️</span> <!-- 區塊 #3 --> <div style="background:#f5f5f5; border: 2px solid #bbb; border-radius: 8px; padding: 10px 15px; min-width: 220px; box-shadow: 2px 2px 5px rgba(0,0,0,0.1); flex: 1;"> <h4>⛓️ 區塊 #3</h4> <ul> <li><strong>輸入:</strong><br>50 元 ➝ 早餐阿姨</li> <li><strong>輸出:</strong><br>15 元 ➝ 公車司機<br>35 元 ➝ 早餐阿姨(找零)</li> </ul> </div> </div> 這邊稍微想一下,你就可以發現,如果站在中心化的角度去想,第一種方法直覺,且紀錄方便。但注意,比特幣是去中心化的,用第二種方式,每一筆交易只需要記錄「我花了誰給我的錢,轉給了誰」,不需要知道整個人的帳本狀況。這種方式可以分散儲存、獨立驗證,而且所有節點只要確認交易輸入對應的輸出存在且未被花掉,就能保證記帳正確。 所以你讀到這就可以知道,用有比特幣到底是什麼概念了,不像銀行一樣,會存說你有多少錢,就代表你有多少錢,而是你可以去鏈上查看,曾經有人給你多少比特幣,來得知你擁有多少比特幣。 當然看到這你心中肯定還有許多疑問,我們雖然利用數字簽名(我的例子裡沒有特別提到)確保是本人控制自己的資產,但仍然無法解決雙重支付的問題,而解決的辦法就是,公開整條鏈的資訊給所有人看。 ## 3. Timestamp Server >透過hash,我們能建立不可逆的順序,並確保資料無法竄改。 由於**hash**實在是太重要了,所以我決定先介紹一下什麼是hash,其中比特幣會用到的hash叫做**sha-256**: 簡單來說,hash就是有以下特性的function: 1. 可以給任意長度的輸入,會有固定長度的輸出 2. 就算輸入有微小的差別,輸出會有很大的差別 3. 很難反推(幾乎不可能) 4. 幾乎不可能找到,兩個輸入擁有一樣的輸出(雖然理論上可行,但機率小到不可能) > 如果你有興趣,你可以在原始碼中`sha256.cpp`看一下,他怎麼算hash值的。 啊這個能做什麼的,在這裡最大的用途就是看資料有沒有被竄改,建議大家可以實際去網路上找sha-256實際感受看看。 阿他能幹嘛能,簡單來說就是可以檢查一個東西有沒有被改過,例如你有一篇作文好了,可能有十萬字,你複製了一份,你想確認兩個是否一樣,你就把兩份都取哈希對比即可,不用一個字一個字比對。而在區塊鏈上,這可以省空間。 那這章原本的內容其實就在說,我們把每份交易內容和前一個區塊的 hash 值一起 hash,這樣除了能確認順序,也確保前後區塊緊密連結,無法竄改。 ![截圖 2025-04-29 晚上9.43.20](https://hackmd.io/_uploads/r13FP8Ryxx.png) (引用比特幣白皮書中圖片) ## 4. Proof-of-Work >那這條鏈上的區塊,到底由誰來加到鏈上? 簡單來說,就是大家來比賽吧,前面說要找區塊鏈的hash值,那我們就在區塊內,除了原本的資料外,再加入一個**Nonce**,礦工可以調整該值(除了Nonce也有其他值可以改),大家來比賽,誰能先hash出一個值,該值很小(通常會規定前面要有幾個0),就贏得了記帳權。 其實`PoW`就類似用CPU算力來投票,而不使用`ip`地址來投票是因為創一堆`ip`地址太容易了。擁有一堆CPU算力相對來說難很多。 而比特幣設計當初,也有考量到大家的算力會越來越高,所以這個難度是動態調整的。 另外,節點還要達成一個共識就是**大家都要相信最長的那條鏈**。我們可以站在攻擊者的角度想,假設你想要修改鏈上的帳本,你一定是修改了之前的某個原本已經被大家承認的區塊,然後你要自己做一條更長的鏈假鏈,讓大家去相信這條假鏈,這基本上很難做到,因為這就等於你與所有人為敵,但這是真的不可能做到嗎?答案其實是否定的,這就是**51攻擊**,有人掌握一半的節點,就有可能。所以你也不難發現,比特幣是當越多人使用時,越安全。 ## 5. Network >你是比特幣節點的一員,你需要做什麼? 1. 有新交易的話,廣播給所有人 2. 所有人把新的交易寫進區塊中 3. 開始`PoW`看誰先找到 4. 如果你找到了,就廣播給所有人這個新區塊 5. 如果你收到別人找到的區塊,確認無誤後(檢查有沒有雙花),接受這個區塊 6. 你表達接受的方式,就是透過使用該區塊的哈希值,去尋找下一個區塊,以此延續下去 這樣其實不難想像,整個鏈就能不斷的被增長維護,且大家都維護共同的、不可篡改的帳本。 另外,白皮書中還提到下面件事: **如果有兩條鏈剛好一樣長怎麼辦?** 別擔心,就選一條延續下去,一條先留著,反正最後一定會有某條比較長,就再改用那條就好。 **如果某個節點漏接了新的區塊怎麼辦?** 不用擔心,他遲早會發現了,因為如果他中間少收到一個區塊,他收到新的區塊之後,就會發現,那個區塊的哈希值怪怪的,他再去跟其他節點補齊即可。 ## 6. Incentive >如何讓大家有動力去`PoW`,讓礦工消耗資源去記帳 先說,礦工的獎勵來自於兩點: 1. 區塊獎勵 2. 手續費 我們前面知道,一個區塊裡面會有許多筆交易,被礦工打包後接到鏈上,那怎麼給礦工酬勞?很簡單,就是每個區塊的第一筆交易,就是給礦工的獎勵,這筆錢不需要由誰來給,而是直接給礦工。從最一開始是一個區塊`50`個比特幣,大約四年減半一次(準確來說是算區塊數),如今我寫文章的當下,一個區塊的獎勵是`3.125`。 到這邊大家應該就能很清楚的知道,礦工怎麼挖礦,怎麼記帳並得到比特幣的吧。 >可以看原始碼中`validation.cpp -> GetBlockSubsidy() ` 看看比特幣是怎麼減半的 你看原始碼會發現,或者光用想的就知道,減到一天總會少到沒有吧,沒錯!大概減半30幾次就全部挖完了。 但別忘了礦工還可以賺手續費,而手續費其實不是固定的,如同我們在交易(#帳本)那個章節提到的[帳本模型](#帳本),只要輸入大於輸出,剩下的就是手續費。舉例來說,我花50元買早餐,但我輸入60元,那10元就是礦工的手續費。所以手續費不是一個定價,而是付錢的人自己決定。但別當礦工是笨蛋,每個區塊大小是固定的,能放的交易數量是有限的,所以礦工會優先挑手續費高的放,所以當比特幣網路很擁擠時,你付太少錢可能你的交易就無法被確認,嚴重可能導致交易失敗,所以這也是為什麼手續費越高,交易速度越快。 ## 7. Reclaiming Disk Space > 怎麼節省空間 我們前面知道了,我們防止雙花的辦法就是公開整條鏈,但對應的代價就是,所有人都必須儲存整條鏈的資訊。而現在,我們就來講怎麼節省空間。 一筆交易合不合法,取決於之前是否有人給過他這筆錢,我們只需要確認這筆輸入是否對應到某筆尚未花費的輸出(UTXO),而不需要一直往上追溯每個來源。所以一筆交易其實在經過足夠的方塊後,是可以被刪除掉的。 但為了不影響區塊的哈希值,比特幣做了以下設計: ![截圖 2025-04-30 晚上10.42.55](https://hackmd.io/_uploads/rJjgvn1gee.png) (引用比特幣白皮書圖片) 而設計的方式也是利用`hash`將交易紀錄兩兩取hash,不斷地直到剩最後一個,我們稱之為`Root Hash`,並把它存到區塊頭中。這樣已經不重要的交易,我們就可以透過剪枝的方式剪掉,如圖右邊,刪掉交易細節,只留下哈希值。 ## 8. Simplified Payment Verification >就算你不是全節點,你也可以確認單筆交易是否成功 ![截圖 2025-05-01 晚上7.41.09](https://hackmd.io/_uploads/SJgJARggxg.png) (引用比特幣白皮書圖片) 如果你的交易資訊是`Tx3`,你只要把你的資料和`Hash01`以及`Hassh2`(其他人的交易細節對你來說不重要,拿他們的hash值只是為了驗證)自己hash出一個`Merkle Root`確認是否和鏈上的值一樣就可以。這方法只要當全網是由誠實的節點控制,最長鏈是可信的時候就可行,但如果是是交易頻繁的人,就會建議還是要自己運行全節點。 ## 9. Combining and Splitting Value >記帳的方式 其實這章節的內容,為了方便理解我已經提前在[第二章](#2-Transactions)講完了。我就補充一個重點:一筆交易是由多比輸入和兩筆輸出組成的,輸出其中一個是你要轉給誰多少錢,另一個則是給自己的找零,透過這樣,每一比輸入其實就一定來自於之前的某一比輸出。 ## 10. Privacy >隱私 我們前面說了,為了防止雙花,我們必須公開所有的交易紀錄,但這樣就等於你花了什麼錢大家都知道,很沒有隱私。那防止的方法就是,區塊鏈上紀錄的是公鑰,只要保持公鑰的匿名性,也就是說大家雖然看得到錢從哪個地址轉到哪的地址,到事實上不知道那個地址真正屬於哪一個人的。 ## 11. Calculations >用數學的方式說明比特幣很安全 這個有興趣的話,直接看原文就很清楚了。 ## 12. Conclusion 講了這麼多,我們終於得到了這樣一個電子交易系統——比特幣。它不需要依賴對任何第三方的信任。 整個系統的架構從數位簽名開始,確保了資產的所有權與轉移權是由使用者自己掌控的。 接著,透過點對點網路(P2P)、工作量證明(Proof-of-Work),以及公開完整區塊鏈的設計來防止雙重支付(double-spending)。 這套系統既簡潔又強大。節點之間幾乎不需要互相信任,只需盡可能地廣播自己知道的訊息,因為訊息的傳送不需要指定對象。 節點可以自由加入或離開網路,他們透過貢獻自己的 CPU 算力來延續區塊鏈,藉此表示對某條鏈的「認可」;反之,若拒絕貢獻算力,則表示「不認同」。 最終,整個網路中的規則與激勵機制一起維護了這個去中心化的共識體系。講了這個多,我們得到了一個電子交易系統-比特幣,它不需要依賴信任某個第三方。整體構造從數位簽名開始,我們確保了貨幣所有權的控管。再來透過p2p網路、PoW以及公開整條鏈來防止雙花。整個系統簡潔而強大,節點只需要少量的合作,不用驗證對方,因為訊息不指定傳到哪,只要盡量的散播就可以,每個節點也可以隨時加入退出,他們花費自己的CPU算力,用繼續延長鏈的方式表達接受,拒絕工作表達不接受,所有的規則和獎勵都在維護著這個共識。