什么是硬分叉,什么是軟分叉,什么是共識?
第0章 引言
比特幣是一套軟件,對軟件代碼進行修改升級就經常會涉及到兩個概念,一個叫硬分叉,一個叫軟分叉。這到底是什么意思呢?社區里最常見的定義說是對共識的修改。但“共識”具體指的是什么,很難理解。我們還是多實際的修改代碼層面來看下它們的定義吧。
第1章 比特幣的交易格式和交易歷史記錄
在理解什么是硬分叉和軟分叉之前,我們先要知道這兩者要修改的東西是什么。
比特幣交易在漢語中有雙重意思,這是硬分叉和軟分叉經常被誤解的一個關鍵。
比特幣交易其中一個含義指的是我們發送比特幣統一使用的數據結構,這是一套規則,我們所有人發送比特幣,不論你使用什么錢包軟件都得遵守這一套規則。它被定義成一個數據結構,其中的每一個字段代表著什么意思。所有錢包軟件都能理解這個意思。
比特幣交易的另一個含義是指使用比特幣的人相互之間發送比特幣的事件。就比如我發了0.12345BTC給你,這是一次交易。所有這些發送比特幣的事件,構成了比特幣交易歷史記錄,這就是目前組成比特幣完整節點的80多G的數據的主要內容。所有這些記錄都可以在區塊鏈瀏覽器上可以查的到,你只要在區塊鏈瀏覽器上鍵入你的比特幣地址就可以查到你所有的交易記錄。
做個比喻來說明。交易格式就類似于我們去銀行給別人匯款填寫的那張匯款單,匯款單上明確定義了匯款人的姓名、身份證、手機號、賬號、金額、收款人姓名、賬號……等信息。比特幣的交易格式也規定了匯款人的信息和收款人的信息,不過這些信息與人的身份信息無關,比特幣交易規定的要填寫的具體信息請看下一章。
第2章 比特幣的交易格式和區塊格式
比特幣的交易歷史記錄這個概念大家都容易懂,就不展開說了,也沒啥好說的。但比特幣的交易格式,也就是交易的數據結構需要重點解釋,否則無法理解什么叫硬分叉和軟分叉。
一筆比特幣交易是一個含有輸入值和輸出值的數據結構,該數據結構植入了將一筆資金從初始點(輸入值)轉移至目標地址(輸出值)的代碼信息。這個數據結構包含一些字段,我們詳細來看下這些字段,摘自《精通比特幣》第5章。
所有的比特幣錢包的都得按照這個表格規定的方式來發送比特幣,否則在比特幣網絡上就不會被識別,也就是無效的交易。這就像是你去銀行匯款,你不按照匯款單填寫,銀行就不接受一樣。
比特幣網絡會在平均10分鐘內,收集各個節點發出的交易然后打成一個包,叫區塊,蓋上時間戳,然后添加到區塊鏈上。區塊是什么呢?
區塊就是聚合了交易信息(也就是平均十分鐘內網絡上各個節點發出的比特幣交易)的容器數據結構。它由一個包含元數據的區塊頭和緊跟其后的構成區塊主體的一長串交易組成。區塊的數據結構被定義成下面這個樣子。
所有的挖礦節點錢包都得按照這個表格規定的方式來打包區塊,否則在比特幣網絡上就不會被別的完整節點識別(SPV節點不算),也就是會被認為是無效的交易。
第3章 硬分叉和軟分叉的“官方”定義
硬分叉和軟分叉在bitcoin.org上都有定義,但定義其實很模糊,為了防止我胡說(質疑bitcoin.org上的定義,確實讓我誠惶誠恐。),我摘錄原文:
硬分叉被定義成這樣:
A permanent divergence in the the block chain, commonly occurs when non-upgraded nodes can’t validate blocks created by upgraded nodes that follow newer consensus rules.
https://bitcoin.org/en/glossary/hard-fork
區塊鏈發生永久性分歧,在新共識規則發布后,部分沒有升級的節點無法驗證已經升級的節點生產的區塊,通常硬分叉就會發生。
軟分叉的定義是這樣的:
A temporary fork in the block chain which commonly occurs when miners using non-upgraded nodes violate a new consensus rule their nodes don’t know about.
https://bitcoin.org/en/glossary/soft-fork
當新共識規則發布后,沒有升級的節點會因為不知道新共識規則下,而生產不合法的區塊,就會產生臨時性分叉。
社區對區塊擴容是該執行硬分叉還是軟分叉的爭議,我猜bitcoin.org上的這兩個模糊的定義應該負主要責任。最大的責任是原文中的“consensus rules(共識規則)”定義不清晰,如果我們在去bitcoin.org上尋找“consensus rules(共識規則)”是什么意思,原文是這樣的:
The block validation rules that full nodes follow to stay in consensus with other nodes.
https://bitcoin.org/en/glossary/consensus-rules
完整節點為了和其他節點維持共識而遵循的區塊驗證規則。
這個對共識的定義,在邏輯上就犯了循環認證的謬誤,這個定義就是說“共識就是為了維持共識的規則”。這和許三多說的話是一樣的:“有意義就是好好活。”“那什么是好好活呢?”“好好活就是做有意義的事情。”其實他根本就沒有回答什么是好好活,以及什么是有意義。
另外這兩個定義中把硬分叉定義為“permanent divergence(永久性分叉)”,和把軟分叉定義為“temporary fork(臨時性分叉)”,這種幾乎是文字游戲的定義法,讓社區很多人害怕硬分叉。認為硬分叉是一個非常可怕的事。
講真,這篇文章在我腦海里大綱成文已久,但我一直不敢寫,因為我懷疑的可是圣地bitcoin.org上的定義啊。我苦苦閱讀bitcoin.org上對比特幣協議升級的定義,終究沒能找到讓我釋疑的描述。我問過國內許多大V,也沒有得到一個完整的答案。最幾天社區的軟分叉愛好者發了大量的文章來宣傳軟分叉,呼吁抵制硬分叉,我閱讀過后,我推理過后,在我的知識體系內,我確信他們是錯的。他們錯誤的根源都來自對“共識”的理解是一種虛幻式的描述,根本就不知道具體指的是什么,好像只要提到了“共識”,就是神圣不可侵犯的一樣,所有的邏輯和推理到了“共識”就停止了。
既然對軟分叉和硬分叉沒有一個抽象的定義,那我們先從它們到底干了什么事來分析吧。
第4章 比特幣區塊鏈上執行軟分叉升級的具體案例
比特幣區塊鏈上最近在幾個月前就執行過一次軟分叉升級,使得那是BIP68/112/113正式在比特幣協議里生效,被社區稱為CSV軟分叉。大約在2016年6月份升級完成的。這個軟分叉到底改變了比特幣代碼的哪里呢?要回答這個問題,我們需要從比特幣交易數據結構上去尋找答案。
比特幣交易數據結構在CSV軟分叉前后發生了什么變化?
圖中的紅字加粗的那個字段就是CSV軟分叉主要修改的地方。在CSV軟分叉之前,這個字段是“序列號(目前未被使用的交易替換功能)”如下圖:
正是因為原來比特幣交易數據結構中這一個字段未被使用,或者當時是模糊定義,所以才可以被使用重新定義。這種未明確定義的字段在舊版本的比特幣完整節點上就不會被仔細驗證,新版本的節點按照定義過的規則生產新區塊,還可以被舊版本的節點驗證接受。這就是軟分叉的具體過程。
但這顯然不是長遠之計,因為這種字段就這么一個,你用了一次,哪以后就沒有了。
比特幣在2012年還有一次重要的升級,叫P2SH,被社區稱之為多重簽名軟分叉。這是對比特幣交易簽名腳本的一次修改,使得比特幣可以很方便的通過多重簽名的方式來發送交易。那一次修改了什么呢?
圖中的紅字加粗的那個字段就是多重簽名軟分叉主要修改的地方。一開始中本聰定義這個字段是叫P2PKH(Pay-to-Public-Key-Hash,支付給公鑰),目前這個字段可以有5種腳本。
因為多重簽名軟分叉之后,沒有升級的節點在驗證已經升級過的節點產生的區塊時,它們對這種新的P2SH鎖定腳本也可以通過驗證,所以這也是一個軟分叉。
以上兩個軟分叉都是對比特幣交易數據結構的修改。軟分叉還能變相修改比特幣的交易歷史。
在2010年8月15日,有一個黑客利用比特幣代碼的一個漏洞,在第74638高度區塊上刷出一筆交易包含了1844億個比特幣。隨后在半天時間內被開發人員發現,并且發布了補丁,將這筆交易的輸出變為無效。但這并不是簡單地將這筆交易本身定義為無效,而將一類叫“負值輸出”的交易定義為無效,剛才那筆交易就是利用比特幣之前沒有禁止輸出值為負值這個漏洞,只是定義了只要輸出總金額不能高于輸入總金額就可以了。在補丁(應該是中本聰發布的)修正了這個漏洞,具體修改的是什么呢?
圖中的紅字加粗的那個字段就是這個漏洞補丁主要修改的地方。修改之前的規則是“總量”不能高于“交易”,修改之后的規則是添加了“總量”不能是負值。這種修改未升級的節點能夠驗證已經升級的節點產生的區塊,所以也是軟分叉。
現在我們對這三個案例進行抽象化,給出一個軟分叉的定義:軟分叉是指比特幣交易的數據結構(這就是被廣泛流傳的“共識”)發生改變時,未升級的節點可以驗證已經升級的節點生產出的區塊,而且已經升級的節點也可以驗證未升級的節點生產出的區塊。
軟分叉對“共識”的修改肯定還包括對區塊格式的修改,但這里的三個案例均是對交易格式的修改。
下面我們來看硬分叉案例。
第5章 比特幣區塊鏈上執行硬分叉的具體案例
在2013年3月12日,當時是bitcoin qt 0.8.0版本軟件發布了,0.8版本采用了一種新的數據庫level db。有的礦工節點升級了bitcoin qt 0.8版本,有的礦工還繼續使用bitcoin qt0.7版本的軟件。雙方各自生產區塊,但bitcoin qt 0.8采用的新數據庫生產出的區塊被被qt0.7版本節點拒絕掉。具體的原因是舊的數據庫對超過800Kb的區塊有時不接受。因此在區塊高度225430比特幣區塊鏈分成了兩條鏈,結果導致了比特幣區塊鏈產生兩條鏈,一條是包含大于800kb區塊的鏈,另一條是拒絕承認這些包含更大區塊的鏈,這就發生了硬分叉。
當時是采用bitcoin qt 0.8版本的礦工放棄了他們挖的鏈,退回到bitcoin qt 0.7版本上繼續挖礦。
這次硬分叉是一次意外,是bitcoin qt 0.8版本的軟件出了bug,導致采用舊軟件的節點拒絕驗證新軟件節點生產的區塊。但硬分叉的成因就是采用舊軟件版本的節點拒絕驗證采用新軟件版本的節點生產的區塊,然后雙方各自挖礦。
在2015年7月4日比特幣區塊鏈在區塊高度363731發生一次硬分叉。當時是Bitcoin Core 開發者往新版本的Bitcoin Core 0.10.0添加了BIP 66。這本來是一起軟分叉的修改,在比特幣網絡上主要礦池都使用了0.10版本的軟件時,但有一個礦池BTC Nuggets沒有升級,導致BTC Nuggets挖出來的兩個區塊其他礦工拒絕掉,然后雙方就各自挖礦延續自己認為是正確的區塊鏈,由此產生硬分叉,分成了兩條鏈。
隨后bitcoin.org發布公告,呼吁礦工升級到bitcoin core 0.10.2版本來消滅分叉。
這也是一次意外,硬分叉的成因是采用新軟件版本的節點拒絕驗證采用舊軟件版本的節點生產的區塊,然后雙方各自挖礦。
到目前為止這兩次硬分叉都是意外,但硬分叉的成因前一個是因為對新產生的區塊格式在不同節點上產生分歧,后一個是因為對交易格式在不同節點上產生分歧。但因為是意外,社區沒有討論出足夠多的資料,我也搞不清楚具體是區塊或交易的哪個字段被修改而導致的分叉。
到了這里,我們可以對這兩個案例進行抽象化,給出一個硬分叉的定義:硬分叉是指比特幣區塊格式或交易格式(這就是廣泛流傳的“共識”)發生改變時,未升級的節點拒絕驗證已經升級的節點生產出的區塊,不過已經升級的節點可以驗證未升級節點生產出的區塊,然后大家各自延續自己認為正確的鏈,所以分成兩條鏈。
下面我們來看社區正在策劃,但還沒被激活的軟分叉和硬分叉。
第6章 正在策劃的區塊擴容硬分叉
目前比特幣社區正在策劃一次硬分叉,來由是目前的區塊被塞滿了交易,為了能在十分鐘一個區塊里容納更多的交易,就需要對區塊的數據結構做修改。那我們先再來看下區塊的數據結構。
目前比特幣網絡主要的完整節點軟件是bitcoin core 0.12,這個軟件規定表6里的“區塊大小”這個字段最大值為1M。這就導致最后一個字段“交易”能夠容納的比特幣交易數據有限,一筆交易至少是250字節,1Mb只能裝下4000多筆交易,平均每秒最多只能處理7筆交易。而因為實際的交易往往會達到500字節的大小,實際上平均每秒往往只能容納3筆交易。
所以有人就提出將這個字段的最大值調高,比如Bitcoin Classic這個軟件就將這個字段的最大值調到2M,并且以后有計劃取前2016個區塊大小的中位數再乘一個約定好的倍數來決定下一批區塊的大小上限。而Bitcoin XT則將這個值修改為20M,并且每兩年翻一倍,直到上限值達到8.3G。而Bitcoin Unlimited則直接將這個字段修改為由礦池決定自己打包多大。
問題是,使用這些修改了這個字段的軟件的節點生產出的區塊就會和沒有升級的節點不兼容,沒升級的會拒絕驗證這些新節點生產的區塊。這就會導致硬分叉。
到這里,我們基本知道了,所謂的硬分叉需要修改的“共識”就是指的是修改區塊數據結構格式,或修改交易數據結構格式。
第7章 隔離見證軟分叉
還有一個對區塊變相擴容的方案是隔離見證,這個方案修改的是比特幣交易數據結構,我們還是來看圖。
圖中紅色的字段就是隔離見證主要動手的地方,這是要將這部分數據移出交易定義的數據結構,并且還要移出區塊定義的數據結構。因此交易就少了一部分數據,這樣每一筆交易的體積就會小,而整個區塊1M空間內就能夠容納更多的交易。
但這樣移走這個字段,也會造成沒有升級的節點的拒絕驗證這些升級的節點生產的區塊。但是有辦法讓他們不拒絕,但代碼寫起來就非常非常復雜,反正這個代碼寫了一年多了,到現在還沒有搞出來。代碼越復雜,勢必潛在的漏洞就越多,想想Bip 66都由軟分叉導致了硬分叉,而這個隔離見證更復雜。
到這里,再一次驗證了,所謂的軟分叉需要修改的“共識”就是指修改區塊數據結構格式,或修改交易數據結構格式。
第8章 軟分叉和硬分叉要修改的“共識”都是對數據結構的修改
現在我們可以對軟分叉和硬分叉到底要做什么工作做個總結,特別是軟分叉和硬分叉之間到底有什么區別,以免得被那些定義不準確的“共識”弄暈了。
軟分叉修改的“共識”具體是指修改了比特幣交易數據結構,或修改了比特幣區塊數據結構。
硬分叉修改的“共識”具體是指修改了比特幣交易數據結構,或修改了比特幣區塊數據結構。
在具體修改的對象層面上,軟分叉和硬分叉是完全沒有區別的,就目前止我們看到的所有的已經發生的,和計劃發生的分叉都是這樣子,它們都修改或試圖修改交易數據結構,或區塊數據結構。
目前發生的,或計劃發生的硬分叉和軟分叉都絕對不會去修改交易歷史記錄。哪怕是2010年8月15日發生的刷出天量幣的漏洞,那一次修改的也是交易數據結構,但附帶的作用就是將一個區塊里的交易作廢了,因為那筆交易在新交易數據結構的定義下是非法的。
所以說軟分叉和硬分叉在修改“共識”層面上本質上是沒有區別的。
那軟分叉和硬分叉的區別在哪呢?
第9章 軟分叉和硬分叉主要區別是新舊節點相互兼容性
區別是對新舊節點的兼容方面。軟分叉修改數據結構后,新節點生產的交易和區塊能夠被舊節點驗證并接受,硬分叉就不能。
正因為硬分叉修改數據結構后,新節點生產的區塊會被舊節點拒絕掉,如果舊節點拒絕升級軟件而堅持按照舊數據結構繼續挖礦,那比特幣就會產生兩條鏈。
而軟分叉因為沒有升級的節點能接受新節點生產的新數據,所以不會出現兩條鏈。但軟分叉為了做到新節點和舊節點生產的數據完全兼容,那是非常難的,因為本質上是不一樣的數據結構大家要相互認,一旦出現有節點拒絕驗證不一樣的交易或區塊,那就會變成硬分叉。這也就是Bip 66軟分叉最終變成硬分叉的原因。
從第4章我們看到具體的軟分叉的修改辦法是將原來定義好的字段進行重新定義,如多重簽名軟分叉。或者是對原本是留在的字段做定義,如CSV軟分叉。
但是現在從交易的數據結構來看,所有的字段都已經被占用,并且準確而詳細地定義了,如果你還要再做軟分叉,那就只能將部分字段原有的定義擦除掉,再重新定義。這樣就會導致原有的功能可能會喪失,如果這個功能是不可或缺的,那就會導致硬分叉,新舊節點相互拒絕。所以軟分叉要特別小心。這也就是隔離見證的做法。隔離見證是直接將某個字段刪除掉,但為了保證新舊節點之間的相互兼容,那個是廢了牛勁了。
便硬分叉則不考慮沒有升級的節點會不會拒絕已經升級的節點生產的數據和代碼的情況,情況要簡單許多。
讓我們來看看這種為了實現軟分叉和硬分叉要做的兼容性有什么區別。
軟分叉和硬分叉要實現新舊節點生產的新舊數據涉及到的主要變化量一共有六個:
1.沒有升級的舊節點;
2.升級了的新節點;
3.舊節點發生的舊交易格式的交易;
4.新節點發生的新交易格式的交易;
5.舊節點生產出的只含舊區塊格式的舊區塊;
6.新節點生產出的含新區塊格式的新區塊。
實際上還要考慮更復雜的其他因素,包括完整節點;SPV節點;新舊節點算力占比;SPV挖礦節點;未確認交易和多個確認交易;CSV交易;RBF交易……。所有這些都是兼容性要考慮的因素。但為了簡化,我這里只考慮上面6個主要變化量,我們先來列個表。
軟分叉需要保證完美的兼容性就要求達到以下兩種情況:
1.升級過的節點接受沒有升級的節點生產的交易和區塊(向后兼容);
2.沒有升級的節點接受升級過的節點生產的交易和區塊(向前兼容)。
硬分叉需要保證的兼容性只需要達到上面的第1種情況,也就是新節點需要從0高度區塊開始驗證整個區塊鏈就可以了。
我們打個比方來說明這兩種兼容性吧:
向后兼容其實是我們最好理解的,就是我們使用word 2013可以打開word 2010版的文件。向前兼容的意思是舊版本軟件要無條件接受自己所不能理解的新版本軟件生產的數據。就像是你用word 2010去打開word2013版的文件。這種難度不知道有多高啊,舊版軟件怎么會知道還沒有定義過的數據呢?它唯一能做的就是忽略這些新功能。
而硬分叉是不考慮這么復雜的兼容性的,首先是拒絕向前兼容。硬分叉就不會理會沒有升級的節點拒絕驗證升級過的節點生產的區塊這會事,你受驗證不驗證,你不升級我就不跟你玩了。
第10章 對比軟分叉和硬分叉的優缺點
軟分叉可以保證不想升級的人不去升級,這種不想升級的需求在現實生活中其實是很常見的。
硬分叉必須要求所有舊節點進行升級,否則舊節點就無法識別新節點生產的交易和區塊,導致區塊鏈分成兩條鏈。
軟分叉的升級空間有限,因為目前的比特幣交易數據結構和區塊數據結構所有字段都已經詳細定義好了,你想保證向前兼容,就不可能增加新的字段,否則舊節點就會拒絕你。所以軟分叉的升級空間補束縛在對現有字段的重新定義。就包括軟分叉就無法重新定義區塊數據結構里的“區塊大小”這個字段,也就是軟分叉永遠實現不了對1M區塊的突破。而且這種極端復雜的兼容性稍微出點錯,就會新舊節點不兼容,即導致硬分叉。這個事情已經發生過一次了。
硬分叉的升級空間則要大很多,因為硬分叉只要考慮能夠接受以前舊節點生產的交易和區塊就可以了,硬分叉不需要考慮舊節點是否會接受新節點生產的交易和區塊。那硬分叉就可以對交易數據結構和區塊數據結構更大膽的修改。
最后,比特幣交易數據結構和區塊數據結構都有一個字段叫“版本號”,意義是“明確這筆交易或區塊參照的規則”。這意味著中本聰是希望使用硬分叉來修改這些規則,就是說如果我們要修改規則,那就重新定義版本號。但軟分叉在不修改“版本號”的前提下,卻修改了規則。
第11章 結束語
“共識”這個詞忽悠了太多的人了。其實在“不破壞共識”包裝下的軟分叉,本質上和硬分叉是一樣要修改相同的對象的,而且更可怕的代價是向前兼容,即要求不升級的軟件去忽視升級過的軟件生產的數據和代碼,冒這種風險得來的好處僅僅是懶得升級軟件。我想任何理智的人都是不愿意接受這種風險收益比的。