跳至主要內容
進階

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. 1 開發者選擇一個已被廣泛確認的區塊(通常 1000+ 確認)
  2. 2 驗證該區塊在所有已知節點上都存在且相同
  3. 3 更新源碼中的雜湊值
  4. 4 代碼審查確保雜湊正確
  5. 5 發布新版本

最佳實踐

✓ 推薦做法

  • • 對大多數用戶使用預設設定
  • • 保持 Bitcoin Core 更新
  • • 信任但驗證源碼來源
  • • 理解這只是效能優化

⚠ 特殊情況

  • • 安全審計時使用 -assumevalid=0
  • • 研究歷史交易時完整驗證
  • • 建立新實現時從頭驗證
  • • 懷疑鏈分裂時完整驗證

總結

  • 效能優化:IBD 時間減少 70-80%,不犧牲安全性
  • 仍然驗證:PoW、區塊結構、UTXO 支出都完整驗證
  • 可選擇性:用戶可以禁用並執行完整驗證
  • 透明公開:AssumeValid 雜湊在開源代碼中公開審查
已複製連結
已複製到剪貼簿