閃電網路路由機制完全指南

深入解析閃電網路的路由演算法、費用計算、流動性管理與隱私保護機制。

閃電網路路由機制完全指南

概述

閃電網路(Lightning Network)是比特幣的第二層擴容解決方案,旨在實現比特幣的低成本、即時支付。而路由機制則是閃電網路中最核心且最複雜的技術挑戰之一。本篇文章將深入解析閃電網路的路由演算法、費用計算、流動性管理與隱私保護機制。


一、閃電網路基礎架構回顧

1.1 通道與支付通道

閃電網路構建於比特幣區塊鏈之上,透過「支付通道」(Payment Channel)實現鏈下交易。支付通道的核心概念是:

支付通道的關鍵特點:

特性說明
雙向支付資金可在兩方向流動
狀態更新每次交易產生新的餘額狀態
離鏈交易大部分交易發生在比特幣主鏈之外
最終性通道關閉後,主鏈結算具有最終性

1.2 HTLC 技術原理

哈希時間鎖合約(Hash Time Locked Contract,HTLC)是閃電網路實現跨通道支付的關鍵技術。HTLC 的核心機制包含兩個鎖:

哈希鎖(Hash Lock)

時間鎖(Time Lock)

HTLC 的 Go 語言偽代碼實現:

// 創建 HTLC
type HTLC struct {
    Amount        satoshi
    Hash          [32]byte      // 預圖像的哈希值
    Expiry        uint32        // 過期區塊高度
    Receiver      PublicKey     // 收款方公鑰
}

// 索取值
func (h *HTLC) Claim(preimage [32]byte, signature Signature) bool {
    // 驗證哈希
    if sha256(preimage) != h.Hash {
        return false
    }
    // 驗證簽名
    return verifySignature(h.Receiver, signature, h.Preimage)
}

// 退款
func (h *HTLC) Refund(senderSignature Signature) bool {
    // 檢查是否過期
    if currentBlockHeight < h.Expiry {
        return false
    }
    return verifySignature(h.Sender, senderSignature, h.Refund)
}

二、路由演算法深度解析

2.1 路由問題的複雜性

閃電網路的路由面臨獨特的技術挑戰:

  1. 網路拓撲動態變化:通道會頻繁開關,餘額會實時變化
  2. 隱私要求:不應向路徑上的節點暴露完整支付路徑
  3. 規模問題:數萬個節點和通道,計算複雜度極高
  4. 流動性約束:資金可能被困在特定通道中

2.2 基礎路徑搜索:BFS 與 Dijkstra

最基礎的路由演算法是廣度優先搜索(BFS)和 Dijkstra 最短路徑演算法:

BFS 特性

Dijkstra 特性

# 簡化的 Dijkstra 實現
import heapq
from typing import Dict, List, Tuple

