高級
Erlay
Erlay (BIP-330):頻寬高效的交易中繼協議
15 分鐘
概述
Erlay 是一種改進的交易中繼協議(BIP-330),旨在大幅減少比特幣節點間 交易公告的頻寬消耗。它結合了傳統洪水式廣播和集合調和技術, 預計可減少約 40% 的交易中繼頻寬。
開發狀態: Erlay 仍在開發中,尚未部署到比特幣主網。 預計將在未來的 Bitcoin Core 版本中推出。
當前問題
傳統交易中繼
當前的交易傳播 (洪水式廣播):
流程:
1. 節點收到新交易
2. 驗證交易有效
3. 向所有連接的節點發送 inv 訊息
4. 對方回覆 getdata 請求
5. 發送完整交易
問題:
┌─────────────────────────────────────────────┐
│ Node A │
│ │ │
│ │ ──inv(txid)──▶ Node B │
│ │ ──inv(txid)──▶ Node C │
│ │ ──inv(txid)──▶ Node D │
│ │ ──inv(txid)──▶ Node E │
│ ... │
│ │
│ 每個節點收到同一交易的多個 inv │
│ 造成大量冗餘 │
└─────────────────────────────────────────────┘
頻寬消耗:
- inv 訊息: 32 bytes × 連接數
- 典型節點: 8-125 個連接
- 每筆交易: 256 - 4000 bytes 僅公告
- 全網約 50% 頻寬用於交易公告 冗餘統計
當前中繼的冗餘分析:
假設:
- 平均 8 個出站連接
- 平均 117 個入站連接
- 每秒約 7 筆交易
每筆交易的 inv 訊息:
- 理論最少: 1 次 (只需收到一次)
- 實際收到: ~8-10 次 (來自不同節點)
- 冗餘率: ~8-10x
頻寬影響:
- inv: 32 bytes × 8 連接 = 256 bytes/tx
- 每秒 7 tx: 1.8 KB/s 僅 inv
- 每月: ~4.7 GB 僅 inv 訊息
這還不包括:
- getdata 請求
- 完整交易傳輸
- 已在 mempool 的重複交易 Erlay 設計
混合方法
Erlay 的兩種模式:
1. 洪水式 (Flooding) - 少數連接
- 只向 8 個出站連接廣播
- 快速傳播
- 保持低延遲
2. 集合調和 (Set Reconciliation) - 多數連接
- 定期與其他連接同步
- 使用 Minisketch
- 大幅減少頻寬
流程:
┌─────────────────────────────────────────────┐
│ 收到新交易 │
│ │ │
│ ├──▶ 洪水廣播給 8 個出站連接 │
│ │ (立即, inv 訊息) │
│ │ │
│ └──▶ 加入調和集合 │
│ (定期, 與其他連接調和) │
└─────────────────────────────────────────────┘
結果:
- 傳播延遲: 略增 (~1-2 秒)
- 頻寬節省: ~40% Minisketch
Minisketch (集合調和庫):
概念:
- 兩個節點各有一組 TXID
- 想知道對方有哪些我沒有的
- 傳統方法: 交換完整列表
- Minisketch: 只交換差異的草圖
工作原理:
1. 每個節點計算其集合的 sketch
2. 交換 sketch (固定大小)
3. 計算 sketch 差異
4. 解碼差異得到缺失的元素
特點:
- 空間效率: O(差異大小), 不是 O(集合大小)
- 誤差容忍: 可以處理估計不準確
- BCH 碼: 基於錯誤更正碼的數學
範例:
Node A 集合: {tx1, tx2, tx3, tx4}
Node B 集合: {tx1, tx2, tx5}
差異: A 有 {tx3, tx4}, B 有 {tx5}
Sketch 大小: ~32 bytes × 3 (差異數量) 協議細節
新訊息類型
Erlay 新增的 P2P 訊息:
1. sendtxrcncl
- 在握手時發送
- 表示支持 Erlay
- 包含版本信息
2. reqtxrcncl
- 請求進行調和
- 發送本地 sketch
3. sketch
- 回覆調和請求
- 包含本地 sketch
4. reconcildiff
- 請求差異中的具體交易
5. reqsketchext
- 請求擴展 sketch (如果解碼失敗)
調和流程:
Node A Node B
│ │
│ ── reqtxrcncl(q) ──▶ │
│ │
│ ◀── sketch(sk_B) ── │
│ │
│ (計算 sk_A XOR sk_B) │
│ (解碼差異) │
│ │
│ ── reconcildiff ──▶ │
│ (請求 B 有而 A 沒有的) │
│ │
│ ◀── tx data ─── │
└─────────────────────────┘ 調和時機
何時進行調和:
定時調和:
- 每隔固定時間 (如 1-2 秒)
- 與每個非洪水連接
- 輪流進行避免同時
觸發條件:
- 累積足夠多新交易
- 定時器觸發
- 對方請求
參數調優:
- 調和間隔: 影響延遲和頻寬
- Sketch 大小: 影響成功率
- 洪水連接數: 影響傳播速度
最佳化:
- 動態估計差異大小
- 根據歷史調整 sketch 容量
- 失敗時擴展 sketch TypeScript 實作
簡化的 Sketch
// 簡化的 Minisketch 概念示範
// 實際實現需要使用 libminisketch
class SimpleSketch {
private elements: Set<string> = new Set();
private capacity: number;
constructor(capacity: number) {
this.capacity = capacity;
}
// 添加元素 (短 TXID)
add(shortTxid: string): void {
this.elements.add(shortTxid);
}
// 計算與另一個 sketch 的差異
// 實際中使用 BCH 碼的數學運算
difference(other: SimpleSketch): {
onlyInThis: string[];
onlyInOther: string[];
} {
const onlyInThis: string[] = [];
const onlyInOther: string[] = [];
for (const elem of this.elements) {
if (!other.elements.has(elem)) {
onlyInThis.push(elem);
}
}
for (const elem of other.elements) {
if (!this.elements.has(elem)) {
onlyInOther.push(elem);
}
}
return { onlyInThis, onlyInOther };
}
// 序列化 (實際中更緊湊)
serialize(): Uint8Array {
// 實際 Minisketch 使用 BCH 碼
// 這裡只是演示
return new TextEncoder().encode(
JSON.stringify([...this.elements])
);
}
}
// 短 TXID 計算 (32-bit)
function computeShortTxid(txid: Uint8Array, salt: Uint8Array): number {
const data = new Uint8Array(txid.length + salt.length);
data.set(txid);
data.set(salt, txid.length);
const hash = sha256(data);
return new DataView(hash.buffer).getUint32(0, true);
} 調和管理器
interface ReconciliationState {
localSet: Set<string>; // 本地待調和的 TXID
lastReconcile: number; // 上次調和時間
estimatedDifference: number; // 估計的差異大小
}
class ErlayManager {
private floodingPeers: Set<Peer> = new Set();
private reconciliationPeers: Map<Peer, ReconciliationState> = new Map();
private reconcileInterval = 2000; // 2 秒
private maxFloodingPeers = 8;
// 選擇洪水廣播的節點
selectFloodingPeers(allPeers: Peer[]): void {
// 優先選擇出站連接
const outbound = allPeers.filter(p => p.isOutbound);
for (const peer of outbound.slice(0, this.maxFloodingPeers)) {
this.floodingPeers.add(peer);
}
}
// 處理新交易
async handleNewTransaction(txid: string): Promise<void> {
// 洪水廣播給選定的節點
for (const peer of this.floodingPeers) {
await peer.send('inv', [{ type: 'tx', hash: txid }]);
}
// 加入其他節點的調和集合
for (const [peer, state] of this.reconciliationPeers) {
state.localSet.add(txid);
}
}
// 定期調和
async reconcileWithPeer(peer: Peer): Promise<void> {
const state = this.reconciliationPeers.get(peer);
if (!state) return;
// 創建本地 sketch
const sketch = new SimpleSketch(state.estimatedDifference * 2);
for (const txid of state.localSet) {
sketch.add(computeShortTxid(hexToBytes(txid), this.salt));
}
// 發送調和請求
await peer.send('reqtxrcncl', {
sketch: sketch.serialize()
});
// 等待回覆並處理
// ...
}
// 處理調和請求
async handleReconciliationRequest(
peer: Peer,
remoteSketch: Uint8Array
): Promise<void> {
const state = this.reconciliationPeers.get(peer);
if (!state) return;
// 計算本地 sketch
const localSketch = new SimpleSketch(100);
for (const txid of state.localSet) {
localSketch.add(computeShortTxid(hexToBytes(txid), this.salt));
}
// 發送本地 sketch
await peer.send('sketch', {
sketch: localSketch.serialize()
});
// 清除已調和的交易
state.localSet.clear();
state.lastReconcile = Date.now();
}
} 優勢分析
Erlay 的主要優勢:
1. 頻寬節省
- 約 40% 減少交易公告頻寬
- 對入站連接多的節點效果更明顯
- 允許增加連接數而不增加成本
2. 更好的連接性
- 節點可以維持更多連接
- 提高網路抗攻擊性
- 減少日蝕攻擊風險
3. 隱私改進
- 調和不暴露交易來源
- 降低交易追蹤難度
- 隨機化傳播路徑
對比:
┌──────────────────────────────────────────────┐
│ 指標 │ 傳統洪水 │ Erlay │
├──────────────────────────────────────────────┤
│ 公告頻寬 │ 100% │ ~60% │
│ 傳播延遲 │ ~3s │ ~4s │
│ 最大連接數 │ ~125 │ ~200+ │
│ 隱私 │ 較差 │ 較好 │
└──────────────────────────────────────────────┘ 權衡與限制
Erlay 的權衡:
1. 延遲增加
- 洪水: 立即傳播
- 調和: 等待下一個調和週期
- 增加約 1-2 秒
- 對大多數用例可接受
2. 複雜性
- 需要維護調和狀態
- Minisketch 算法複雜
- 故障恢復較複雜
3. 內存使用
- 需要存儲待調和集合
- 每個連接額外狀態
- 可以通過設定上限管理
4. 協調成本
- 需要雙方都支持
- 漸進部署期間效果有限
- 完全效益需要廣泛採用
適用場景:
- 高連接數節點: 效益最大
- 頻寬受限環境: 顯著改善
- 時間不敏感應用: 可接受延遲 部署計劃
Erlay 部署狀態:
當前進度:
- BIP-330 已提出
- libminisketch 已開發
- Bitcoin Core PR 進行中
預期時間表:
- 測試網部署: TBD
- 主網啟用: TBD
向後兼容:
- 新節點可以與舊節點通信
- 只有雙方都支持時使用 Erlay
- 不需要共識變更
採用策略:
- 版本位協商
- 可選功能
- 漸進式推廣 相關資源
已複製連結