高級
Taproot Leaf Versions
了解 Taproot 腳本樹的葉子版本機制,實現未來腳本升級的軟分叉兼容性。
10 分鐘
Taproot 葉子版本(Leaf Version)是嵌入在腳本樹葉子中的版本號, 允許在不改變輸出格式的情況下引入新的腳本語義,為未來升級提供軟分叉路徑。
葉子版本概述
Taproot 葉子版本機制:
TapLeaf 結構:
┌─────────────────────────────────────────┐
│ leaf_version (1 byte) │
│ script_length (varint) │
│ script (variable) │
└─────────────────────────────────────────┘
葉子雜湊計算:
leaf_hash = tagged_hash("TapLeaf",
leaf_version || compact_size(script) || script)
當前定義的版本:
- 0xc0: TapScript(當前唯一有效版本)
- 其他: 保留給未來升級
版本字節結構:
位 0: 必須為 0(保留給奇偶性)
位 1-7: 實際版本號
有效版本必須是偶數且 <= 0xfe TapScript(版本 0xc0)
TapScript 是當前唯一定義的葉子版本:
版本號: 0xc0 (192)
二進制: 11000000
TapScript 特性:
1. 基於 SegWit v0 腳本
2. 使用 Schnorr 簽名 (64 bytes)
3. OP_CHECKSIGADD 取代 OP_CHECKMULTISIG
4. 無 201 操作碼限制
5. 有 sigops 預算限制
與 SegWit v0 的差異:
相同:
- 大多數操作碼語義相同
- 基本腳本結構相同
改進:
- OP_SUCCESS 操作碼(允許未來升級)
- OP_CHECKSIGADD(高效多簽)
- 簽名格式變更(Schnorr)
- 資源限制不同 控制塊中的版本
控制塊結構:
┌─────────────────────────────────────────┐
│ control_byte (1 byte) │
│ = leaf_version | output_parity │
│ internal_pubkey (32 bytes) │
│ merkle_path (32 bytes × depth) │
└─────────────────────────────────────────┘
control_byte 解析:
leaf_version = control_byte & 0xfe
output_parity = control_byte & 0x01
範例:
control_byte = 0xc0 → version 0xc0, parity 0
control_byte = 0xc1 → version 0xc0, parity 1
驗證過程:
1. 提取 leaf_version
2. 檢查版本是否有效
3. 如果版本未知 → 視為 OP_SUCCESS
4. 如果版本已知 → 按該版本規則執行 OP_SUCCESS 機制
OP_SUCCESS 與葉子版本的關係:
OP_SUCCESS 操作碼:
- 0x50, 0x62, 0x89-0x8a, 0x8d-0x8e
- 0x95-0x99, 0xbb-0xfe
當遇到 OP_SUCCESS:
1. 整個腳本立即成功
2. 不執行任何驗證
3. 允許未來賦予新語義
葉子版本的類似作用:
未知葉子版本:
1. 節點不理解該版本
2. 將整個腳本視為成功
3. 未來軟分叉可定義新規則
這提供了兩層升級路徑:
1. 新操作碼 → 使用 OP_SUCCESS 預留的碼
2. 新腳本語義 → 使用新葉子版本
// 兩者都是軟分叉兼容的 版本號設計
葉子版本的設計考量:
為什麼使用 0xc0 作為初始版本?
歷史原因:
- 早期設計使用不同的編碼
- 0xc0 是經過調整後的結果
- 確保與其他數據的可區分性
版本空間:
有效版本: 偶數 0x00-0xfe
共 128 個可能的版本
預留策略:
- 0xc0: TapScript(當前)
- 0xc2-0xfe: 未來腳本版本
- 0x00-0xbe: 也可用於未來
為什麼要求偶數?
位 0 用於輸出公鑰的 y 座標奇偶性
這允許在控制塊中編碼兩個信息
control_byte = leaf_version | parity
- leaf_version 是偶數(位 0 = 0)
- parity 提供位 0 未來升級場景
使用新葉子版本的升級:
場景 1: 新簽名方案
leaf_version = 0xc2
- 使用後量子密碼學
- 新的簽名驗證規則
- 不影響 0xc0 腳本
場景 2: 新腳本語言
leaf_version = 0xc4
- 完全不同的腳本語義
- 可能是 Simplicity
- 與現有腳本共存
場景 3: 性能優化
leaf_version = 0xc6
- 優化的操作碼集
- 更高效的驗證
- 保持向後兼容
軟分叉過程:
1. 新版本定義新規則
2. 舊節點視未知版本為 OP_SUCCESS
3. 新節點按新規則驗證
4. 軟分叉保持網路共識
// 這種設計使升級更加平滑 腳本樹中的多版本
同一棵樹中可以有不同版本的葉子:
腳本樹結構:
[Root]
/ \
[Branch] [Leaf C: v0xc4]
/ \
[Leaf A] [Leaf B]
v0xc0 v0xc2
Leaf A: TapScript (0xc0)
- 使用當前規則
Leaf B: 未來版本 (0xc2)
- 舊節點視為成功
- 新節點使用新規則
Leaf C: 另一個未來版本 (0xc4)
- 可能是完全不同的語言
使用場景:
- 漸進式遷移到新版本
- 提供備用執行路徑
- 實驗性功能測試
每個葉子的版本獨立:
- 不影響其他葉子
- 不影響 key path
- 完全自包含 驗證實現
Bitcoin Core 中的版本處理:
// 驗證葉子版本
bool VerifyTaprootLeaf(
const CScript& script,
uint8_t leaf_version,
const XOnlyPubKey& internal_key,
const std::vector<uint8_t>& control,
ScriptError* serror)
{
// 檢查版本有效性
if (leaf_version & 1) {
// 奇數版本無效
return set_error(serror, SCRIPT_ERR_TAPROOT_VERSION);
}
// 計算葉子雜湊
uint256 leaf_hash = ComputeTapleafHash(leaf_version, script);
// 驗證 Merkle 證明
if (!VerifyTaprootCommitment(control, internal_key, leaf_hash)) {
return set_error(serror, SCRIPT_ERR_WITNESS_PROGRAM_MISMATCH);
}
// 根據版本執行腳本
if (leaf_version == TAPROOT_LEAF_TAPSCRIPT) {
// 0xc0: 執行 TapScript
return ExecuteTapScript(script, ...);
} else {
// 未知版本: 視為成功(軟分叉預留)
return true;
}
}
// 葉子雜湊計算
uint256 ComputeTapleafHash(uint8_t version, const CScript& script) {
CHashWriter ss(HASHER_TAPLEAF);
ss << version;
ss << script;
return ss.GetSHA256();
} 安全考量
葉子版本的安全性:
1. 版本混淆攻擊
風險: 攻擊者使用錯誤版本提交腳本
緩解: 版本嵌入葉子雜湊,無法篡改
2. 未知版本風險
風險: 使用未知版本可能被任何人花費
緩解:
- 錢包不應創建未知版本
- 僅在軟分叉啟用後使用新版本
3. 軟分叉過渡期
風險: 新舊節點驗證結果不同
緩解:
- 等待足夠確認
- 礦工升級後再使用
最佳實踐:
1. 只使用已知版本(目前只有 0xc0)
2. 不要創建使用保留版本的輸出
3. 驗證錢包軟件正確處理版本
4. 關注軟分叉升級公告 開發者指南
使用葉子版本的最佳實踐:
# Python 範例
TAPROOT_LEAF_TAPSCRIPT = 0xc0
def create_tapleaf(script: bytes, version: int = TAPROOT_LEAF_TAPSCRIPT):
"""創建 TapLeaf"""
if version & 1:
raise ValueError("Leaf version must be even")
if version > 0xfe:
raise ValueError("Leaf version out of range")
# 目前只支持 TapScript
if version != TAPROOT_LEAF_TAPSCRIPT:
raise ValueError("Only TapScript (0xc0) is currently defined")
return {
'version': version,
'script': script
}
def compute_tapleaf_hash(leaf):
"""計算葉子雜湊"""
version = leaf['version']
script = leaf['script']
# TapLeaf tagged hash
data = bytes([version]) + compact_size(script) + script
return tagged_hash("TapLeaf", data)
def create_control_block(internal_pubkey, leaf_version, merkle_path, output_parity):
"""創建控制塊"""
control_byte = leaf_version | output_parity
return bytes([control_byte]) + internal_pubkey + b''.join(merkle_path) 相關概念
- Script Path:腳本路徑花費
- Taproot Annex:附加數據欄位
- Tagged Hashes:標籤雜湊函數
- Soft Fork Activation:軟分叉啟用
- OP_SUCCESS:成功操作碼
已複製連結