跳至主要內容
高級

CoinStatsIndex

深入了解 Bitcoin Core 的 coinstatsindex,提供 UTXO 集統計和 muhash 驗證功能。

12 分鐘

什麼是 CoinStatsIndex?

CoinStatsIndex 是 Bitcoin Core 22.0 引入的可選索引,用於追蹤每個區塊高度的 UTXO 集統計資訊。 它提供快速的 UTXO 集查詢功能,支援 MuHash 驗證,是 AssumeUTXO 功能的重要基礎。

主要功能

UTXO 統計

  • • 每個高度的 UTXO 數量
  • • 總供應量追蹤
  • • OP_RETURN 燒毀統計

MuHash

  • • UTXO 集的密碼學承諾
  • • 支援增量更新
  • • AssumeUTXO 驗證

配置方式

啟用索引

# 命令行啟用
bitcoind -coinstatsindex

# 配置文件
echo "coinstatsindex=1" >> ~/.bitcoin/bitcoin.conf

# 首次啟用需要建立索引
# 會掃描整個區塊鏈,可能需要數小時

注意: 建立索引需要掃描整個區塊鏈。在 SSD 上約需 2-4 小時, HDD 可能需要更長時間。索引大小約 2-3 GB。

儲存位置

# 索引儲存在
~/.bitcoin/indexes/coinstats/

# 檔案結構
indexes/coinstats/
├── db/          # LevelDB 資料庫
└── ...

# 查看索引大小
du -sh ~/.bitcoin/indexes/coinstats/

RPC 命令

gettxoutsetinfo

# 基本用法(沒有 coinstatsindex 也可用,但較慢)
bitcoin-cli gettxoutsetinfo

# 使用 coinstatsindex(快速)
bitcoin-cli gettxoutsetinfo muhash

# 查詢特定高度(需要 coinstatsindex)
bitcoin-cli gettxoutsetinfo muhash '"800000"'

# 輸出示例
{
  "height": 800000,
  "bestblock": "00000000000000000002a7c4c1e48d76c5a37902165a270156b7a8d72728a054",
  "txouts": 95234567,
  "bogosize": 7123456789,
  "muhash": "abcdef1234567890...",
  "total_amount": 19468750.00000000,
  "total_unspendable_amount": 219.12345678,
  "block_info": {
    "prevout_spent": 12345.67890000,
    "coinbase": 6.25000000,
    "new_outputs_ex_coinbase": 12340.42890000,
    "unspendable": 0.00000000,
    "unspendables": {
      "genesis_block": 50.00000000,
      "bip30": 0.00000000,
      "scripts": 169.12345678,
      "unclaimed_rewards": 0.00000000
    }
  }
}

輸出欄位說明

欄位 說明
txouts UTXO 數量
bogosize 序列化大小估算(字節)
muhash UTXO 集的 MuHash
total_amount 可花費的總金額
total_unspendable_amount 不可花費的總金額

MuHash 原理

什麼是 MuHash?

MuHash(Multiplicative Hash)是一種可增量更新的集合哈希函數。 它允許高效地計算 UTXO 集的密碼學承諾,而不需要遍歷整個集合。

// MuHash 的數學原理
// 基於有限域上的乘法

class MuHash {
  private numerator: bigint;    // 分子
  private denominator: bigint;  // 分母
  private readonly prime: bigint;  // 3072-bit 素數

  constructor() {
    this.numerator = 1n;
    this.denominator = 1n;
    this.prime = /* 大素數 */;
  }

  // 添加元素:乘以元素的哈希
  add(element: Buffer): void {
    const hash = this.hashToFieldElement(element);
    this.numerator = (this.numerator * hash) % this.prime;
  }

  // 移除元素:除以元素的哈希(乘以分母)
  remove(element: Buffer): void {
    const hash = this.hashToFieldElement(element);
    this.denominator = (this.denominator * hash) % this.prime;
  }

  // 合併兩個 MuHash
  combine(other: MuHash): void {
    this.numerator = (this.numerator * other.numerator) % this.prime;
    this.denominator = (this.denominator * other.denominator) % this.prime;
  }

  // 獲取最終哈希
  finalize(): Buffer {
    // 計算 numerator / denominator (mod prime)
    const denominatorInv = this.modInverse(this.denominator, this.prime);
    const result = (this.numerator * denominatorInv) % this.prime;
    return this.toSHA256(result);
  }

  private hashToFieldElement(data: Buffer): bigint {
    // 使用 SHA256 將數據映射到有限域
    const hash = sha256(data);
    return BigInt('0x' + hash.toString('hex')) % this.prime;
  }
}

MuHash 特性

  • 1
    可交換性

    元素的添加順序不影響結果

  • 2
    可增量更新

    添加或刪除元素的複雜度為 O(1)

  • 3
    可合併性

    兩個 MuHash 可以高效合併

  • 4
    抗碰撞

    基於離散對數假設的安全性

實現細節

索引結構

interface CoinStatsEntry {
  height: number;
  blockHash: string;

