高級
Soft Fork Activation
軟分叉啟用機制:BIP-9、BIP-8 和 Speedy Trial
18 分鐘
概述
軟分叉是一種向後兼容的共識規則更改。舊節點仍然認為新規則產生的區塊有效, 但新節點會拒絕不符合新規則的區塊。啟用軟分叉需要協調礦工和節點, 這個過程有多種機制可以選擇。
軟分叉 vs 硬分叉: 軟分叉收緊規則(新規則是舊規則的子集),硬分叉放寬規則。 軟分叉不需要所有節點同時升級,更安全平滑。
歷史方法
Flag Day
Flag Day 啟用:
定義: 在指定日期/高度自動啟用新規則
範例:
- BIP-16 (P2SH): 區塊高度 173,805
- BIP-30: 2012年3月15日
流程:
1. 開發者確定啟用時間
2. 發布包含新規則的軟體
3. 節點在指定時間後執行新規則
優點:
- 簡單直接
- 確定的時間表
缺點:
- 如果礦工算力不足,可能造成鏈分裂
- 不知道礦工準備情況
- 可能需要多次推遲 IsSuperMajority (ISM)
IsSuperMajority 機制:
定義: 要求最近 1000 個區塊中 950 個 (95%) 使用新版本
用於:
- BIP-34 (Coinbase 高度)
- BIP-66 (嚴格 DER 簽名)
- BIP-65 (OP_CHECKLOCKTIMEVERIFY)
流程:
1. 礦工升級並設置區塊版本號
2. 當 95% 區塊使用新版本時,規則生效
3. 之後拒絕舊版本區塊
問題:
- 區塊版本號是整數,難以同時進行多個升級
- 沒有明確的超時機制
- 可能無限期停滯 BIP-9 版本位
機制設計
BIP-9 版本位信號:
區塊頭版本欄位 (4 bytes):
┌─────────────────────────────────────────────────────────┐
│ bit 31-29 │ bit 28 │ bit 27-0 │
│ 001 │ ? │ 可用於信號的 29 個位 │
├───────────┴────────┴────────────────────────────────────┤
│ 0x20000000 = 基礎版本 (版本位啟用) │
└─────────────────────────────────────────────────────────┘
每個軟分叉分配一個位 (0-28):
- 位 0: 某個提案
- 位 1: 另一個提案
- ...
可以同時進行多個升級信號
狀態機:
DEFINED → STARTED → LOCKED_IN → ACTIVE
↓
FAILED 參數
BIP-9 參數:
每個部署定義:
- bit: 使用的位 (0-28)
- starttime: 開始時間 (Unix 時間戳)
- timeout: 超時時間
- threshold: 激活閾值 (通常 95%)
難度調整週期 (2016 區塊) 為計數單位
狀態轉換:
┌──────────────────────────────────────────────────────────┐
│ DEFINED │
│ 當前時間 >= starttime │
│ ↓ │
│ STARTED │
│ 週期內 >= 95% 區塊設置該位 │
│ ↓ │
│ LOCKED_IN │
│ 一個週期後 │
│ ↓ │
│ ACTIVE │
│ 規則永久生效 │
├──────────────────────────────────────────────────────────┤
│ STARTED │
│ 當前時間 >= timeout 且未達到閾值 │
│ ↓ │
│ FAILED │
│ 提案失敗 │
└──────────────────────────────────────────────────────────┘ 實作
enum DeploymentState {
DEFINED = 'DEFINED',
STARTED = 'STARTED',
LOCKED_IN = 'LOCKED_IN',
ACTIVE = 'ACTIVE',
FAILED = 'FAILED'
}
interface BIP9Deployment {
bit: number;
startTime: number;
timeout: number;
threshold: number; // 0.95 for 95%
minActivationHeight?: number;
}
const RETARGET_INTERVAL = 2016;
function getDeploymentState(
deployment: BIP9Deployment,
currentHeight: number,
currentTime: number,
getBlock: (height: number) => { version: number; time: number }
): DeploymentState {
// 計算當前週期的起始高度
const periodStart = Math.floor(currentHeight / RETARGET_INTERVAL) * RETARGET_INTERVAL;
const periodEnd = periodStart + RETARGET_INTERVAL - 1;
// 獲取週期開始時間
const periodStartTime = getBlock(periodStart).time;
// 狀態轉換邏輯
if (periodStartTime < deployment.startTime) {
return DeploymentState.DEFINED;
}
// 檢查之前週期是否已 LOCKED_IN 或 ACTIVE
// (簡化:這裡需要遞歸或緩存之前的狀態)
if (periodStartTime >= deployment.timeout) {
// 需要檢查是否曾經 LOCKED_IN
return DeploymentState.FAILED; // 或 ACTIVE
}
// 計算信號比例
let signalCount = 0;
for (let h = periodStart; h <= Math.min(periodEnd, currentHeight); h++) {
const block = getBlock(h);
if (block.version & (1 << deployment.bit)) {
signalCount++;
}
}
const threshold = Math.floor(RETARGET_INTERVAL * deployment.threshold);
if (signalCount >= threshold) {
return DeploymentState.LOCKED_IN;
}
return DeploymentState.STARTED;
}
// 檢查區塊版本是否信號支持
function isSignaling(version: number, bit: number): boolean {
// 必須是版本位格式
if ((version & 0xE0000000) !== 0x20000000) {
return false;
}
return (version & (1 << bit)) !== 0;
} BIP-8
強制啟用選項
BIP-8 改進:
BIP-9 的問題:
- 礦工可以無限期拖延
- 即使社區支持,也可能超時失敗
BIP-8 添加:
- lockinontimeout (LOT) 參數
- LOT=true: 超時時強制 LOCKED_IN
- LOT=false: 與 BIP-9 相同
狀態機 (LOT=true):
DEFINED → STARTED → LOCKED_IN → ACTIVE
↓ ↑
MUST_SIGNAL ───┘
在 MUST_SIGNAL 期間:
- 必須設置信號位
- 不設置的區塊被認為無效
爭議:
- LOT=true 可能造成鏈分裂
- 礦工被迫升級
- 類似 UASF (用戶激活軟分叉) 使用區塊高度
BIP-8 使用高度而非時間:
BIP-9:
- 使用 MTP (中位時間過去值)
- 時間可被操縱 (時間戳範圍寬鬆)
BIP-8:
- 使用區塊高度
- 更可預測
- 無法被礦工操縱
參數:
- startheight: 開始信號的高度
- timeoutheight: 超時高度
- threshold: 閾值 (可自定義)
- lockinontimeout: 是否強制激活 Speedy Trial
Taproot 啟用
Speedy Trial (快速嘗試):
用於 Taproot 激活 (2021):
特點:
- 短暫的信號窗口 (3 個月)
- 90% 閾值 (低於傳統 95%)
- 如果成功,延遲激活到固定高度
- 如果失敗,可以用其他方法重試
Taproot 參數:
- 開始: 區塊 681,408 (2021年4月)
- 超時: 區塊 693,504 (2021年8月)
- 閾值: 90% (1815/2016)
- 激活高度: 709,632 (2021年11月)
結果:
- 2021年6月12日 LOCKED_IN
- 2021年11月14日 ACTIVE
為什麼用這種方式:
- 避免 LOT 爭議
- 給礦工時間準備
- 如果失敗可以再討論 時間表
Taproot 激活時間表:
2020年10月: Bitcoin Core 0.21.0 包含 Taproot 代碼
2021年4月: 開始信號
2021年6月: 達到 90% 閾值, LOCKED_IN
2021年11月: 區塊 709,632 激活
信號統計:
週期 1: 低於閾值
週期 2: 低於閾值
週期 3: 達到 90%+ ✓
這表明礦工有足夠時間準備升級 實作細節
版本位計算
// 計算區塊版本
function computeBlockVersion(
deployments: Map<string, BIP9Deployment>,
currentHeight: number,
currentTime: number,
getBlock: (height: number) => { version: number; time: number }
): number {
let version = 0x20000000; // 基礎版本位
for (const [name, deployment] of deployments) {
const state = getDeploymentState(
deployment,
currentHeight,
currentTime,
getBlock
);
// 只在 STARTED 或 MUST_SIGNAL 狀態設置位
if (state === DeploymentState.STARTED) {
version |= (1 << deployment.bit);
}
}
return version;
}
// 驗證區塊版本
function validateBlockVersion(
block: { version: number; height: number; time: number },
deployments: Map<string, BIP9Deployment>,
getBlock: (height: number) => { version: number; time: number }
): boolean {
for (const [name, deployment] of deployments) {
const state = getDeploymentState(
deployment,
block.height,
block.time,
getBlock
);
// 在 MUST_SIGNAL 期間必須設置位
if (state === 'MUST_SIGNAL') {
if (!(block.version & (1 << deployment.bit))) {
return false; // 區塊無效
}
}
}
return true;
} 共識規則檢查
// 檢查軟分叉規則是否生效
function isDeploymentActive(
deploymentName: string,
height: number,
chainState: ChainState
): boolean {
const deployment = chainState.deployments.get(deploymentName);
if (!deployment) return false;
const state = getDeploymentState(
deployment,
height,
chainState.getMedianTime(height),
(h) => chainState.getBlock(h)
);
return state === DeploymentState.ACTIVE;
}
// 在交易驗證中使用
function validateTransaction(
tx: Transaction,
height: number,
chainState: ChainState
): boolean {
// 如果 SegWit 已激活
if (isDeploymentActive('segwit', height, chainState)) {
// 驗證 witness 數據
if (!validateWitness(tx)) {
return false;
}
}
// 如果 Taproot 已激活
if (isDeploymentActive('taproot', height, chainState)) {
// 驗證 Taproot 規則
if (!validateTaproot(tx)) {
return false;
}
}
return true;
} 重要軟分叉
比特幣主要軟分叉:
2012: BIP-16 (P2SH)
- Flag Day 啟用
- 引入腳本雜湊地址
2015: BIP-65 (CLTV)
- ISM 啟用
- 時間鎖功能
2015: BIP-66 (嚴格 DER)
- ISM 啟用
- 修復簽名格式問題
2016: BIP-68/112/113 (CSV)
- BIP-9 啟用
- 相對時間鎖
2017: BIP-141/143/144 (SegWit)
- BIP-9 啟用 (經歷 UASF 爭議)
- 隔離見證
2021: BIP-341/342 (Taproot)
- Speedy Trial 啟用
- Schnorr 簽名和 MAST 爭議與教訓
SegWit 啟用爭議 (2017):
背景:
- SegWit 準備就緒
- 部分礦工反對
- 長期無法達到 95%
UASF (BIP-148):
- 用戶激活軟分叉
- 威脅礦工必須信號
- 引發 BIP-91 妥協
教訓:
1. 95% 閾值可能過高
2. 礦工不應有否決權
3. 需要更好的協調機制
Taproot 的不同:
- 社區更團結
- Speedy Trial 減少爭議
- 90% 閾值更合理
- 成功且平滑的升級 未來展望
潛在的未來軟分叉:
1. OP_CTV (BIP-119)
- 契約功能
- 激活方法待定
2. ANYPREVOUT (BIP-118)
- 簽名可重新綁定
- 支持 Eltoo 閃電通道
3. 大區塊 (如 64-bit arithmetic)
- 各種改進提案
啟用機制討論:
- 社區傾向 Speedy Trial 類似方法
- 可能需要更多社區共識工具
- 礦工信號作為準備度指標 相關資源
已複製連結