比特幣開發指南之一
比特幣開發指南 - 區塊鏈
區塊鏈提供比特幣的公開的總賬, 這是個一組有序的,並且有時間戳的交易記錄。該系統用於防止雙重支付和以前的交易記錄被修改。
比特幣網路中的每個完整節點獨立儲存只包含由該節點驗證過的區塊的區塊鏈。當幾個節點在他們的區塊鏈中都有相同的區塊時,他們就被認為是達成共識的。這些節點遵循以保證共識的驗證規則叫做共識規則。本章描述了比特幣核心使用過的許多共識規則。
區塊鏈概述
上面的插圖展示了區塊鏈的一個簡化版本。交易資訊被收集到區塊的交易資料部分中。每一個交易的副本都會被hash,配對,再hash,再配對…,重複hash直到一個單獨的hash被保留,這就是merkle tree的merkle root。
merkle root被儲存在區塊頭中。每個區塊也儲存前一個區塊頭的hash,從而把區塊連結到一起。這確保在不修改記錄它的區塊和後續所有區塊情況下不能修改交易。
交易也被連結到一起。比特幣錢包軟體給人的印象就是聰是從一個錢包傳送到另一個錢包,但實際上聰是從一筆交易轉移到另一筆交易。每筆交易花費從之前的一個或多個交易中收到的聰,所以一筆交易的輸入就是之前的一筆交易的輸出。
一筆單獨的交易可以建立多個輸出,如傳送給多個地址,但是在區塊鏈中,一筆特定交易的每次輸出只能被用作一次輸入。任何後續的引用都是一個雙花——試圖重複花費相同的聰。
輸出和交易識別符號(TXIDs)掛鉤,TXIDs是已簽名交易的hash值。
因為一筆特定交易的每筆輸出只能被花費一次,所以區塊鏈中的所有交易的輸出不是被劃分成未花費交易輸出(UTXOs),就是被劃分成已花費交易輸出。支付若想有效,必須使用UTXOs作為輸入。
先忽略coinbase交易(後續會陳述),如果交易的輸出值超過輸入,這筆交易就會被拒絕——但如果輸入值超過輸出,差值就會被建立了包含該交易的區塊的礦工視作交易費用。例如,在上面的插圖中,每筆交易收到的都要比聯合輸入的少10000聰,即實際上支付了10000聰的交易費用。
工作量證明
區塊鏈是被網路中的非同步節點協作維護,所以比特幣要求每個區塊證明一定數量的有意義的工作被投入到它的建立當中,確保意圖修改過去區塊的不可靠的節點要比僅想新增新區塊到區塊鏈中的誠實節點做更多的工作。
把區塊連結到一起使得修改包含在任意區塊中的交易都不可能,除非修改該區塊後的所有區塊。因此,修改特定區塊的成本隨著區塊鏈新塊的增加而增加,放大了工作量證明的效果。
比特幣中的工作量證明利用了密碼hash的隨機性。一個好的加密hash演算法將任意資料轉換成一個偽隨機數。如果以任何方式修改資料並重新執行hash,都會產生一個新的偽隨機數,所以沒有辦法通過修改資料預測hash數。
為了證明你為建立新塊做了一些額外的工作,必須建立一個沒有超過確定值的區塊頭hash。例如,如果最大的hash值是2^256 -1,通過生成小於2^255的值,可以證明你嘗試了兩種組合。
把區塊連結到一起使得修改包含在任意區塊中的交易都不可能,除非修改該區塊後的所有區塊。因此,修改特定區塊的成本隨著區塊鏈新塊的增加而增加,放大了工作量證明的效果。
比特幣中的工作量證明利用了密碼hash的隨機性。一個好的加密hash演算法將任意資料轉換成一個偽隨機數。如果以任何方式修改資料並重新執行hash,都會產生一個新的偽隨機數,所以沒有辦法通過修改資料預測hash數。
為了證明你為建立新塊做了一些額外的工作,必須建立一個沒有超過確定值的區塊頭hash。例如,如果最大的hash值是2^256 -1,通過生成小於2^255的值,可以證明你嘗試了兩種組合。
在上面給定的例子中,平均每隔一次就會生成一個成功的hash值。甚至可以預估hash請求生成小於目標閾值的可能性。比特幣假定一個線性概率,目標閾值越低,平均需要嘗試的hash請求次數越多。
新的區塊將被新增到區塊鏈中,如果他們的hash值像共識協議預期的困難值一樣具有挑戰性。每2016塊,網路使用儲存在區塊頭中的時間戳計算這2016個區塊的第一個和最後一個的時間差。理想值是1209600秒(2周)
- 如果花費少於兩週的時間生成了2016個區塊,預期困難值將會按比例增加(如300%)以便下一個2016個區塊花費兩週時間生成,如果hash校驗速率相同的話。
- 如果花費超過兩週的時間才生成2016個區塊,同理預期困難值就會按比例降低(比如減到75%)
因為每一個區塊頭hash值必須低於目標閾值,而且每個區塊都被連結到之前一個區塊,所以傳播一個修改過的區塊需要的算力和整個比特幣網路從初始區塊創造的時間到現在所花費的算力一樣多。只要擁有大多數的網路算力,就可以發起諸如51%攻擊交易歷史(甚至低於50%的算力也有機會實現該攻擊)。
區塊頭提供了幾個易於修改的欄位,例如一個專用的nonce欄位,所以獲取新的hash值並不要求等待新的交易。而且,工作量證明只hash具有80個位元組的區塊頭,所以區塊雖然包含了大量的交易資料並不減緩hash速度,而且新增額外的交易資料僅要求重新計算merkle tree的祖先節點。
區塊高度和分叉
任何成功計算出來低於目標閾值的區塊頭的hash值的比特幣礦工都可以將整個區塊新增到區塊鏈中(假設該區塊有效)。這些區塊一般都被他們的區塊高度所標識——該區塊和第一個比特幣區塊(區塊0,即創世區塊)之間的區塊數量。例如,區塊2016是目標閾值困難度第一次被調整的位置。
多個區塊可能有相同的區塊高度,當兩個或多個礦工在大致相同的時間產生各自的區塊時,就會發生。這在區塊鏈中產生了一條明顯的分叉,如上圖所示。
當礦工在區塊鏈末同時產生區塊時,每個節點獨立選擇接受哪個區塊。如果不考慮其他,節點總會使用它們看到的第一個區塊。
最終一個礦工會產生另一個塊,它只會連線到相互競爭同時開採的區塊中(即分叉)的一個。這使得分叉的一側比另一側更強(擁有更大困難度)。假設一個分叉僅包含有效的區塊,正常的節點總是遵循最困難的鏈去重新建立和拋棄屬於較短分叉的陳舊的塊。(陳舊的塊也被稱之為孤兒或孤兒塊,但這些術語也用於沒有父區塊的真正的孤兒塊)
如果礦工以不同的目的工作,就會出現長期的分叉。比如一些礦工努力工作擴充套件區塊鏈,與此同時一些礦工試圖發起51%攻擊修改交易歷史。
因此在區塊鏈分叉期間,多個區塊可能具有相同的高度。區塊高度不應用作全域性的唯一識別符號。相反,區塊總是通過它們頭部hash被索引到。
交易資料
每個區塊必須包含一個或更多的交易。這些交易中的第一筆交易必須是coinbase(幣基)交易,也叫做生成交易。主要用於收集和花費區塊獎勵(包括區塊補貼和該區塊中所有交易的交易費)
coinbase交易的UTXO有特定的條件以至於至少100個區塊以後才能花費(作為輸入)。這臨時性的避免礦工花費了在區塊鏈分叉後,被證明是孤兒塊(因此這個coinbase交易會被銷燬)的區塊獎勵和交易費用。
區塊並不要求包含非coinbase交易,但是礦工為了獲取交易費用幾乎總會納入額外的交易。
所有的交易,包括coinbase交易,都以二進位制rawtransaction格式編碼到區塊中。
rawtransaction格式是被hash以建立交易識別符號(txid)。通過配對txid然後把它們用hash處理構成merkle tree。如果有奇數個txid,沒有配對的txid將會與自己的拷貝來做hash。
hash的結果就是互相配對然後hash到一起,任意沒有夥伴的hash將與自己副本hash。不斷重複這個過程直至只有一個hash被保留,這就是merkle root。
例如,如果交易僅被加入(沒有被hash),一個有五個交易的merkle tree如下所示:
正如在簡化支付驗證(SPV)子章節所討論的,通過從區塊頭獲取merkle root和從全節點獲取的中間hash值列表,merkle tree允許客戶端驗證交易是否被加入到區塊中。全節點不需要被信任:修改區塊頭代價很大而且中間hash值不能被修改否則驗證會失敗。
例如,為了驗證交易D被加入到區塊中,除了merkle root,SPV客戶端只需要複製C、AB和EEEE hash就行;SPV客戶端不需要知道了解任何其他交易。如果該區塊中的五個交易都是最大體積,下載整個區塊要500000個位元組——但是下載三個hash加上區塊頭只有140個位元組。
共識規則變化
為了維護共識,所有的完整節點使用相同的共識規則驗證區塊。然而,有時共識規則也要改變以便引入新的特性或阻止網路攻擊。當新的規則被實現時,可能有一段時期未升級節點遵循舊規則,新節點遵循新規則,建立兩種可能達成共識的方法:
遵循新共識規則的區塊被升級節點接受但被未升級節點拒絕。例如,一個新的交易特性被用到一個區塊中,升級節點理解該特性並接受它,但是未升級節點會因為它違反了舊規則而拒絕它。
違反了新規則的區塊被升級節點拒絕但被未升級節點接受。例如,一箇舊交易特性被用到區塊內,升級節點因為它違反新規則而拒絕它,但是未升級節點因為它遵循舊規則接受它。
第一種情況,被未升級節點拒絕,從這些未升級節點獲得區塊鏈資料的挖礦軟體拒絕和從升級節點獲取資料的挖礦軟體構建同一條鏈。這建立了永久的分歧鏈——一條針對未升級節點,一條針對升級節點——叫做硬分叉。
第二種情況,被升級節點拒絕,有可能保持區塊鏈避免永久分歧如果升級節點控制大部分算力的話。這是因為,這種情況下,未升級節點將像升級節點一樣接受所有有效和相同的區塊。所以升級節點可以構建一條鏈,同時被未升級節點接受作為最有效的區塊鏈。這叫做軟分叉。
雖然分叉實際上是區塊鏈的分歧,共識規則的改變常被視作建立一個硬或軟分叉。例如,增加區塊到1M以上需要硬分叉,此例中,一個實際的區塊鏈分叉不是必要的——但是可能出現。
共識規則變化常以不同方式被觸發。在比特幣的頭兩年,中本聰通過在立即實施新規則的客戶端上釋放向後相容的版本升級,從而實現了好幾次軟分叉。多重軟分叉如BIP30已經經由標誌日觸發,在標誌日裡新的規則在預設的時間或區塊高度被執行。這種經由標誌日觸發的分叉作為使用者觸發軟分叉(UASF)是非常出名的,因為它們是依賴於有足夠的節點在標誌日後實施新的規則。
之後,軟分叉等待多數的算力(典型的如75%或95%)示意他們實施新的共識規則的意願。一旦示意(支援新規則的人數)閾值通過,所有節點開始實施新規則。這樣的分叉叫做礦工觸發軟分叉(MASF),因為它們是依賴於礦工觸發。
檢測分叉
未升級節點在兩種分叉期間或許會使用和描述不正確的資訊,導致幾種可能會導致經濟損失的情境。尤其是未升級節點或許會傳播和接受被升級節點視作無效的交易,所以將永遠不會成為共識的最好的區塊鏈。未升級節點或許會拒絕傳播已經被新增到最好的區塊鏈上的區塊或交易,因此提供不完整的資訊。
比特幣核心包括通過關注區塊鏈工作量證明以檢測硬分叉的程式碼。如果一個未升級節點收到區塊鏈頭部顯示比它視作有效的最好的鏈多至少六個區塊工作量證明,該節點在getnetworkinfo PRC結果中提交一個警告,並執行-alertnotify命令如果設定了的話。警告操作者未升級節點無法切換到可能是最好的區塊鏈中。
全節點也校驗區塊和交易版本號。如果在最近的幾個區塊中看到的區塊或交易版本號比節點使用的版本號更高的話,節點會假設它沒有使用當前的共識規則。比特幣核心通過getnetworkinfo PRC和-alertnotify命令(如果設定了的話)報告這種情況。
在以上兩種情況下,區塊和交易資料如果來自一個很明顯不是使用當前共識規則的節點,就不應被信賴。
連線到完整節點的SPV客戶端可以通過連線到幾個完整節點,並確保這些節點在具有相同區塊高度的同一條鏈上(加上或減去幾個區塊來說明傳輸延遲和老化塊),來檢測可能的硬分叉。如果有分歧,客戶端就會從具有更弱的鏈的節點斷開。
SPV客戶端也應檢測區塊和交易版本號,確保他們使用當前的共識規則處理接收到的交易和建立新的交易。