  // UTXO 統計
  txoutCount: bigint;
  bogoSize: bigint;
  totalAmount: bigint;

  // MuHash
  muhash: Buffer;  // 32 bytes

  // 供應量追蹤
  totalUnspendable: bigint;
  genesisBlockUnspendable: bigint;
  bip30Unspendable: bigint;
  scriptsUnspendable: bigint;
  unclaimedRewards: bigint;
}

class CoinStatsIndex {
  private db: LevelDB;
  private currentStats: CoinStatsEntry;
  private muhash: MuHash;

  async init(): Promise {
    // 從 chainstate 初始化 MuHash
    this.muhash = new MuHash();

    // 遍歷所有現有 UTXO 建立初始 MuHash
    for await (const utxo of this.iterateUTXOs()) {
      const serialized = this.serializeUTXO(utxo);
      this.muhash.add(serialized);
    }
  }

  // 處理新區塊
  async connectBlock(block: Block, undoData: UndoData): Promise {
    // 移除被花費的 UTXO
    for (const spent of undoData.spentOutputs) {
      const serialized = this.serializeUTXO(spent);
      this.muhash.remove(serialized);
      this.currentStats.txoutCount--;
      this.currentStats.totalAmount -= spent.value;
    }

    // 添加新的 UTXO
    for (const tx of block.transactions) {
      for (const output of tx.outputs) {
        if (!this.isUnspendable(output)) {
          const utxo = this.createUTXO(tx.txid, output);
          const serialized = this.serializeUTXO(utxo);
          this.muhash.add(serialized);
          this.currentStats.txoutCount++;
          this.currentStats.totalAmount += output.value;
        } else {
          // 追蹤不可花費的輸出
          this.currentStats.totalUnspendable += output.value;
          this.currentStats.scriptsUnspendable += output.value;
        }
      }
    }

    // 更新區塊資訊
    this.currentStats.height = block.height;
    this.currentStats.blockHash = block.hash;
    this.currentStats.muhash = this.muhash.finalize();

    // 寫入資料庫
    await this.db.put(block.height, this.serialize(this.currentStats));
  }

  // 序列化 UTXO 用於 MuHash
  private serializeUTXO(utxo: UTXO): Buffer {
    // 格式: txid + vout + script + value + height + coinbase
    return Buffer.concat([
      Buffer.from(utxo.txid, 'hex'),
      this.encodeVarInt(utxo.vout),
      utxo.scriptPubKey,
      this.encodeAmount(utxo.value),
      this.encodeVarInt(utxo.height),
      Buffer.from([utxo.coinbase ? 1 : 0]),
    ]);
  }
}

與 AssumeUTXO 的關係

AssumeUTXO 驗證

CoinStatsIndex 的 MuHash 是 AssumeUTXO 功能的核心。 它允許快速驗證預設的 UTXO 集快照是否正確。

// AssumeUTXO 驗證流程
class AssumeUTXOVerifier {
  async verifySnapshot(
    snapshot: UTXOSnapshot,
    expectedMuHash: string
  ): Promise {
    // 使用 coinstatsindex 計算 MuHash
    const muhash = new MuHash();

    for (const utxo of snapshot.utxos) {
      const serialized = this.serializeUTXO(utxo);
      muhash.add(serialized);
    }

    const calculatedHash = muhash.finalize().toString('hex');

    // 比對預期的 MuHash
    return calculatedHash === expectedMuHash;
  }
}

// chainparams.cpp 中的 AssumeUTXO 配置
const ASSUME_UTXO_DATA = {
  // 高度 840000 的快照
  840000: {
    hashSerialized: '...',  // UTXO 集的 SHA256
    muhash: '...',          // MuHash(用於驗證)
    txoutCount: 150000000,
    totalAmount: 19700000_00000000n,
  },
};

使用場景

✓ 適合啟用

  • • 需要查詢歷史 UTXO 統計
  • • 進行區塊鏈分析
  • • 驗證 AssumeUTXO 快照
  • • 研究供應量分佈

✗ 可能不需要

  • • 磁碟空間有限
  • • 只需要基本節點功能
  • • 使用 pruned 模式
  • • 效能敏感的環境

供應量審計

# 驗證比特幣總供應量
bitcoin-cli gettxoutsetinfo muhash | jq '{
  total_amount,
  total_unspendable_amount,
  theoretical_supply: (19 * 10000000 + (.height - 840000) * 3.125)
}'

# 分析不可花費的比特幣
bitcoin-cli gettxoutsetinfo muhash | jq '.block_info.unspendables'

效能考量

資源需求

項目 數值
磁碟空間 約 2-3 GB
初始建立時間(SSD) 2-4 小時
每區塊更新時間 < 1 秒
歷史查詢時間 < 100 ms

總結

  • UTXO 統計:每個高度的 UTXO 集統計資訊
  • MuHash:可增量更新的 UTXO 集密碼學承諾
  • AssumeUTXO:驗證 UTXO 快照的重要基礎
  • 資源:需要額外 2-3 GB 磁碟空間和初始建立時間
已複製連結
已複製到剪貼簿