跳至主要內容
進階

Feefilter Message

了解 P2P 協議中的 feefilter 消息,減少低費率交易的無效中繼。

8 分鐘

Feefilter(BIP-133)是比特幣 P2P 協議中的消息類型,允許節點告知對等節點 自己的最低費率要求,從而減少無法被接受的交易的中繼,節省網路頻寬。

Feefilter 概述

Feefilter 消息目的:

問題:
1. 節點 A 廣播交易給節點 B
2. 交易費率低於 B 的 mempool 最低要求
3. B 拒絕交易
4. 頻寬被浪費

解決方案:
1. B 發送 feefilter 給 A
2. 告知 A 自己的最低費率
3. A 不再發送低於此費率的交易
4. 節省雙方頻寬

BIP-133 定義:
- 2016 年引入
- Bitcoin Core 0.13.0 實現
- 現在廣泛支持

消息格式

Feefilter 消息結構:

┌─────────────────────────────────────────┐
│ feerate (8 bytes, little-endian)        │
└─────────────────────────────────────────┘

feerate:
- 單位: satoshis per 1000 bytes
- 類型: uint64_t
- 範例: 1000 = 1 sat/vB

消息範例:
feerate = 1000 (0x00000000000003e8)

十六進制:
e8 03 00 00 00 00 00 00

這表示:
- 最低費率 1000 sat/kvB
- 等於 1 sat/vB
- 低於此費率的交易不要發送

完整 P2P 消息:
┌──────────────────────────────────────────────┐
│ magic (4 bytes)                              │
│ command: "feefilter" (12 bytes, padded)      │
│ length: 8 (4 bytes)                          │
│ checksum (4 bytes)                           │
│ payload: feerate (8 bytes)                   │
└──────────────────────────────────────────────┘

何時發送 Feefilter

觸發 Feefilter 的情況:

1. 連接建立後
   - 初始握手完成
   - 發送當前的最低費率
   - 通常接近 0(mempool 未滿)

2. Mempool 達到容量
   - 開始驅逐低費率交易
   - 最低費率上升
   - 發送更新的 feefilter

3. Mempool 清空
   - 區塊確認後
   - 最低費率下降
   - 發送更新的 feefilter

4. 配置變更
   - minrelaytxfee 改變
   - 手動調整閾值

Bitcoin Core 的策略:
- 避免頻繁發送
- 費率變化超過閾值才更新
- 防止洩露 mempool 狀態

接收方處理

收到 Feefilter 後的處理:

1. 記錄對等節點的費率要求
peer.feefilter = received_feerate

2. 過濾出站交易
for tx in pending_relay:
    if tx.feerate < peer.feefilter:
        skip_relay(tx, peer)
    else:
        relay(tx, peer)

3. 動態調整
- 對方可能多次發送 feefilter
- 總是使用最新的值
- 0 表示無限制

實現範例:
class Peer:
    def __init__(self):
        self.fee_filter = 0  # 初始無限制

    def on_feefilter(self, feerate):
        self.fee_filter = feerate

    def should_relay_tx(self, tx):
        if self.fee_filter == 0:
            return True
        return tx.get_feerate() >= self.fee_filter

費率計算

費率單位與轉換:

Feefilter 使用:
- satoshis per 1000 virtual bytes (sat/kvB)

常見表示:
- sat/vB (satoshis per virtual byte)
- BTC/kvB (比特幣 per 1000 virtual bytes)

轉換:
1 sat/vB = 1000 sat/kvB
1 BTC/kvB = 100,000,000 sat/kvB = 100,000 sat/vB

範例:
feefilter = 1000 → 1 sat/vB
feefilter = 5000 → 5 sat/vB
feefilter = 10000 → 10 sat/vB

計算交易費率:
tx_feerate = (tx_fee_in_sats × 1000) / tx_vsize

範例:
tx_fee = 500 sats
tx_vsize = 250 vB
tx_feerate = (500 × 1000) / 250 = 2000 sat/kvB = 2 sat/vB

比較:
if tx_feerate >= feefilter:
    relay_allowed = True

隱私考量

