進階
Pruning
深入了解 Bitcoin Core 的區塊鏈修剪機制,如何在有限硬碟空間下運行完整驗證節點。
10 分鐘
什麼是 Pruning?
Pruning(修剪)是 Bitcoin Core 的一項功能,允許節點在完成驗證後刪除舊的區塊數據,只保留 UTXO 集合和最近的區塊。 這讓用戶可以在有限的硬碟空間下運行完整驗證節點。
存儲需求比較
~600 GB
完整區塊鏈
(持續增長)
~10 GB
修剪節點(最小)
(550 MiB 區塊)
~8 GB
UTXO 集合
(必須保留)
運作原理
保留的數據
✓ 永久保留
- • UTXO 集合:所有未花費的交易輸出
- • 區塊頭:所有區塊的 80 位元組頭部
- • 區塊索引:區塊位置和元數據
- • 鏈狀態:當前鏈頂和驗證狀態
✗ 被刪除
- • 舊區塊體:完整交易數據
- • 舊區塊撤銷數據:用於重組的回滾數據
- • 已花費輸出:歷史 UTXO
修剪流程
初始區塊下載(IBD)
↓
接收區塊 → 完整驗證 → 更新 UTXO → 寫入磁碟
↓
區塊數據超過限制?
↙ ↘
否 是
↓ ↓
繼續 刪除最舊區塊
↓
保留區塊頭
↓
釋放磁碟空間 目標大小
修剪目標是保留的區塊數據量,不包括 UTXO 集合和索引。最小值是 550 MiB,建議至少 1000 MiB:
# 最小修剪(550 MiB 區塊數據)
bitcoind -prune=550
# 建議設定(2 GB 區塊數據)
bitcoind -prune=2000
# 保留約一週的區塊(~10 GB)
bitcoind -prune=10000
# 配置文件設定
echo "prune=2000" >> ~/.bitcoin/bitcoin.conf 配置選項
修剪模式
| 設定 | 說明 | 磁碟使用 |
|---|---|---|
| prune=0 | 禁用修剪(預設) | ~600 GB+ |
| prune=1 | 手動修剪模式 | 可變 |
| prune=550 | 自動修剪(最小) | ~10 GB |
| prune=10000 | 自動修剪(10 GB) | ~18 GB |
手動修剪
# 啟用手動修剪模式
bitcoind -prune=1
# 手動修剪到指定高度
bitcoin-cli pruneblockchain 800000
# 返回實際修剪到的高度
# {
# "result": 800000
# }
# 查看修剪狀態
bitcoin-cli getblockchaininfo | grep prune 相關 RPC 命令
# 查看區塊鏈資訊
bitcoin-cli getblockchaininfo
# {
# "pruned": true,
# "pruneheight": 750000,
# "prune_target_size": 576716800,
# ...
# }
# 手動觸發修剪
bitcoin-cli pruneblockchain 800000
# 查看磁碟使用
bitcoin-cli gettxoutsetinfo
# {
# "disk_size": 8234567890, # UTXO 集合大小
# ...
# } 功能限制
修剪節點的限制
- ⚠ 無法提供歷史區塊: 修剪節點不能為其他節點提供已刪除的區塊數據。
- ⚠ 無法重新掃描:
rescanblockchain只能掃描保留的區塊。 - ⚠ 無法使用 txindex: 交易索引需要完整區塊數據,與修剪不兼容。
- ⚠ 錢包導入受限: 導入舊地址可能無法找到歷史交易。
仍然可行的功能
- • 完整交易驗證(當前和未來)
- • 接收和發送比特幣
- • 驗證新區塊和交易
- • 提供區塊頭給 SPV 客戶端
- • 提供保留範圍內的區塊
- • 參與交易中繼
實現細節
區塊文件結構
Bitcoin Core 將區塊存儲在 blk*.dat 文件中,每個文件最大約 128 MB:
~/.bitcoin/blocks/
├── blk00000.dat # 區塊數據
├── blk00001.dat
├── ...
├── blkNNNNN.dat
├── rev00000.dat # 撤銷數據(用於重組)
├── rev00001.dat
├── ...
└── index/ # LevelDB 索引
├── 000001.log
├── CURRENT
└── ... 修剪演算法
interface PruneState {
pruneTarget: number; // 目標保留大小(位元組)
currentSize: number; // 當前區塊數據大小
pruneHeight: number; // 已修剪到的高度
minBlocksToKeep: number; // 最少保留區塊數
}
class BlockPruner {
private readonly MIN_BLOCKS = 288; // 約 2 天的區塊
async maybePrune(state: PruneState): Promise {
// 檢查是否需要修剪
if (state.currentSize <= state.pruneTarget) {
return;
}
const chainHeight = await this.getChainHeight();
const minKeepHeight = chainHeight - this.MIN_BLOCKS;
// 從最舊的區塊開始刪除
let heightToDelete = state.pruneHeight + 1;
while (
state.currentSize > state.pruneTarget &&
heightToDelete < minKeepHeight
) {
await this.pruneBlock(heightToDelete);
heightToDelete++;
}
console.log(`Pruned to height ${heightToDelete - 1}`);
}
private async pruneBlock(height: number): Promise {
// 1. 刪除區塊數據文件中的數據
await this.deleteBlockData(height);
// 2. 刪除對應的撤銷數據
await this.deleteUndoData(height);
// 3. 更新索引(保留區塊頭)
await this.updateIndex(height, { pruned: true });
}
}
// 文件級修剪
class FileBasedPruner {
private readonly FILE_SIZE = 128 * 1024 * 1024; // 128 MB
async pruneOldFiles(
currentSize: number,
targetSize: number
): Promise {
let prunedSize = 0;
const files = await this.getBlockFiles();
// 按順序刪除舊文件
for (const file of files) {
if (currentSize - prunedSize <= targetSize) {
break;
}
// 檢查文件中的區塊是否都可以刪除
if (await this.canPruneFile(file)) {
const fileSize = await this.deleteFile(file);
prunedSize += fileSize;
console.log(`Deleted ${file}, freed ${fileSize} bytes`);
}
}
return prunedSize;
}
private async canPruneFile(file: string): Promise {
// 檢查文件中是否有需要保留的區塊
const blocks = await this.getBlocksInFile(file);
const minHeight = await this.getMinKeepHeight();
return blocks.every(b => b.height < minHeight);
}
} 網路影響
區塊服務能力
修剪節點會在 P2P 網路中廣播其能提供的區塊範圍:
服務標誌位:
- NODE_NETWORK (1) : 可以提供完整區塊歷史
- NODE_NETWORK_LIMITED (1024) : 只能提供最近的區塊
修剪節點使用 NODE_NETWORK_LIMITED,表示:
- 可以提供最近 288 個區塊(約 2 天)
- 不能提供更早的區塊
- 仍然可以中繼新交易和區塊 IBD 對等節點選擇
新節點在進行初始區塊下載時,會優先連接具有 NODE_NETWORK 標誌的完整節點,因為修剪節點無法提供完整歷史。
錢包注意事項
新錢包
對於新創建的錢包,修剪節點運作正常。錢包只需要追蹤從創建時間之後的交易。
導入密鑰
警告:導入舊密鑰
如果導入一個有歷史交易的密鑰,修剪節點可能無法找到相關交易:
# 這可能不會找到所有歷史交易
bitcoin-cli importprivkey "your-private-key"
# 解決方案 1:使用 rescanblockchain 掃描保留的區塊
bitcoin-cli rescanblockchain 800000 # 只能掃描保留的範圍
# 解決方案 2:使用完整節點導入
# 或使用外部區塊瀏覽器查看歷史
# 解決方案 3:使用 descriptor 錢包指定出生高度
bitcoin-cli importdescriptors '[{
"desc": "wpkh(your-xpub/0/*)#checksum",
"timestamp": "2023-01-01"
}]' 與其他方案比較
| 方案 | 存儲 | 驗證 | 信任 |
|---|---|---|---|
| 完整節點 | ~600 GB | 完整 | 無需信任 |
| 修剪節點 | ~10 GB | 完整 | 無需信任 |
| SPV 錢包 | ~50 MB | 僅 PoW | 信任節點 |
| 託管錢包 | 0 | 無 | 完全信任 |
結論
修剪節點提供與完整節點相同的安全保證,因為它在刪除數據之前已經驗證了所有內容。 唯一的代價是無法為其他節點提供歷史區塊。
設定指南
全新安裝
# 1. 創建配置文件
mkdir -p ~/.bitcoin
cat > ~/.bitcoin/bitcoin.conf << 'EOF'
# 啟用修剪,保留 2 GB 區塊數據
prune=2000
# 其他推薦設定
server=1
txindex=0 # 修剪模式下必須禁用
EOF
# 2. 啟動節點
bitcoind
# 3. 等待同步完成
bitcoin-cli getblockchaininfo 轉換現有節點
# 1. 停止節點
bitcoin-cli stop
# 2. 編輯配置文件
echo "prune=2000" >> ~/.bitcoin/bitcoin.conf
# 3. 如果之前啟用了 txindex,必須禁用
sed -i 's/txindex=1/txindex=0/' ~/.bitcoin/bitcoin.conf
# 4. 重新啟動
bitcoind
# 節點會在運行時自動開始修剪舊區塊 注意: 如果之前啟用了 txindex,需要在轉換前禁用它,否則節點會報錯。 禁用 txindex 後需要重新建立索引或刪除索引文件。
最佳實踐
✓ 推薦
- • 設定 prune >= 1000(至少 1 GB)
- • 使用 SSD 以獲得更好效能
- • 定期備份錢包文件
- • 監控磁碟空間使用
✗ 避免
- • 不要同時啟用 txindex
- • 不要頻繁導入舊密鑰
- • 不要期望提供歷史區塊
- • 不要設定過小的修剪目標
總結
- ✓ 完整驗證:修剪節點驗證所有交易和區塊,安全性與完整節點相同
- ✓ 低存儲需求:只需約 10 GB 即可運行完整驗證節點
- ✓ 保留 UTXO:所有未花費輸出都被保留,支援正常交易
- ⚠ 有限服務:無法為其他節點提供歷史區塊,無法使用 txindex
已複製連結