進階
Sendcmpct Message
了解 P2P 協議中的 sendcmpct 消息,用於協商緊湊區塊中繼模式。
8 分鐘
Sendcmpct(BIP-152)是比特幣 P2P 協議中用於協商緊湊區塊(Compact Blocks) 傳輸的消息。它允許節點選擇高頻寬模式或低頻寬模式,優化區塊傳播效率。
Sendcmpct 概述
Sendcmpct 消息目的:
傳統區塊中繼:
1. 節點 A 發現新區塊
2. A 發送 inv 給 B
3. B 回覆 getdata
4. A 發送完整區塊 (~1-2 MB)
緊湊區塊中繼:
1. 節點 A 發現新區塊
2. A 直接發送緊湊區塊 (~20 KB)
3. B 重建區塊(使用 mempool 中的交易)
4. 如果缺少交易,請求補充
優勢:
- 減少 95%+ 的頻寬
- 加快區塊傳播
- 減少孤立區塊
Sendcmpct 作用:
- 協商使用緊湊區塊
- 選擇高/低頻寬模式
- 指定協議版本 消息格式
Sendcmpct 消息結構:
┌─────────────────────────────────────────┐
│ announce (1 byte, bool) │
│ version (8 bytes, uint64_t) │
└─────────────────────────────────────────┘
欄位說明:
announce:
- 0: 低頻寬模式 (Low Bandwidth)
- 1: 高頻寬模式 (High Bandwidth)
version:
- 1: 原始緊湊區塊
- 2: 支持 SegWit
消息範例:
高頻寬模式,版本 2:
01 02 00 00 00 00 00 00 00
低頻寬模式,版本 2:
00 02 00 00 00 00 00 00 00
完整 P2P 消息:
┌──────────────────────────────────────────────┐
│ magic (4 bytes) │
│ command: "sendcmpct" (12 bytes, padded) │
│ length: 9 (4 bytes) │
│ checksum (4 bytes) │
│ payload: announce + version (9 bytes) │
└──────────────────────────────────────────────┘ 高頻寬 vs 低頻寬模式
兩種模式的區別:
高頻寬模式 (announce = 1):
┌─────────┐ cmpctblock ┌─────────┐
│ Node A │ ──────────────→ │ Node B │
└─────────┘ └─────────┘
- A 收到新區塊後立即發送 cmpctblock
- 不等待 B 的請求
- 最低延遲
- 可能浪費頻寬(如果 B 已有區塊)
低頻寬模式 (announce = 0):
┌─────────┐ inv ┌─────────┐
│ Node A │ ──────────────→ │ Node B │
│ │ getdata(cmpct) │ │
│ │ ←────────────── │ │
│ │ cmpctblock │ │
│ │ ──────────────→ │ │
└─────────┘ └─────────┘
- A 先發送 inv
- B 請求緊湊區塊
- 節省頻寬
- 多一次往返延遲
選擇策略:
- 只對少數幾個節點使用高頻寬(通常 3 個)
- 其他節點使用低頻寬
- 礦工節點優先使用高頻寬 協議版本
緊湊區塊協議版本:
Version 1:
- BIP-152 原始版本
- 不支持 SegWit
- 使用 txid 識別交易
Version 2:
- 支持 SegWit
- 使用 wtxid 識別交易
- 包含見證數據
版本協商:
1. A 發送 sendcmpct(announce=1, version=2)
2. B 發送 sendcmpct(announce=0, version=2)
3. 雙方使用共同支持的最高版本
Bitcoin Core 行為:
- 只對高頻寬連接發送 version 2
- 低頻寬連接也支持 version 2
- 不再使用 version 1(SegWit 已啟用)
// 當前所有節點都應該使用 version 2 協商流程
完整的緊湊區塊協商:
連接建立後:
┌─────────┐ ┌─────────┐
│ Node A │ version │ Node B │
│ │ ──────────────→ │ │
│ │ verack │ │
│ │ ←────────────── │ │
│ │ version │ │
│ │ ←────────────── │ │
│ │ verack │ │
│ │ ──────────────→ │ │
│ │ │ │
│ │ sendcmpct(1,2) │ (HB) │
│ │ ──────────────→ │ │
│ │ sendcmpct(0,2) │ (LB) │
│ │ ←────────────── │ │
└─────────┘ └─────────┘
說明:
- A 請求 B 以高頻寬模式發送
- B 請求 A 以低頻寬模式發送
- 雙方獨立選擇發送模式
- 可以隨時更新偏好
更新偏好:
- 可以多次發送 sendcmpct
- 最新的消息覆蓋之前的
- 用於動態調整 高頻寬節點選擇
Bitcoin Core 的高頻寬策略:
選擇標準:
1. 最多 3 個高頻寬連接
2. 優先選擇最快發送區塊的節點
3. 動態調整
實現:
class BlockRelay {
std::set<NodeId> high_bandwidth_peers;
const size_t MAX_HB_PEERS = 3;
void UpdateHBPeers() {
// 排名最快的區塊提供者
auto ranked = RankPeersBySpeed();
// 選擇前 3 個
high_bandwidth_peers.clear();
for (int i = 0; i < MAX_HB_PEERS && i < ranked.size(); i++) {
high_bandwidth_peers.insert(ranked[i]);
SendMessage(ranked[i], sendcmpct(true, 2));
}
// 其他設為低頻寬
for (auto peer : all_peers) {
if (!high_bandwidth_peers.count(peer)) {
SendMessage(peer, sendcmpct(false, 2));
}
}
}
};
評估指標:
- 區塊到達時間
- 連接延遲
- 成功重建率 與緊湊區塊的關係
Sendcmpct 啟用的消息流程:
高頻寬模式:
1. sendcmpct(1, 2) -- 協商
2. cmpctblock -- 緊湊區塊
3. getblocktxn -- 請求缺失交易(如果需要)
4. blocktxn -- 補充交易
低頻寬模式:
1. sendcmpct(0, 2) -- 協商
2. inv -- 區塊通知
3. getdata(MSG_CMPCT_BLOCK) -- 請求緊湊區塊
4. cmpctblock -- 緊湊區塊
5. getblocktxn -- 請求缺失交易(如果需要)
6. blocktxn -- 補充交易
相關消息類型:
- sendcmpct: 協商緊湊區塊
- cmpctblock: 緊湊區塊數據
- getblocktxn: 請求缺失交易
- blocktxn: 補充交易數據 調試與監控
# 監控緊湊區塊狀態
# 查看節點連接的緊湊區塊狀態
bitcoin-cli getpeerinfo
[
{
"id": 1,
"addr": "...",
"services": "...",
"bip152_hb_to": true, // 對方請求高頻寬
"bip152_hb_from": false, // 我們請求低頻寬
...
}
]
# 日誌中的緊湊區塊消息
# bitcoin.conf
debug=cmpctblock
# 日誌輸出:
# "Requesting cmpctblock from peer=5"
# "Reconstructed block with 2 missing txs"
# "Successfully reconstructed block"
# 統計信息
bitcoin-cli getpeerinfo | jq '.[] | {
id: .id,
hb_to: .bip152_hb_to,
hb_from: .bip152_hb_from
}' 實現細節
Bitcoin Core 中的實現:
// 發送 sendcmpct
void PeerManager::SendCompactBlockAnnouncement(CNode& node, bool high_bandwidth) {
uint64_t version = 2; // SegWit 版本
connman.PushMessage(&node,
CNetMsgMaker(node.GetCommonVersion())
.Make(NetMsgType::SENDCMPCT, high_bandwidth, version));
node.m_bip152_highbandwidth_to = high_bandwidth;
}
// 處理收到的 sendcmpct
void PeerManager::ProcessSendCmpct(CNode& node, CDataStream& vRecv) {
bool announce;
uint64_t version;
vRecv >> announce >> version;
// 驗證版本
if (version < 1 || version > 2) {
return;
}
// 記錄對方的偏好
node.m_bip152_highbandwidth_from = announce;
node.m_bip152_version = std::min(version, (uint64_t)2);
}
// 發送區塊時檢查
void PeerManager::SendBlockAnnouncement(const CBlock& block) {
for (auto& node : connman.GetNodes()) {
if (node.m_bip152_highbandwidth_from) {
// 高頻寬: 直接發送緊湊區塊
SendCmpctBlock(node, block);
} else {
// 低頻寬: 發送 inv
SendInv(node, block.GetHash());
}
}
} 相關概念
- Compact Blocks:緊湊區塊
- Message Types:P2P 消息類型
- Block Relay:區塊中繼
- Block Download:區塊下載
- P2P Protocol:點對點協議
已複製連結