Feefilter 的隱私影響:

問題:
- Feefilter 洩露 mempool 狀態
- 可以推斷節點的交易量
- 可能被用於指紋識別

緩解措施:

1. 量化費率
   - 不發送精確值
   - 使用離散的費率級別
   - 減少信息洩露

2. 延遲更新
   - 不立即響應 mempool 變化
   - 批量更新
   - 添加隨機延遲

3. 最大/最小限制
   - 設置合理的範圍
   - 避免極端值

Bitcoin Core 實現:
// 費率量化
CFeeRate GetMinFeeFilter() {
    CFeeRate rate = ...;
    // 向上取整到特定精度
    return rate.RoundUp(...);
}

// 更新頻率限制
if (now - last_feefilter_sent < MIN_FEEFILTER_INTERVAL) {
    return;  // 不要太頻繁發送
}

與其他機制的關係

Feefilter 與相關機制:

1. minrelaytxfee
   - 節點的靜態最低中繼費率
   - 配置文件設置
   - Feefilter 可能更高(mempool 滿時)

2. mempoolminfee
   - Mempool 的動態最低費率
   - 根據容量調整
   - Feefilter 基於此值

3. incrementalrelayfee
   - RBF 替換的增量要求
   - 與 feefilter 獨立

關係:
feefilter >= max(minrelaytxfee, mempoolminfee)

配置選項:
# bitcoin.conf
minrelaytxfee=0.00001  # 1 sat/vB
maxmempool=300  # MB

命令查詢:
bitcoin-cli getmempoolinfo
{
  "mempoolminfee": 0.00001,
  "minrelaytxfee": 0.00001,
  ...
}

調試與監控

# 監控 feefilter 活動

# 查看節點的 mempool 狀態
bitcoin-cli getmempoolinfo
{
  "loaded": true,
  "size": 5000,
  "bytes": 2500000,
  "usage": 8000000,
  "total_fee": 0.5,
  "maxmempool": 300000000,
  "mempoolminfee": 0.00001,
  "minrelaytxfee": 0.00001
}

# 查看對等節點信息
bitcoin-cli getpeerinfo
[
  {
    "id": 1,
    "minfeefilter": 0.00001000,
    ...
  }
]

# 日誌中的 feefilter 消息
# ~/.bitcoin/debug.log
# grep feefilter debug.log

# 設置日誌級別
# bitcoin.conf
debug=net

# 輸出範例:
# "Received feefilter of 0.00001000 from peer=5"
# "Sending feefilter of 0.00002000 to peer=3"

實現細節

Bitcoin Core 中的實現:

// 發送 feefilter
void PeerManager::MaybeSendFeefilter(CNode& node) {
    if (!node.IsAddrRelayPeer()) return;

    CAmount currentFilter = ...;  // 計算當前過濾器值

    // 檢查是否需要更新
    if (currentFilter != node.m_fee_filter_sent) {
        if (/* 過濾器變化足夠大 */) {
            connman.PushMessage(&node,
                CNetMsgMaker(node.GetCommonVersion())
                    .Make(NetMsgType::FEEFILTER, currentFilter));
            node.m_fee_filter_sent = currentFilter;
        }
    }
}

// 處理收到的 feefilter
void PeerManager::ProcessFeefilter(CNode& node, CDataStream& vRecv) {
    CAmount newFeeFilter;
    vRecv >> newFeeFilter;

    // 驗證
    if (newFeeFilter < 0 || newFeeFilter > MAX_MONEY) {
        return;  // 無效值
    }

    node.m_fee_filter_received = newFeeFilter;
}

// 中繼交易時檢查
bool PeerManager::ShouldRelayTx(const CTransaction& tx, CNode& node) {
    CFeeRate txFeeRate = GetTxFeeRate(tx);
    return txFeeRate.GetFeePerK() >= node.m_fee_filter_received;
}

相關概念

  • Message Types:P2P 消息類型
  • Mempool Policy:交易池策略
  • Transaction Fees:交易手續費
  • Fee Estimation:費用估算
  • Mempool Eviction:交易驅逐
已複製連結
已複製到剪貼簿