
原文鏈接:
原文鏈接:
https://bitcoinops.org/en/topics/soft-fork-activation/
原文鏈接:歷史
歷史
歷史
二級標題
二級標題
二級標題
[2009] 硬編碼高度:共識層nLockTime 啟用
這個已知最早的軟分叉在Bitcoin 軟件0.1.6 版本中實現(發佈於2009 年11 月),硬編碼在區塊高度31000 處激活,實際發生時間是2009 年11 月22 日。在大部分開發工作都是由中本聰完成時,這種硬編碼激活高度的方法至少還用在了另一個早期的軟分叉中。
[2011] 硬編碼時間和手動幹預:BIP12OP_EVAL 失敗
在中本聰離開比特幣之後,合併到比特幣的第一個軟分叉代碼是BIP12OP_EVAL。本來計劃是使用一個硬編碼時間和在支持變更的算力佔比少於50% 時手動幹預的方法。引自BIP12:
[...] 新的客戶端和礦工將解釋OP_EVAL 為一個no-op,直至2012 年2 月1 日。在此之前,支持的礦工可以將“OP_EVAL” 字樣寫在自己生產的區塊裡面,方便我們計算支持的算力佔比。如果在2012 年1 月15 日之前沒有超過50% 的算力支持這一變更,激活將會推遲,直到有超過50% 的算力支持OP_EVAL(如果顯然大部分算力都不會激活這一升級,則升級會被取消)。
手動幹預可能是有必要的,因為OP_EVAL 在激活代碼合併之後、推出之前,被發現有一個嚴重的漏洞。雖然這個bug 被修復了,一些開發者擔心這個強大的新操作碼可能會有其它問題,所以人們就放棄了這次軟分叉。
[2012] 再次嘗試硬編碼時間以及手動幹預:BIP16 P2SH
人們提出了多個替代OP_EVAL 的簡化提案(見BIP13/16、17、18 和19,還有其它想法)。而BIP13/16 支付給腳本哈希值(P2SH)獲得了大部分開發者的支持。 P2SH使用了跟OP_EVAL 一樣的激活機制。最初計劃的激活時間是2012 年3 月1 日,但到了2 月15 開票日,在最後100 個區塊中,只有不到50% 的礦工表示他們會在3 月之前執行BIP16 規則。這導致了一個“相當長的替代鏈”(鏈分裂),因為一些仍然在3 月1 日實行BIP16 的礦工拒絕了來自多數礦工(不實行新規則)的區塊。第二次開票日是在幾千個區塊之後,3 月15 日;這一次它獲得了足夠多的支持。所以開發者在3 月30 放出了Bitcoin 0.6.0,將激活時間設在了4 月1 日。
[2012] 硬編碼時間:BIP30 拒絕複製txid
P2SH 的激活完成後,人們發現可能出現多個交易共用同一個txid 的情況。就其自身而言,這個bug 只會導致嘗試利用這個bug 的用戶的資金被銷毀,但它也可以結合比特幣的默克爾樹構建中的一些奇怪的行為打破節點間的共識(見CVE- 2012 -2459)。第一個修復這個漏洞的軟分叉是BIP30,它簡單將使用同一個txid 的後發交易標記為無效交易,如果前發交易還有沒花費的輸出的話。這個修復在開發團隊中沒有爭議,因此在包含P2SH 激活參數的Bitcoin 0.6.0 中以硬編碼時間的方式激活。
[2012] IsSuperMajority (ISM):BIP34 coinbase 前綴
雖然BIP30 修復了txid 重合導致的短期問題,比特幣開發者知道這只是權宜之計,軟件沒理由每次收到一筆新交易都要搜索帶有未花費輸出的所有交易的索引。所以第二個解決方案開始提上日程,旨在消除讓txid 複製變成實用攻擊向量的弱點。這就是BIP34。對這一次更新,開發者使用了類似於BIP16 P2SH 的礦工投票方法,但這一次,準備好支持EIP34 的礦工需要增加他們的區塊的nVersion 的數值。更重要的是,開發者自動化了比特幣代碼中新規則的實行,因此他們可以在等待礦工升級期間發布支持軟分叉的軟件。這個來自BIP34 的規則用一個叫做IsSUperMajority() 的函數實現了。最開始它包含了一個單項的激活閾值,達到了便開始實行BIP34 的新共識規則:
75% 規則:如果最新的1000 個區塊中有75% 是vision2 或者更大的,就開始拒絕無效的vision 2 區塊
在這個功能的開發期間,人們決定加入第二項激活閾值,決定性地修復使用BIP34 所要解決的問題:
95% 規則:如果最新的1000 個區塊中有950 個都是vision2 乃至更大的,就拒絕所有vision 1 區塊
拒絕舊版本區塊這個規則的一個已知問題是,除非所有礦工都已經升級,每天都可能有幾個無效區塊產生(如果恰好是95% 的礦工激活,每個區塊都有5% 的機率是無效的)。已經升級並執行ISM 規則的節點會拒絕這些區塊,但更老的節點和輕客戶端不知道這個規則,所以會接受這些區塊。這會讓網絡比普通情形更加依賴於不在無效塊後面繼續挖礦的礦工。
[2015] ISM 以及無驗證挖礦:BIP66 嚴格DER 激活
在2014 年9 月,Pieter Wuille 發現OpenSSL 在處理不同平台的DER 編碼簽名時存在分歧。這個可以被利用來,比如說,創建一個在Linux 操作系統上可以通過驗證但在windows 操作系統上會失敗的區塊—— 攻擊者定點創造鏈分裂。 Wuille 和其他幾位開發者秘密開發了補丁,並致力於以軟分叉激活,保證所有簽名都使用同樣的格式。 BIP66 就是為這件事創建的,在公開宣傳中,是作為移除比特幣對OpenSSL 依賴的一步(這個目標是真實的,最終在2019 年得以實現)。在BIP66 獲得用戶和開發者充分多的支持(許多人甚至不知道這個安全漏洞存在)之後,它使用與BIP34 相同的ISM 激活機制,將區塊版本號遞增為v3,並要求達到95% 的閾值後就拒絕v2 和更低版本號的區塊。
75% 的閾值在2015 年7 月4 日達到,而95% 閾值在區塊高度363725 處達成,所有的節點都運行Bitcoin Core v0.10.0 乃至更高版本的軟件(或者兼容的實現),開始實行新規則。不過,在區塊高度363731 處,一個沒升級的礦工生產了一個沒包含當前版本號的區塊,在新的ISM 激活規則下不是有效區塊。但其他礦工都在這個無效區塊後面繼續生產,最終產生了一條帶有6 個無效區塊的鏈。這意味著未升級的節點和許多輕客戶端都會將第一個無效區塊中的96 筆交易當成積累了6 個區塊確認的交易,即使它們在當時還沒獲得過哪怕一個有效區塊的確認。最終,開發者只能聯繫礦池運營者,讓他們手動重啟軟件並回到有效的鏈上。這樣的事件在第二天又重演了一次,使一些交易獲得了三次無效的確認。幸運的是,這六個和三個區塊中的所有常規交易,後來都打包到了有效區塊內,意味著普通用戶沒有損失。
最初位於363731 高度的無效區塊就是僅僅因為使用舊的版本號而變成無效的、預計每天都有可能出現的約5% 區塊之一。而下一個區塊是未升級礦工挖出的概率也是5%,所以連續兩個區塊都是版本號取消區塊的概率是0.25%。給定95% 的礦工都已升級,連續6 個區塊都是版本號無效區塊的概率是0.000002% —— 但罪魁禍首還不是極端壞運氣。沒有考慮到的是礦工可能會做“無驗證挖礦”,也就是礦工在收到一個新區塊之後,不加驗證,直接在後面繼續生產,這樣可以提高一點效率。雖然無驗證挖礦軟件理論上很容易就能處理無效區塊版本號,這個功能在當時挖掘那五個區塊的礦工所用的軟件中還沒有實現。最終,足夠多的礦工升級了他們的無驗證挖礦軟件,或者升級了他們的節點,而BIP66 激活相關的意外鏈分裂就此絕跡。
為了應對這些導致2015 年7 月出現分叉的問題,開發者加倍努力減少對無驗證驗證挖礦的需求,成果如BIP152 壓縮區塊的中繼以及FIBRE 軟件。開發者也開始思考一種更好的激活機制,也就是後面會提到的BIP9 協議。
[2015] 最後一次ISM:BIP65OP_CHECKLOCKTIMEVERIFY 激活
BIP66 嚴格DER 軟分叉之前,就有人提出要用軟分叉為比特幣增加一個新的操作碼OP_CHECKLOCKTIMEVERIFY (CLTV),但因為修復OpenSSL 漏洞而推遲了。這就體現了ISM 機制使用遞增版本號的另一個弱點—— 一個礦工如果發出信號支持最新的提議(vision n)也就隱含地表示了支持之前所有的提議(如vision n-1)。這就限制了使用ISM 同時協調多個升級的能力。
不過,儘管BIP 66 激活時出了一些問題,ISM 被再一次用到了推遲的BIP65 的激活中。這一次就沒有再出問題了。
[2016] BIP9 versionbits:BIP68/112/113 相對鎖定時間激活
BIP9 提出了一種新的激活機制來解決ISM 的幾個問題:
沒必要地懲罰礦工:ISM 激活會導致區塊版本號遞增,沒有遞增版本號的礦工所生產的區塊就會被當成無效的,即使這個區塊並沒有違反軟分叉的其它規則。舉個例子,在2015 年7 月4 日的鏈分裂中,所有的交易都遵守軟分叉規則—— 這些礦工損失50 萬美元的唯一理由就是升級要求區塊頭裡應該包含一個3 而沒升級的礦工使用了2 。
很難並行化:使用ISM,即使開發者認為有必要,也必須等待一個分叉結束,另一個分叉才能開始收集信號。
不允許失敗:ISM 不設過期時間。等待激活信號的節點軟件一旦放出,運行了新軟件的節點就會一直監控信號,直到激活完成。沒有辦法確定人們是不是完全不需要這個軟分叉。
不可預期的激活時間:無法提前知道確切的激活時間,意味著協議開發者、商戶系統管理員以及礦池運營者,都很難在激活之後短時間內立即投入使用,即使出現了需要快速反應的問題。
BIP9 versionbits 嘗試解決這些問題。它將區塊頭內的vision 字段用作bit 字段。這個字段裡面的數據只用來表示信號—— 不會被當成無效區塊的依據—— 並且可以並行地設置。測量每2016 個區塊運行一次,以壓縮某一小部分算力足夠幸運便能冒充95% 支持的可能性。最後,當達到了95% 的信號門檻,激活之前會有額外的2016 個區塊(約兩週)的“鎖定期”,以便各方准備升級。如果過期時間之前未能達到激活的門檻,整個軟分叉的嘗試就結束,沒有用上的代碼可以在後來的軟件版本中刪除。
這個激活方法第一次使用是在BIP68 共識強制的序列號、BIP112OP_CHECKSEQUENCEVERIFY 以及BIP113 中位時間定義的nLockTime 的軟分叉中。這個分叉很快進入了鎖定階段,然後自動進入了激活階段。
[2016-7] BIP9、BIP148 以及BIP91:BIP141/143 隔離見證激活
隔離見證軟分叉是用BIP9 激活參數發布的。少數礦工很快地表示了支持,但支持率遠低於95% 的門檻。一些比特幣用戶認為礦工是在不合理地拖延一個有用的新特性,所以開發出了自願的激活措施,就是BIP148。 BIP148 的最終形式指定,從某個日期開始,拒絕一切不支持segwit 的區塊,
實現BIP148 的軟件出現後,網絡中就有了三類節點—— 不升級的節點,BIP9/141 節點,以及BIP148/141 節點—— 陷入共識錯誤的機率更大了。如果礦工沒有支持隔離見證,而大部分用戶都繼續把這些區塊當成有效的,BIP148 的用戶可能就會收到在其他用戶看來無效的比特幣。此外,如果大部分用戶都支持BIP148,但礦工繼續生產許多在BIP148 看來無效的區塊,那些不實行BIP148 的用戶就會接受BIP148 用戶認為無效的比特幣。只有用戶都遵守同樣的規則,且大部分算力都支持BIP148 規則,升級才是安全的。
一種降低風險的辦法是,給出足夠的時間,讓用戶可以升級到強制激活隔離見證的節點,但BIP148 無法做到這一點,因為它的目標是觸發現有的BIP9 流程,也就意味著,它要在BIP9 到期日很久以前就強迫礦工發信號表示支持。作為BIP148 可能不得人心的替代方案,BIP149 提議給用戶多一年的時間來升級。 BIP149 從未獲得足夠多的公開支持,但它是第一個使用BIP8 的提案,而BIP8 在未來幾年裡引發了更多的討論。
在BIP148 開始獲得重大的公開支持時,多個礦工、交易所和業界人士表示支持一個兩步驟的提議,在激活隔離見證的同時會與支持BIP148 的節點保持共識。第一個步驟寫在BIP91 中,它改進了BIP9 的規則。礦工可以使用BIP9 的位字段來表示他們是否會實行一個暫時的規則:拒絕一切不發信號支持BIP141/143 隔離見證的區塊。與BIP9 不同,BIP91 的閾值從95% 降到了80%,而其監控和鎖定期的長度從2016 個區塊降低到了336 個區塊。
BIP91 鎖定並且激活了。隨後,BIP141/143 鎖定並激活。在它們鎖定時,BIP148 的強制支持措施過期。
這個來自礦工、交易所和業界人士的提議的第二個階段需要一個硬分叉,在遭到大量個人用戶和企業的激烈反對之後,提案的簽名人撤回了這個提議。
至今,人們仍然在爭論,這些事件以及同期發生的其他事件,到底為隔離見證激活造成了多大的影響。
緊急激活
不止一次,人們在共識代碼中發現了嚴重的漏洞,開發者沒有經過激活的流程就放出了補丁。這樣做可能導致共識失敗,但也為升級的節點立即消除了漏洞。重大的事件包括:
使用chainwork 來替換高度(2010 年7 月):比特幣一開始認定最多區塊的鏈(“最長鏈”)為有效的鏈。如果每個區塊都有同樣的難度,那這樣的最長鏈同時也會是積累了最多工作量證明的鏈。但是不同的區塊有不一樣的難度,所以chainwork 軟分叉在Bitcoin 0.3.3 中放出,將累積最多工作量證明的鏈視為有效鏈。
消除繞過腳本的bug(2010 年7 月):比特幣一開始將花費UTXO 的腳本(scriptSig)和保護UTXO 的腳本(scriptPubKey)結合起來、同時求值。這種設計使得人們可以在鎖定機制計算之前就終止腳本,以成功狀態退出,例如,在運行OP_CHECKSIG 以檢查簽名之前就終止腳本。這個bug 最初被報告為使用OP_TRUE OP_RETURN 的scriptSig 可以花費任何人的比特幣。這個漏洞在Bitcoin 0.3.6 中第一次修復,辦法是讓OP_RETURN 必定以失敗收場,而且為腳本的其它顯示安排了數字。雖然所有這些變更都是軟分叉,但相同代碼的修改(後來移除某些限制)也會造成硬分叉式的更改。即使是這麼重大的變更,scriptSig 可以篡改scriptPubKeys 運行的底層問題仍然存在,所以第二次軟分叉在Bitcoin 0.3.8 中實現,它讓兩者獨立執行。
修復溢出漏洞(2010 年8 月):某人創建了一筆交易來花費0.5 btc 並創建了兩個價值92,233,720,368.54277039 BTC 的輸出。比特幣的確要求輸出的數值不能大於輸入的數值,但檢測方法是把輸出的數值加入到一個最多能表示9,223,372,036,854,776 聰(約9200 萬btc)的64 位整數中,這個整數溢出後就會從-9,223,372,036,854,776聰開始。這就意味著,這個交易似乎只花費了總計-0.1 btc。這還繞過了另一條規則,就是禁止單個為負的輸出,但是不禁止總計為負的數值—— 因為它假設了任何正值的總和都仍會是正的。這使得某人創造出了1840 億btc,而且這樣的把戲可以不斷重複,沒有任何代價,產生無數的比特幣。幾個小時內,Bitcoin 0.3.10 放出了一個軟分叉補丁,限制輸出為2100 萬btc。它還要求放棄帶有溢出交易的鏈—— 這是有意製造的共識失敗,但為了比特幣仍然有意義就必須這麼做。
臨時修復BDB 鎖定問題(2013 年3 月):2012 年初,比特幣開發者意識到,如果同時對UTXO 數據庫(鏈狀態)做太多更改,可能會超出鏈狀態數據的默認容量限制之一。因為當時的比特幣區塊比較小,只有在區塊鏈重組、需要同時處理來自多個區塊的交易時才會觀察到這個情形。當時人們實現了一個簡單的解決方案:在重組期間,一次只處理來自一個區塊的交易。後來,一些人開始請求礦工把可選的默認區塊大小從250 KB 提高。在2013 年3 月12 日,某個礦工生產了一個約1 MB 的區塊,包含了超過1700 筆交易—— 也是截至當時最大的比特幣區塊—— 在許多節點上都超過了數據庫的容量,導致它們認為這個區塊時無效的,即使它完全符合比特幣的明示的共識規則。把水攪得更渾的是,一個新版Bitcoin Core 已經發布,它用上了不一樣的數據庫引擎,沒有這種限制,因此也能安然地接收這個更大的區塊—— 所以不同版本的節點之間出現了共識錯誤。在快速分析了情況之後,開發者鼓勵用戶暫時降級到舊版本(會拒絕掉這個大區塊的版本),然後更新到一個緊急版本,以軟分叉暫時將區塊大小的上限降到500 KB ,好留出時間讓每個用戶都能升級新的數據庫引擎,而這種暫時的下調會在幾個月之後自動過期。
未來的激活
Segwit 激活幾個月出現問題之後,一些人開始考慮BIP8。 BIP8 的支持者們認為它能解決BIP9 的一些問題:
允許強制激活:BIP8 是BIP148 的一般化,礦工可以在等待激活的時間段裡自願發信號表示支持,但它還設了一個最後通牒時間段,礦工在這段時間裡必鬚髮信號表示支持,否則所生產的區塊就有可能變作無效的。後來,人們設計了一個參數LockinOnTimeout (LOT)來觸發這種動作:使用LOT=true 的節點,會要求礦工在激活即將超時的最後一段時間裡發出信號;使用LOT=false 的節點,不會這麼要求,但如果有足夠多的區塊帶有信號,仍然會實行新規則。
使用高度而非時間:BIP9 開始和停止監控激活信號的時間都基於礦工寫入區塊的時間的平均值。所以礦工是有可能操控這個時間的,這會阻礙LOT=true 的功能,所以BIP8 提議使用區塊高度而非時間。
BIP8 的靈活性使其成為了taproot 軟分叉的多種候選激活提案之一,雖然批評者也批評了它的某些方面,比如某些設置允許礦工拒絕激活得到廣泛社區支持的提議、鼓勵一個團體“俘虜” 另一個團體所用的信號機制、要求礦工對所生產的區塊作沒有實質意義的更改、看起來給了開發者凌駕於共識規則的權威以及提高了共識失敗的風險。截至本文撰寫之時,taproot 激活方法的討論仍在進行。
BIP9
BIP8
主要的代碼和文檔
(很多,略)
Optech 新聞和網站相關部分
(很多,略)
Taproot