閃電網路路由機制完全指南
深入解析閃電網路的路由演算法、費用計算、流動性管理與隱私保護機制。
閃電網路路由機制完全指南
概述
閃電網路(Lightning Network)是比特幣的第二層擴容解決方案,旨在實現比特幣的低成本、即時支付。而路由機制則是閃電網路中最核心且最複雜的技術挑戰之一。本篇文章將深入解析閃電網路的路由演算法、費用計算、流動性管理與隱私保護機制。
一、閃電網路基礎架構回顧
1.1 通道與支付通道
閃電網路構建於比特幣區塊鏈之上,透過「支付通道」(Payment Channel)實現鏈下交易。支付通道的核心概念是:
- 兩方在比特幣區塊鏈上建立一個 2-of-2 多重簽名地址
- 雙方將資金存入該地址,創建初始餘額狀態
- 在通道存續期間,雙方可進行無限次數的鏈下交易
- 通道關閉時,最終餘額狀態廣播到比特幣主鏈
支付通道的關鍵特點:
| 特性 | 說明 |
|---|---|
| 雙向支付 | 資金可在兩方向流動 |
| 狀態更新 | 每次交易產生新的餘額狀態 |
| 離鏈交易 | 大部分交易發生在比特幣主鏈之外 |
| 最終性 | 通道關閉後,主鏈結算具有最終性 |
1.2 HTLC 技術原理
哈希時間鎖合約(Hash Time Locked Contract,HTLC)是閃電網路實現跨通道支付的關鍵技術。HTLC 的核心機制包含兩個鎖:
哈希鎖(Hash Lock):
- 收款方生成一個預圖像(preimage)
- 計算預圖像的 SHA-256 哈希值
- 付款方必須提供正確的預圖像才能提取資金
時間鎖(Time Lock):
- 設定一個未來區塊高度作為過期時間
- 如果過期前未達成條件,資金退還給付款方
- 時間鎖確保了資金的活性(liveness)
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 路由問題的複雜性
閃電網路的路由面臨獨特的技術挑戰:
- 網路拓撲動態變化:通道會頻繁開關,餘額會實時變化
- 隱私要求:不應向路徑上的節點暴露完整支付路徑
- 規模問題:數萬個節點和通道,計算複雜度極高
- 流動性約束:資金可能被困在特定通道中
2.2 基礎路徑搜索:BFS 與 Dijkstra
最基礎的路由演算法是廣度優先搜索(BFS)和 Dijkstra 最短路徑演算法:
BFS 特性:
- 適合無權重或權重相等的圖
- 時間複雜度:O(V + E),V 為節點數,E 為邊數
- 空間複雜度:O(V)
Dijkstra 特性:
- 適合帶有非負權重的圖
- 時間複雜度:O((V + E) log V)(使用優先隊列優化)
- 可處理通道費用作為權重
# 簡化的 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 更為複雜,主要因為:
- 餘額隱私:節點不會公開通道的具體餘額
- 雙向費用:費用在兩個方向可能不同
- 時間價值:延遲會影響支付的可行性
液態路由的核心思想:
液態路由(最初稱為「Flare」)是一種混合式路由方案,結合了局部拓撲信息和全局路徑搜索:
- 本地視圖:每個節點維護相鄰節點和通道的信息
- 路徑探索:使用随機行走(Random Walk)和探測(Probing)發現遠程節點
- 費用估算:基於歷史數據和網路狀態估算費用
# 液態路由的關鍵參數
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 是近年提出的改進路由方案,核心特點:
- 分層搜索:先搜索近距離節點,再擴展到遠程
- 概率性選擇:使用概率分佈選擇下一跳,而非確定性最短路徑
- 適應性費用:根據網路擁塞程度動態調整費用
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):
- 每筆支付必須支付的固定費用
- 當前預設值:1 聰(satoshi)
- 節點運營者可自定義設定
比例費用(Proportional Fee):
- 支付金額的百萬分之一(ppm,parts per million)
- 當前預設值:0.001%(即 1 ppm)
- 1,000,000 聰支付 = 1 聰比例費用
總費用計算公式:
total_fee = base_fee + (amount * proportional_fee / 1,000,000)
3.2 費用市場動態
閃電網路的費用市場遵循供需原理:
- 通道費用差異:繁忙通道收取較高費用
- 流動性收費:資金流向緊缺方向需要支付溢價
- 時間價值:緊急支付願意支付更高費用
# 動態費用計算模型
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 流動性廣告與市場
閃電網路正在發展流動性市場:
流動性廣告:
- 節點可廣告其願意接受的支付金額
- 包含費用率和時間偏好
- 使用
option_wumbo擴展
流動性租賃:
- 長期出租通道容量
- 預付費用以確保流動性可用
- 類似於傳統金融的流動性提供
// 流動性廣告消息
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 的隱私問題:
- 哈希值在整條路徑上相同,可被追蹤
- 支付類型(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 路由協議演進
- 多路徑支付(MPP):
- 將大額支付拆分為多個小額支付
- 提高成功率和隱私性
- 已在閃電網路實踐
- 原子化多路徑支付(AMP):
- MPP 的改進版本
- 使用統一的預圖像確保原子性
- 進一步提升隱私
- 支付點流(Payment Points Streaming):
- 持續性的小額支付
- 適用於訂閱、串流等場景
- 使用 PTLC 實現
8.2 路由基礎設施
- 路徑尋找服務(Pathfinding Service):
- 專業化的路由計算服務
- 提供更智能的路徑推薦
- 可能的中心化風險
- 通道資金交易所(Channel Capitalization Exchange):
- P2P 的流動性交易市場
- 標準化的流動性合約
- 提高資本效率
九、結論
閃電網路的路由機制是一個仍在快速演進的技術領域。從基礎的 Dijkstra 搜索到液態路由,再到現代的 Erlik 和 PTLC,每一次技術進步都在平衡費用、成功率、流動性和隱私這四個核心維度。
對於運營商而言,理解路由機制有助於優化節點配置和管理流動性。對於用戶而言,了解路由原理可以更好地理解支付的失敗原因和費用結構。
隨著比特幣主鏈費用持續波動和閃電網路採用率提升,路由技術的重要性將持續增加。未來,我們可能會看到更多專業化的路由服務和協議優化,這將使閃電網路更加高效和用戶友好。
參考資源
- Lightning Network Specification (BOLT)
- "Flare: An Approach to Routing in Lightning Network" (2016)
- "Erlik: An Agent-Based Routing for Lightning Network" (2021)
- LND Routing Documentation
- Rusty Russell's Lightning Network Blog
本文包含
相關文章
- 閃電網路高級路由技術:算法、隱私與優化策略 — 深入探討閃電網路路由系統的高級技術層面,包括 Sphinx 密碼學協議、盲化路徑、費用-延遲權衡模型、流動性感知路由算法,以及費用市場機制與節點運營最佳實踐。
- 閃電網路通道技術詳解 — 閃電網路通道的技術原理與實現
- 閃電網路完整開發指南:從基礎到生產環境部署 — 深入探討閃電網路的技術架構、客戶端選擇、通道建立、路由機制、流動性管理,以及生產環境部署的最佳實踐,包含 Python、JavaScript 與 Rust 完整程式碼範例。
- 閃電網路 BOLTs 規範完全指南 — 深入解析閃電網路的核心技術規範,包括 BOLT 11 支付請求格式、BOLT 2 通道建立、BOLT 3 HTLC 機制、BOLT 4 路由協議、BOLT 5 狀態管理等完整技術細節。
- 閃電網路 PTLC 技術深度解析:隱私增強與支付拆分 — 深入分析閃電網路中 PTLC(Payment Trail Lightning Component)的密碼學原理,與 HTLC 的比較隱私優勢,以及在原子多路徑支付(AMP)中的應用。
延伸閱讀與來源
這篇文章對您有幫助嗎?
請告訴我們如何改進:
評論
發表評論
注意:由於這是靜態網站,您的評論將儲存在本地瀏覽器中,不會公開顯示。
目前尚無評論,成為第一個發表評論的人吧!