def dijkstra_routing(
    graph: Dict[str, List[Tuple[str, int, int]]],
    source: str,
    target: str,
    amount: int
) -> Tuple[List[str], int]:
    """
    基礎路由搜索

    Args:
        graph: 鄰接表 {節點: [(相鄰節點, 容量, 費用)]}
        source: 起始節點
        target: 目標節點
        amount: 支付的金額

    Returns:
        (路徑, 總費用)
    """
    # 優先隊列: (費用, 節點, 路徑)
    pq = [(0, source, [source])]
    visited = set()

    while pq:
        cost, node, path = heapq.heappop(pq)

        if node in visited:
            continue
        visited.add(node)

        if node == target:
            return path, cost

        for neighbor, capacity, fee in graph.get(node, []):
            if capacity >= amount:
                # 計算節點費用(基於固定費率 + 比例費)
                node_fee = max(1, amount // 1000) + fee
                new_cost = cost + node_fee

                heapq.heappush(pq, (
                    new_cost,
                    neighbor,
                    path + [neighbor]
                ))

    return [], 0  # 無法找到路徑

2.3 閃電網路的專屬路由:液態路由(Liquid Routing)

閃電網路實際使用的路由機制比基礎 Dijkstra 更為複雜,主要因為:

  1. 餘額隱私:節點不會公開通道的具體餘額
  2. 雙向費用:費用在兩個方向可能不同
  3. 時間價值:延遲會影響支付的可行性

液態路由的核心思想

液態路由(最初稱為「Flare」)是一種混合式路由方案,結合了局部拓撲信息和全局路徑搜索:

# 液態路由的關鍵參數

LIQUID_ROUTING_PARAMS = {
    # 局部視圖半徑
    "local_view_radius": 8,

    # 路徑探索深度
    "max_path_length": 20,

    # 每次探索的候選路徑數量
    "num_candidates": 3,

    # 探測失敗後的重試次數
    "probe_retries": 2,

    # 費用估算的衰減因子
    "fee_decay_factor": 0.95,

    # 歷史數據的時間窗口(秒)
    "history_window": 3600,
}

2.4 現代路由:Erlik 和 Keysend

Erlik 路由

Erlik 是近年提出的改進路由方案,核心特點:

  1. 分層搜索:先搜索近距離節點,再擴展到遠程
  2. 概率性選擇:使用概率分佈選擇下一跳,而非確定性最短路徑
  3. 適應性費用:根據網路擁塞程度動態調整費用
import random

class ErlikRouter:
    def __init__(self, graph, local_radius=6):
        self.graph = graph
        self.local_radius = local_radius

    def find_path(self, source, target, amount):
        """使用 Erlik 演算法尋找路徑"""
        # 階段 1:本地搜索
        local_path = self._local_search(source, target, amount)
        if local_path:
            return local_path

        # 階段 2:遠程搜索
        return self._extended_search(source, target, amount)

    def _local_search(self, source, target, amount):
        """在本地半徑內搜索"""
        visited = {source}
        path = [source]

        while len(path) <= self.local_radius:
            current = path[-1]
            if current == target:
                return path

            # 獲取候選下一跳
            candidates = self._get_candidates(current, amount)
            candidates = [c for c in candidates if c not in visited]

            if not candidates:
                break

            # 概率性選擇(傾向於費用低的節點)
            next_node = self._probabilistic_select(candidates)
            visited.add(next_node)
            path.append(next_node)

        return None

    def _probabilistic_select(self, candidates):
        """基於費用的概率性選擇"""
        costs = [self._estimate_fee(c, amount) for c in candidates]
        # 轉換為權重(費用越低,權重越高)
        weights = [1 / (c + 1) for c in costs]
        total = sum(weights)
        probs = [w / total for w in weights]

        return random.choices(candidates, weights=probs)[0]

Keysend(自發支付)

Keysend 是閃電網路的擴展功能,允許不透過發票(invoice)進行支付:

Keysend 的消息結構:

// keysend 消息格式
message Keysend {
    // 目標節點的公鑰
    bytes destination = 1;

    // 支付的金額(聰)
    uint64 amount = 2;

    // 付款方的公鑰(可選,用於匿名支付)
    bytes source = 3;

    // 預圖像(由付款方生成)
    bytes preimage = 4;

    // 路由提示(可選)
    repeated RouteHint route_hints = 5;

    // 自定義數據(可選)
    map<string, bytes> custom_records = 6;
}

三、費用計算機制

3.1 費用結構

閃電網路的費用結構比比特幣主鏈複雜,包含兩個組成部分:

基礎費用(Base Fee)

比例費用(Proportional Fee)

總費用計算公式

total_fee = base_fee + (amount * proportional_fee / 1,000,000)

3.2 費用市場動態

閃電網路的費用市場遵循供需原理:

  1. 通道費用差異:繁忙通道收取較高費用
  2. 流動性收費:資金流向緊缺方向需要支付溢價
  3. 時間價值:緊急支付願意支付更高費用
# 動態費用計算模型

class DynamicFeeManager:
    def __init__(self, base_fee=1, ppm=1):
        self.base_fee = base_fee
        self.ppm = ppm

        # 費用調整參數
        self.target_utilization = 0.5  # 目標通道利用率
        self.fee_multiplier_range = (0.5, 5.0)  # 費用倍數範圍

    def calculate_fee(self, channel, amount):
        """計算單通道費用"""
        # 計算當前利用率
        utilization = amount / channel.capacity

        # 根據利用率調整費用
        if utilization > self.target_utilization:
            # 利用率高,提高費用
            multiplier = 1 + (utilization - self.target_utilization) * 10
        else:
            # 利用率低,降低費用
            multiplier = 1 - (self.target_utilization - utilization) * 2

        # 限制費用倍數範圍
        multiplier = max(
            self.fee_multiplier_range[0],
            min(multiplier, self.fee_multiplier_range[1])
        )

        # 計算最終費用
        base = self.base_fee * multiplier
        proportional = (amount * self.ppm / 1_000_000) * multiplier

        return int(base + proportional)

3.3 路徑總費用優化

選擇最佳路徑時,需要平衡費用和成功率:

def optimize_path_fee(graph, source, target, amount, success_prob=0.95):
    """
    費用優化路徑搜索

    考慮因素:
    - 直接費用
    - 成功概率(基於歷史數據)
    - 時間價值
    """
    paths = find_all_paths(graph, source, target, max_hops=10)

    best_path = None
    best_score = float('inf')

    for path in paths:
        path_fee = calculate_path_fee(path, amount)
        path_success = estimate_success_probability(path, amount)

        # 考慮成功概率的調整費用
        adjusted_fee = path_fee / (path_success ** 2)

        if adjusted_fee < best_score:
            best_score = adjusted_fee
            best_path = path

    return best_path, best_score

四、流動性管理

4.1 流動性問題的本質

閃電網路的流動性問題源於雙向通道的設計:

4.2 流動性類型

類型說明管理難度
流入流動性收到的資金容量中等
流出流動性支付的資金容量困難
本地流動性通道內的雙向容量簡單
遠程流動性透過通道可達的資金複雜

4.3 循環流動性(Circular Liquidity)

循環流動性是一種重要的流動性管理技術:

def find_circular_rebalance(graph, source_node, amount):
    """
    尋找循環路徑以重新平衡流動性

    目標:將資金從 source_node 的「過多」方向轉移到「不足」方向
    """
    # 步驟 1:分析 source_node 的所有通道餘額
    channels = get_node_channels(source_node)

    # 分類通道
    inbound_excess = []  # 資金過多(需要輸出)
    outbound_deficit = []  # 資金不足(需要輸入)

    for channel in channels:
        local_balance = channel.local_balance
        remote_balance = channel.remote_balance

        if local_balance > remote_balance * 1.5:
            # 本地餘額遠大於遠程,資金過多
            inbound_excess.append(channel)
        elif remote_balance > local_balance * 1.5:
            # 遠程餘額大於本地,資金不足
            outbound_deficit.append(channel)

    # 步驟 2:尋找連接兩類通道的路徑
    circular_paths = []

    for excess_channel in inbound_excess:
        for deficit_channel in outbound_deficit:
            # 尋找從 excess_channel.peer 到 deficit_channel.peer 的路徑
            path = find_path(
                graph,
                excess_channel.peer,
                deficit_channel.peer,
                amount
            )

            if path:
                full_path = [
                    source_node,
                    excess_channel.peer,
                    *path,
                    deficit_channel.peer,
                    source_node
                ]
                circular_paths.append(full_path)

    # 步驟 3:選擇最優路徑
    return select_best_circular_path(circular_paths, amount)

4.4 流動性廣告與市場

閃電網路正在發展流動性市場:

流動性廣告

流動性租賃

// 流動性廣告消息
message LiquidityAd {
    // 廣告ID
    uint64 id = 1;

    // 節點公鑰
    bytes node_pubkey = 2;

    // 最小金額
    uint64 min_amount = 3;

    // 最大金額
    uint64 max_amount = 4;

    // 費用率(ppm)
    uint32 fee_rate_ppm = 5;

    // 有效期(秒)
    uint32 validity = 6;

    // 簽名
    bytes signature = 7;
}

五、隱私保護機制

5.1 洋蔥路由(Onion Routing)

閃電網路使用類似 Tor 的洋蔥路由技術保護隱私:

# 洋蔥路由的封裝結構

class OnionPacket:
    def __init__(self, route, amount, preimage):
        self.version = 0
        self.route = route
        self.amount = amount
        self.preimage = preimage
        self.hops = []

        # 構建每跳的加密信息
        self._build_hops()

    def _build_hops(self):
        """從目標向起點構建洋蔥"""
        # 最後一跳的信息
        last_hop = {
            'amt_to_forward': self.amount,
            'outgoing_cltv_value': calculate_expiry(),
            'payment_data': self.preimage  # 只在最後一跳揭示
        }

        encrypted_data = encrypt_hop_data(last_hop, self.route[-1].key)

        # 向前構建每跳
        for i in range(len(self.route) - 2, -1, -1):
            hop_data = {
                'amt_to_forward': self.amount,
                'outgoing_cltv_value': calculate_expiry(),
                'encrypted_data': encrypted_data,
                'short_channel_id': self.route[i].channel_id
            }

            hop_key = self.route[i].key
            encrypted_data = encrypt_hop_data(hop_data, hop_key)

            self.hops.append({
                'pubkey': self.route[i].pubkey,
                'encrypted_payload': encrypted_data
            })

        self.hops.reverse()

5.2 PTLC 與 Taproot 隱私

匿名化支付(PTLC)取代 HTLC 是閃電網路隱私的重要改進:

HTLC 的隱私問題

PTLC 的改進

# PTLC 的關鍵實現

import ecp256k1dsa
from sec import G, Point

class PTLC:
    def __init__(self):
        self.curve = ecdsa.SECP256K1

    def create_ptlc(self, amount, recipient_pubkey):
        """創建 PTLC"""
        # 1. 生成隨機標量作為預圖像
        preimage = secrets.randbelow(self.curve.order)

        # 2. 計算預圖像的橢圓曲線點
        preimage_point = preimage * G

        # 3. 計算目標節點的共享密鑰
        shared_secret = self._derive_shared_secret(
            recipient_pubkey,
            preimage
        )

        # 4. 構建 PTLC 數據
        ptlc_data = {
            'amount': amount,
            'payment_point': preimage_point,
            'encrypted_recipient_data': self._encrypt_with_shared_key(
                recipient_pubkey,
                shared_secret
            )
        }

        return ptlc_data, preimage

    def claim_ptlc(self, ptlc_data, preimage, recipient_privkey):
        """收款方索取 PTLC"""
        # 驗證預圖像
        expected_point = preimage * G
        if expected_point != ptlc_data['payment_point']:
            return False

        # 解密並獲取資金
        return True

    def forward_ptlc(self, ptlc_data, next_hop_pubkey, current_privkey):
        """中間節點轉發 PTLC"""
        # 1. 計算與當前節點的共享密鑰
        current_shared = self._derive_shared_secret(
            current_privkey.public_key,
            self._extract_point_from_data(ptlc_data)
        )

        # 2. 解密獲取下一跳信息
        next_hop_data = self._decrypt_with_shared_key(
            ptlc_data['encrypted_recipient_data'],
            current_shared
        )

        # 3. 創建新的 PTLC 轉發給下一跳
        new_preimage = secrets.randbelow(self.curve.order)
        new_point = new_preimage * G

        # 4. 添加調整值以保持不可關聯性
        adjustment = self._random_point()
        modified_point = new_point + adjustment

        return self._create_forward_ptlc(
            next_hop_data,
            modified_point,
            new_preimage
        )

六、路由失敗與故障處理

6.1 失敗類型

失敗類型原因處理方式
通道餘額不足支付金額超過通道容量尋找替代路徑
節點離線節點不可達等待重連或繞過
費用不足路徑費用不夠增加費用重試
HTLC 過期超時未完成資金退還
目標節點不可達目標節點離線多次嘗試

6.2 失敗處理策略

class RoutingFailureHandler:
    def __init__(self, router, max_retries=3):
        self.router = router
        self.max_retries = max_retries
        self.failure_history = defaultdict(list)

    def execute_payment(self, source, target, amount):
        """帶故障處理的支付執行"""
        for attempt in range(self.max_retries):
            try:
                # 嘗試找到路徑
                path, fee = self.router.find_path(source, target, amount)

                if not path:
                    continue

                # 執行支付
                result = self._execute_hop_by_hop(path, amount)

                if result.success:
                    return result

                # 記錄失敗
                self._record_failure(path, result.error)

                # 根據失敗類型調整
                if result.error == 'INSUFFICIENT_BALANCE':
                    # 排除失敗的通道
                    self.router.exclude_channel(result.failed_channel)
                elif result.error == 'TIMEOUT':
                    # 排除失敗的節點
                    self.router.exclude_node(result.failed_node)

            except Exception as e:
                self._record_failure(None, str(e))

        return PaymentResult(success=False, error='MAX_RETRIES_EXCEEDED')

七、實際操作指南

7.1 路由節點配置優化

運行閃電節點時的路由優化配置:

# lnd.conf 路由相關配置

[routing]
# 費用策略
fee.base = 1000           # 1 sat 基本費用
fee.ppm = 100             # 0.01% 比例費用

# 通道配置
min_htlc = 1              # 最小 HTLC 金額
max_htlc = 16777215       # 最大 HTLC 金額
max_pending_htlcs = 30    # 最大待處理 HTLC 數

# 路由控制
routing.assumechanvalid = true
routing.strictgraphpruning = false

# 隱私設置
bitcoin.ignore-historical-channel-updates = true

7.2 路由問題診斷命令

# 查看通道餘額
lncli listchannels | jq '.channels[] | {channel_id, capacity, local_balance, remote_balance}'

# 查看路由歷史
lncli queryroutes <destination_pubkey> <amount>

# 調試路由問題
lncli debuglevel +RUM

# 檢查網路圖
lncli describegraph

# 查看失敗的支付
lncli listpayments --status FAILED

7.3 流動性優化實踐

# 方法 1:手動循環支付
# 將資金從節點 A 的輸出側轉移到輸入側

# 步驟 1:創建包含目標節點的路徑
lncli sendpayment \
    --routes <route_with_target> \
    --amt <amount> \
    --fee_limit <max_fee>

# 方法 2:使用流動性管理工具
# 例如:Lightning Loop(Circle/Alex Masmej)

# 安裝 loop
brew install loop-cli

# 檢查流動性
loop out terms
loop in terms

# 執行循環支付
loop out <amount>  # 將資金移出(增加流出流動性)
loop in <amount>   # 將資金移入(增加流入流動性)

# 方法 3:使用 Submarine Swap
# 通過 submarine swap 服務商進行鏈上-鏈下兌換

八、未來發展方向

8.1 路由協議演進

  1. 多路徑支付(MPP)
  1. 原子化多路徑支付(AMP)
  1. 支付點流(Payment Points Streaming)

8.2 路由基礎設施

  1. 路徑尋找服務(Pathfinding Service)
  1. 通道資金交易所(Channel Capitalization Exchange)

九、結論

閃電網路的路由機制是一個仍在快速演進的技術領域。從基礎的 Dijkstra 搜索到液態路由,再到現代的 Erlik 和 PTLC,每一次技術進步都在平衡費用、成功率、流動性和隱私這四個核心維度。

對於運營商而言,理解路由機制有助於優化節點配置和管理流動性。對於用戶而言,了解路由原理可以更好地理解支付的失敗原因和費用結構。

隨著比特幣主鏈費用持續波動和閃電網路採用率提升,路由技術的重要性將持續增加。未來,我們可能會看到更多專業化的路由服務和協議優化,這將使閃電網路更加高效和用戶友好。


參考資源

  1. Lightning Network Specification (BOLT)
  2. "Flare: An Approach to Routing in Lightning Network" (2016)
  3. "Erlik: An Agent-Based Routing for Lightning Network" (2021)
  4. LND Routing Documentation
  5. Rusty Russell's Lightning Network Blog

本文包含

延伸閱讀與來源

這篇文章對您有幫助嗎?

評論

發表評論

注意:由於這是靜態網站,您的評論將儲存在本地瀏覽器中,不會公開顯示。

目前尚無評論,成為第一個發表評論的人吧!