進階
WTXID
了解 Witness Transaction ID 的概念,以及它如何解決 SegWit 交易的唯一識別問題。
10 分鐘
WTXID(Witness Transaction ID)是 SegWit 引入的交易識別碼,它對整個交易 (包括見證數據)進行雜湊計算,提供了交易的完整標識。
TXID vs WTXID
在 SegWit 之前,交易只有一個識別碼 TXID。SegWit 引入了新的序列化格式, 需要兩種不同的識別方式:
| 特性 | TXID | WTXID |
|---|---|---|
| 計算範圍 | 不含見證數據 | 含見證數據 |
| 用於 | 交易引用、outpoint | 區塊 Merkle 樹 |
| 可延展性 | 不可變 | 見證可變則變 |
| Legacy TX | TXID = WTXID | 相同 |
計算方式
// TXID 計算(不含見證數據)
TXID = SHA256(SHA256(
version ||
inputs ||
outputs ||
locktime
))
// WTXID 計算(含見證數據)
WTXID = SHA256(SHA256(
version ||
marker ||
flag ||
inputs ||
outputs ||
witness ||
locktime
)) 交易序列化格式
SegWit 交易有兩種序列化格式:
// 傳統格式(用於 TXID 計算)
[version][input count][inputs][output count][outputs][locktime]
// 見證格式(用於 WTXID 計算和網路傳輸)
[version][marker=0x00][flag=0x01][input count][inputs]
[output count][outputs][witness][locktime] marker 和 flag:marker 為 0x00,flag 為 0x01。這個組合不會與 傳統交易混淆,因為傳統交易的 input count 不可能為 0。
Witness Merkle Tree
區塊中有兩棵 Merkle 樹:傳統的 TXID 樹和 WTXID 樹。
區塊頭
├── merkle_root (TXID 樹的根)
│ ├── coinbase_txid
│ ├── tx1_txid
│ └── tx2_txid
│
Coinbase 的 OP_RETURN
└── witness_commitment
└── witness_merkle_root (WTXID 樹的根)
├── 0x00...00 (coinbase 的 WTXID 固定為 0)
├── tx1_wtxid
└── tx2_wtxid Witness Commitment
WTXID Merkle 根被嵌入在 coinbase 交易的輸出中:
// Witness commitment 結構
OP_RETURN
0xaa21a9ed // commitment 標識符
SHA256(SHA256(witness_root || witness_reserved_value))
// 驗證邏輯
uint256 ComputeWitnessMerkleRoot(const std::vector& vtx)
{
std::vector leaves;
leaves.push_back(uint256()); // coinbase WTXID = 0
for (size_t i = 1; i < vtx.size(); i++) {
leaves.push_back(vtx[i]->GetWitnessHash());
}
return ComputeMerkleRoot(leaves);
} RPC 查詢
# 獲取交易的 TXID 和 WTXID
bitcoin-cli getrawtransaction <txid> true
# 返回結果包含
{
"txid": "abc123...", // TXID
"hash": "def456...", // WTXID(在有見證數據時不同)
"wtxid": "def456...", // 同上,顯式字段
...
}
# 解碼原始交易
bitcoin-cli decoderawtransaction <hex>
# 獲取區塊的 witness commitment
bitcoin-cli getblock <blockhash> 2 | jq '.tx[0].vout[] | select(.scriptPubKey.asm | contains("OP_RETURN"))' 編程示例
import hashlib
def double_sha256(data):
return hashlib.sha256(hashlib.sha256(data).digest()).digest()
def compute_txid(tx_bytes):
"""計算 TXID(不含見證數據)"""
# 解析並移除見證數據
stripped = strip_witness(tx_bytes)
return double_sha256(stripped)[::-1].hex()
def compute_wtxid(tx_bytes):
"""計算 WTXID(含見證數據)"""
return double_sha256(tx_bytes)[::-1].hex()
# 對於非 SegWit 交易
# TXID == WTXID
# 對於 SegWit 交易
# TXID != WTXID(因為見證數據) 使用場景
- 交易引用:使用 TXID(保持向後兼容)
- 區塊驗證:驗證兩棵 Merkle 樹
- 交易中繼:INV 訊息可使用 WTXID
- BIP-339 wtxidrelay:節點可以宣告使用 WTXID 進行交易中繼
BIP-339: WTXID Relay
BIP-339 引入了基於 WTXID 的交易中繼,提高了效率並減少了頻寬浪費:
// 握手時宣告支持
version message
-> services: NODE_WITNESS
wtxidrelay message (空訊息,在 verack 之前發送)
-> 表示將使用 WTXID 進行 INV/GETDATA
// 交易中繼
INV: MSG_WTX (0x05) + wtxid
GETDATA: MSG_WTX (0x05) + wtxid 為什麼需要兩種 ID?
保留 TXID 是為了向後兼容:
- 現有錢包和交易所使用 TXID 追蹤交易
- outpoint(輸入引用)使用 TXID
- 鏈上合約可能依賴 TXID
- WTXID 用於新的見證相關功能
已複製連結