進階
AssumeValid
深入了解 Bitcoin Core 的 AssumeValid 優化機制,如何在保持安全性的前提下加速初始區塊下載。
12 分鐘
什麼是 AssumeValid?
AssumeValid 是 Bitcoin Core 0.14.0 引入的優化功能,允許節點在初始區塊下載(IBD)期間跳過對歷史區塊中交易簽名的驗證。 這不是信任假設的改變,而是效能優化——節點仍然驗證所有其他共識規則,只是推遲了簽名驗證。
AssumeValid vs 傳統驗證
傳統 IBD
- • 驗證每個交易的簽名
- • CPU 密集型操作
- • 同步需要數小時到數天
- • 完整的密碼學驗證
AssumeValid IBD
- • 跳過已知區塊的簽名驗證
- • 大幅減少 CPU 使用
- • 同步時間減少 80%+
- • 仍驗證區塊結構和 UTXO
運作原理
AssumeValid 區塊
Bitcoin Core 在源碼中硬編碼了一個「假設有效」的區塊雜湊。對於這個區塊及其所有祖先區塊,節點會跳過腳本驗證。
驗證流程
下載區塊 → 驗證 PoW → 驗證區塊結構 → 驗證 Merkle Root
↓
區塊高度 <= AssumeValid?
↙ ↘
是 否
↓ ↓
跳過腳本驗證 完整腳本驗證
↓ ↓
更新 UTXO Set 更新 UTXO Set 仍然驗證的內容
AssumeValid 不是盲目信任,節點仍然驗證大量共識規則:
✓ 仍然驗證
- • 工作量證明(PoW)
- • 區塊結構和大小
- • Merkle 樹正確性
- • 交易結構有效性
- • UTXO 雙花檢查
- • 幣齡和金額
- • 時間鎖條件
- • Coinbase 成熟度
⏭ 跳過驗證
- • ECDSA/Schnorr 簽名
- • 腳本執行
- • OP_CHECKSIG 操作
- • 多重簽名驗證
- • 時間鎖腳本
- • 自訂腳本邏輯
安全性分析
信任模型
AssumeValid 的安全性基於以下觀察:
- 1. PoW 仍然驗證: 攻擊者無法創建不包含真實工作量證明的假區塊鏈。
- 2. UTXO 仍然驗證: 雙花和無效支出仍會被檢測到,只是不驗證簽名本身。
- 3. 公開審查: AssumeValid 雜湊在源碼中公開,任何人都可以審查。
- 4. 可禁用: 用戶可以用
-assumevalid=0禁用此功能。
攻擊場景分析
假設的攻擊
如果 Bitcoin Core 開發者是惡意的,他們可以在 AssumeValid 區塊之前插入一個偷取所有比特幣的交易?
為什麼不可行
- • 需要礦工配合:惡意交易需要被打包進區塊,需要礦工的 PoW
- • UTXO 驗證:偷取比特幣會創建無效的 UTXO 支出,這仍會被檢測
- • 公開可審計:任何人都可以驗證 AssumeValid 區塊是否在主鏈上
- • 需要超過源碼信任:如果不信任源碼,根本不應該運行該軟體
配置選項
命令行選項
# 使用預設的 AssumeValid(推薦)
bitcoind
# 禁用 AssumeValid,完整驗證所有簽名
bitcoind -assumevalid=0
# 使用自訂的 AssumeValid 區塊
bitcoind -assumevalid=000000000000000000029ab747014fcd6b1e0d3b1d0c4e5a6e7f8b9c0d1e2f3a
# 查看當前的 AssumeValid 設定
bitcoin-cli getblockchaininfo | grep assumevalid 配置文件
在 bitcoin.conf 中設定:
# 禁用 AssumeValid
assumevalid=0
# 或指定特定區塊
assumevalid=000000000000000000029ab747014fcd6b1e0d3b1d0c4e5a6e7f8b9c0d1e2f3a 實現細節
源碼中的定義
AssumeValid 區塊雜湊在 chainparams.cpp 中定義,每個版本都會更新到較新的區塊:
// src/kernel/chainparams.cpp (Bitcoin Core)
class CMainParams : public CChainParams {
public:
CMainParams() {
// ...
// AssumeValid 區塊雜湊
// 這個區塊及其所有祖先的腳本都不會被驗證
consensus.defaultAssumeValid = uint256S(
"00000000000000000001a0a448d6cf2546b06801389cc030b2b18c6491266815"
);
// 相關的最小鏈工作量
consensus.nMinimumChainWork = uint256S(
"000000000000000000000000000000000000000052b2559353df4117b7348b64"
);
}
}; 驗證邏輯
// 簡化的驗證邏輯
bool CheckBlock(const CBlock& block, CValidationState& state) {
// 1. 始終驗證 PoW
if (!CheckProofOfWork(block.GetHash(), block.nBits, params)) {
return state.Invalid("high-hash");
}
// 2. 始終驗證區塊結構
if (!CheckBlockStructure(block, state)) {
return false;
}
// 3. 檢查是否在 AssumeValid 範圍內
bool fScriptChecks = true;
if (pindexBestHeader != nullptr) {
// 如果當前區塊是 assumevalid 區塊的祖先
if (IsAssumeValidAncestor(pindex)) {
fScriptChecks = false; // 跳過腳本驗證
}
}
// 4. 驗證交易
for (const auto& tx : block.vtx) {
if (!CheckTransaction(*tx, state)) {
return false;
}
// 只有在需要時才驗證腳本
if (fScriptChecks) {
if (!VerifyScript(tx->vin, tx->vout)) {
return state.Invalid("script-verify-failed");
}
}
}
return true;
} TypeScript 模擬
interface Block {
hash: string;
height: number;
prevHash: string;
transactions: Transaction[];
nonce: number;
difficulty: bigint;
}
interface ValidationConfig {
assumeValidBlock: string | null; // null = 完整驗證
}
class BlockValidator {
private assumeValidHeight: number | null = null;
constructor(private config: ValidationConfig) {}
async initialize(getBlockHeight: (hash: string) => Promise) {
if (this.config.assumeValidBlock) {
this.assumeValidHeight = await getBlockHeight(
this.config.assumeValidBlock
);
}
}
async validateBlock(block: Block): Promise {
// 1. 始終驗證 PoW
if (!this.validatePoW(block)) {
throw new Error('Invalid proof of work');
}
// 2. 始終驗證區塊結構
if (!this.validateStructure(block)) {
throw new Error('Invalid block structure');
}
// 3. 決定是否需要腳本驗證
const needScriptValidation = this.shouldValidateScripts(block);
// 4. 驗證所有交易
for (const tx of block.transactions) {
// UTXO 驗證(始終執行)
if (!await this.validateUTXO(tx)) {
throw new Error('Invalid UTXO spend');
}
// 腳本驗證(可能跳過)
if (needScriptValidation) {
if (!await this.validateScripts(tx)) {
throw new Error('Script validation failed');
}
}
}
return true;
}
private shouldValidateScripts(block: Block): boolean {
// 如果沒有設定 assumevalid,驗證所有腳本
if (this.assumeValidHeight === null) {
return true;
}
// 如果區塊高度超過 assumevalid,需要驗證
return block.height > this.assumeValidHeight;
}
private validatePoW(block: Block): boolean {
// 驗證區塊雜湊小於目標難度
const hash = BigInt('0x' + block.hash);
const target = this.calculateTarget(block.difficulty);
return hash < target;
}
private validateStructure(block: Block): boolean {
// 驗證 Merkle root、區塊大小等
return true; // 簡化
}
private async validateUTXO(tx: Transaction): Promise {
// 驗證輸入引用有效的 UTXO,金額正確等
return true; // 簡化
}
private async validateScripts(tx: Transaction): Promise {
// 執行腳本驗證(ECDSA 簽名等)
return true; // 簡化
}
private calculateTarget(difficulty: bigint): bigint {
const maxTarget = BigInt('0x00000000FFFF0000000000000000000000000000000000000000000000000000');
return maxTarget / difficulty;
}
}
// 使用示例
const validator = new BlockValidator({
assumeValidBlock: '00000000000000000001a0a448d6cf2546b06801389cc030b2b18c6491266815'
});
// 同步期間
async function syncBlocks(blocks: Block[]) {
for (const block of blocks) {
await validator.validateBlock(block);
console.log(`Block ${block.height}: validated (scripts: ${
block.height > 800000 ? 'yes' : 'skipped'
})`);
}
} 效能影響
IBD 時間比較(典型硬體)
| 模式 | CPU 使用 | 同步時間 | 安全性 |
|---|---|---|---|
| 完整驗證 | 100% | 24-72 小時 | 最高 |
| AssumeValid(預設) | ~30% | 4-12 小時 | 等效 |
| AssumeUTXO | ~10% | 10-30 分鐘* | 信任快照 |
* AssumeUTXO 需要額外時間在背景完成驗證
為什麼快這麼多?
簽名驗證是 IBD 中最耗費 CPU 的操作。區塊鏈包含數億個簽名,每個都需要橢圓曲線運算:
- • ECDSA 驗證:每個簽名需要 ~1ms CPU 時間
- • 總簽名數:區塊鏈包含 10+ 億個簽名
- • 跳過效益:節省 80%+ 的 CPU 計算時間
- • 瓶頸轉移:從 CPU 轉為 I/O 和網路
AssumeValid vs AssumeUTXO
| 特性 | AssumeValid | AssumeUTXO |
|---|---|---|
| 跳過內容 | 簽名驗證 | 整個歷史區塊 |
| PoW 驗證 | 是 | 背景執行 |
| UTXO 驗證 | 是 | 信任快照 |
| 同步速度 | 快 3-5 倍 | 快 10-20 倍 |
| 預設啟用 | 是 | 否 |
| 最終狀態 | 完整驗證節點 | 完整驗證節點* |
* AssumeUTXO 在背景完成驗證後達到完整驗證狀態
更新機制
AssumeValid 區塊雜湊會在每個 Bitcoin Core 主要版本中更新,通常選擇發布前幾個月的區塊:
更新流程
- 1 開發者選擇一個已被廣泛確認的區塊(通常 1000+ 確認)
- 2 驗證該區塊在所有已知節點上都存在且相同
- 3 更新源碼中的雜湊值
- 4 代碼審查確保雜湊正確
- 5 發布新版本
最佳實踐
✓ 推薦做法
- • 對大多數用戶使用預設設定
- • 保持 Bitcoin Core 更新
- • 信任但驗證源碼來源
- • 理解這只是效能優化
⚠ 特殊情況
- • 安全審計時使用 -assumevalid=0
- • 研究歷史交易時完整驗證
- • 建立新實現時從頭驗證
- • 懷疑鏈分裂時完整驗證
總結
- ✓ 效能優化:IBD 時間減少 70-80%,不犧牲安全性
- ✓ 仍然驗證:PoW、區塊結構、UTXO 支出都完整驗證
- ✓ 可選擇性:用戶可以禁用並執行完整驗證
- ✓ 透明公開:AssumeValid 雜湊在開源代碼中公開審查
已複製連結