跳到主要內容

12. 路徑尋找與付款傳遞

閃電網路上的付款傳遞依賴於尋找一條從發送者到接收者的路徑,這個過程稱為 路徑尋找。由於路由是由發送者完成的,發送者必須找到一條合適的路徑來到達目的地。然後這條路徑被編碼在洋蔥中,正如我們在 洋蔥路由 中看到的。

在本章中,我們將檢視路徑尋找的問題,理解通道餘額的不確定性如何使這個問題複雜化,並了解典型的路徑尋找實現如何嘗試解決它。

12.1. 閃電協定套件中的路徑尋找

路徑尋找、路徑選擇、多部分付款(MPP)和付款嘗試試錯迴圈佔據了協定套件頂部付款層的大部分。

這些組件在協定套件中以輪廓突顯,如 閃電協定套件中的付款傳遞 所示。

閃電協定套件中的付款傳遞
Figure 93. 閃電協定套件中的付款傳遞

12.1.1. BOLT 在哪裡?

到目前為止,我們已經研究了幾種作為閃電網路一部分的技術,並且看到了它們作為 BOLT 標準一部分的確切規範。你可能會驚訝地發現路徑尋找不是 BOLT 的一部分!

這是因為路徑尋找不是需要不同實現之間任何形式的協調或互操作性的活動。正如我們所見,路徑由發送者選擇。即使路由細節在 BOLT 中有詳細規定,路徑發現和選擇完全留給發送者。因此,每個節點實現可以選擇不同的策略/演算法來尋找路徑。事實上,不同的節點/客戶端和錢包實現甚至可以競爭,並將其路徑尋找演算法作為差異化的亮點。

12.2. 路徑尋找:我們在解決什麼問題?

路徑尋找這個術語可能有些誤導,因為它暗示搜尋連接兩個節點的 單一路徑。在開始時,當閃電網路很小且連接不夠充分時,問題確實是關於找到一種方式來連接支付通道以到達接收者。

但是,隨著閃電網路的爆炸性增長,路徑尋找問題的性質已經改變。在 2021 年中期,當我們完成這本書時,閃電網路由 20,000 個節點組成,由至少 55,000 個公開通道連接,總容量接近 2,000 BTC。一個節點平均有 8.8 個通道,而前 10 個連接最多的節點 每個 有 400 到 2,000 個通道。閃電網路通道圖的一小部分子集的視覺化如 截至 2021 年 7 月閃電網路部分的視覺化 所示。

mtln 1202
Figure 94. 截至 2021 年 7 月閃電網路部分的視覺化

截至 2021 年 7 月閃電網路部分的視覺化 中的網路視覺化是用一個簡單的 Python 腳本生成的,你可以在本書儲存庫的 code/lngraph 中找到它。

如果發送者和接收者都連接到其他連接良好的節點,並且至少有一個具有足夠容量的通道,那麼將有數千條路徑。問題變成從數千條可能的路徑列表中選擇將成功傳遞付款的 最佳 路徑。

12.2.1. 選擇最佳路徑

要選擇最佳路徑,我們必須首先定義「最佳」是什麼意思。可能有許多不同的標準,例如:

  • 具有足夠流動性的路徑。顯然,如果一條路徑沒有足夠的流動性來路由我們的付款,那麼它就不是一條合適的路徑。

  • 費用低的路徑。如果我們有幾個候選者,我們可能想要選擇費用較低的那些。

  • 時間鎖短的路徑。我們可能想要避免鎖定我們的資金太長時間,因此選擇時間鎖較短的路徑。

所有這些標準在某種程度上都是可取的,選擇在多個維度上都有利的路徑不是一件容易的事。像這樣的最佳化問題可能太複雜而無法解決「最佳」解決方案,但通常可以解決最優解的某種近似值,這是好消息,因為否則路徑尋找將是一個棘手的問題。

12.2.2. 數學和電腦科學中的路徑尋找

閃電網路中的路徑尋找屬於數學中 圖論 的一般類別,以及電腦科學中 圖遍歷 的更具體類別。

像閃電網路這樣的網路可以表示為一種稱為 的數學結構,其中 節點 通過 (相當於支付通道)相互連接。閃電網路形成一個 有向圖,因為節點是 不對稱 連接的,因為通道餘額在兩個通道合作夥伴之間分配,付款流動性在每個方向上都不同。在其邊上具有數值容量約束的有向圖稱為 流網路,這是一種用於優化運輸和其他類似網路的數學結構。當解決方案需要在最小化成本的同時實現特定流量時,可以使用流網路作為框架,這被稱為最小成本流問題(MCFP)。

12.2.3. 容量、餘額、流動性

為了更好地理解將聰從 A 點傳輸到 B 點的問題,我們需要更好地定義三個重要術語:容量、餘額和流動性。我們使用這些術語來描述支付通道路由付款的能力。

在連接 A←→B 的支付通道中:

容量

這是通過資金交易注入 2-of-2 多重簽名的聰的總量。它代表通道中持有的最大價值。通道容量由八卦協定宣告,節點可以知道。

餘額

這是每個通道合作夥伴持有的可以發送給另一個通道合作夥伴的聰的數量。A 的餘額的一部分可以朝著節點 B 的方向(A→B)發送。B 的餘額的一部分可以朝相反方向(A←B)發送。

流動性

可以實際在通道上朝一個方向發送的可用(子集)餘額。A 的流動性等於 A 的餘額減去通道儲備金和 A 承諾的任何待處理 HTLC。

網路(通過八卦公告)唯一知道的值是通道的總容量。該容量的某些未知部分作為每個合作夥伴的餘額分配。該餘額的某些子集可用於朝一個方向通過通道發送:

  • capacity = balance(A) + balance(B)
  • liquidity(A) = balance(A) – channel_reserve(A) – pending_HTLCs(A)

12.2.4. 餘額的不確定性

如果我們知道每個通道的確切通道餘額,我們可以使用在良好的電腦科學課程中教授的任何標準路徑尋找演算法來計算一條或多條付款路徑。但我們不知道通道餘額;我們只知道總通道容量,這是由節點在通道公告中宣傳的。為了使付款成功,通道的發送側必須有足夠的餘額。如果我們不知道容量如何在通道合作夥伴之間分配,我們就不知道我們嘗試發送付款的方向上是否有足夠的餘額。

餘額不在通道更新中宣告有兩個原因:隱私和可擴展性。首先,宣告餘額會降低閃電網路的隱私性,因為它將允許通過對餘額變化的統計分析來監視付款。其次,如果節點(全域地)在每次付款時宣告餘額,閃電網路的擴展性將與向所有參與者廣播的鏈上比特幣交易一樣糟糕。因此,餘額不被宣告。為了在餘額不確定的情況下解決路徑尋找問題,我們需要創新的路徑尋找策略。這些策略必須與使用的路由演算法密切相關,即基於源的洋蔥路由,其中發送者有責任通過網路找到一條路徑。

不確定性問題可以用數學方式描述為 流動性範圍,根據已知資訊指示流動性的下限和上限。由於我們知道通道的容量,並且我們知道通道儲備餘額(每端允許的最小餘額),流動性可以定義為:

  • min(liquidity) = channel_reserve
  • max(liquidity) = capacity – channel_reserve

或作為範圍:

  • channel_reserve <= liquidity <= (capacity – channel_reserve)

我們的通道流動性不確定性範圍是最小和最大可能流動性之間的範圍。這對網路來說是未知的,除了兩個通道合作夥伴。然而,正如我們將看到的,我們可以使用從付款嘗試返回的失敗 HTLC 來更新我們的流動性估計並減少不確定性。例如,如果我們收到一個 HTLC 失敗代碼,告訴我們一個通道無法完成一個小於我們對最大流動性估計的 HTLC,這意味著最大流動性可以更新為失敗 HTLC 的金額。簡單來說,如果我們認為流動性可以處理 N 聰的 HTLC,而我們發現它無法傳遞 M 聰(其中 M 更小),那麼我們可以將我們的估計更新為 M–1 作為上限。我們試圖找到天花板並撞上了它,所以它比我們想像的要低!

12.2.5. 路徑尋找的複雜性

通過圖找到路徑是現代電腦可以相當高效地解決的問題。如果邊的權重都相等,開發人員主要選擇廣度優先搜尋。在邊的權重不相等的情況下,使用基於 Dijkstra 演算法的演算法,例如 A*(讀作「A-star」)。在我們的情況下,邊的權重可以代表路由費用。只有容量大於要發送金額的邊才會被包含在搜尋中。在這種基本形式中,閃電網路中的路徑尋找非常簡單直接。

然而,通道流動性對發送者來說是未知的。這將我們簡單的理論電腦科學問題變成了一個相當複雜的現實問題。我們現在必須在只有部分知識的情況下解決路徑尋找問題。例如,我們懷疑哪些邊可能能夠轉發付款,因為它們的容量看起來足夠大。但我們無法確定,除非我們嘗試一下或直接詢問通道所有者。即使我們能夠直接詢問通道所有者,在我們詢問其他人、計算路徑、構建洋蔥並發送它的時候,他們的餘額可能已經改變。我們不僅資訊有限,而且我們擁有的資訊是高度動態的,可能在任何時候在我們不知情的情況下改變。

12.2.6. 保持簡單

閃電節點中實現的路徑尋找機制是首先創建一個候選路徑列表,通過某種函數進行過濾和排序。然後,節點或錢包將在試錯迴圈中探測路徑(通過嘗試傳遞付款),直到找到成功傳遞付款的路徑。

這種探測是由閃電節點或錢包完成的,用戶不會直接觀察到軟體的這個過程。然而,如果付款沒有立即完成,用戶可能會懷疑正在進行探測。

雖然盲目探測不是最優的,留有很大的改進空間,但應該注意到,即使是這種簡單的策略對於較小的付款和連接良好的節點也出奇地有效。

大多數閃電節點和錢包實現通過對候選路徑列表進行排序/加權來改進這種方法。一些實現按成本(費用)或成本和容量的某種組合對候選路徑進行排序。

12.3. 路徑尋找和付款傳遞流程

路徑尋找和付款傳遞涉及幾個步驟,我們在此列出。不同的實現可能使用不同的演算法和策略,但基本步驟可能非常相似:

  1. 從公告和更新創建包含每個通道容量的 通道圖,並過濾該圖,忽略任何容量不足以發送我們想要金額的通道。

  2. 找到連接發送者和接收者的路徑。

  3. 按某種權重對路徑進行排序(這可能是上一步 演算法 的一部分)。

  4. 按順序嘗試每條路徑直到付款成功(試錯迴圈)。

  5. 可選地使用 HTLC 失敗返回來更新我們的圖,減少 不確定性

我們可以將這些步驟分為三個主要活動:

  • 通道圖構建

  • 路徑尋找(通過一些啟發式方法過濾和排序)

  • 付款嘗試

如果我們使用失敗返回來更新圖,或者如果我們正在進行多部分付款(見 多部分付款),這三個活動可以在 付款回合 中重複。

在接下來的章節中,我們將更詳細地研究這些步驟中的每一個,以及更高級的付款策略。

12.4. 通道圖構建

八卦協定與通道圖 中,我們介紹了節點在其八卦中使用的三種主要訊息:node_announcement、channel_announcement 和 channel_update。這三種訊息允許任何節點以 通道圖 的形式逐漸構建閃電網路的「地圖」。這些訊息中的每一個都為通道圖提供了關鍵的資訊:

node_announcement

這包含閃電網路上節點的資訊,例如其節點 ID(公鑰)、網路地址(例如 IPv4/6 或 Tor)、能力/功能等。

channel_announcement

這包含兩個節點之間公開(已宣告)通道的容量和通道 ID,以及通道存在和所有權的證明。

channel_update

這包含節點的費用和時間鎖(CLTV)期望,用於在指定通道上路由出站(從該節點的角度)付款。

從數學圖的角度來看,node_announcement 是創建圖的節點或 頂點 所需的資訊。channel_announcement 允許我們創建代表支付通道的圖的 。由於支付通道的每個方向都有自己的餘額,我們創建一個有向圖。channel_update 允許我們納入費用和時間鎖來設置圖邊的 成本權重

根據我們將用於路徑尋找的演算法,我們可能為圖的邊建立多個不同的成本函數。

現在,讓我們忽略成本函數,只使用 node_announcement 和 channel_announcement 訊息建立一個顯示節點和通道的通道圖。

在本章中,我們將看到 Selena 如何嘗試找到一條路徑向 Rashid 支付一百萬聰。首先,Selena 正在使用來自閃電網路八卦的資訊來構建通道圖,以發現節點和通道。然後 Selena 將探索她的通道圖以找到一條向 Rashid 發送付款的路徑。

這是 Selena 的 通道圖。沒有所謂的 那個 通道圖,永遠只有 一個通道圖,而且它總是從構建它的節點的角度來看(見 地圖-領土關係)。

Selena 並不只在發送付款時構建通道圖。相反,Selena 的節點 持續 構建和更新通道圖。從 Selena 的節點啟動並連接到網路上的任何對等節點的那一刻起,它就會參與八卦並使用每條訊息來盡可能多地了解網路。

地圖-領土關係

來自維基百科關於 地圖-領土關係 的頁面,「地圖-領土關係描述了一個物體與其表示之間的關係,就像地理領土與其地圖之間的關係一樣。」

地圖-領土關係最好用 Lewis Carroll 的短篇故事《Sylvie and Bruno Concluded》來說明,該故事描述了一張虛構的地圖,其比例為 1:1,因此具有完美的準確性,但變得完全無用,因為如果展開它將覆蓋整個領土。

這對閃電網路意味著什麼?閃電網路是領土,通道圖是該領土的地圖。

雖然我們可以想像一個代表閃電網路完整、最新地圖的理論(柏拉圖式理想)通道圖,但這樣的地圖只是閃電網路本身。每個節點都有自己從公告構建的通道圖,而且必然是不完整的、不正確的和過時的!

地圖永遠無法完全準確地描述領土。

Selena 監聽 node_announcement 訊息並發現另外四個節點(除了預期接收者 Rashid)。結果圖代表六個節點的網路:Selena 和 Rashid 分別是發送者和接收者;Alice、Bob、Xavier 和 Yan 是中間節點。Selena 的初始圖只是一個節點列表,如 節點公告 所示。

mtln 1203
Figure 95. 節點公告

Selena 還收到七條帶有相應通道容量的 channel_announcement 訊息,允許她構建網路的基本「地圖」,如 通道圖 所示。(名稱 Alice、Bob、Selena、Xavier、Yan 和 Rashid 已被它們的首字母取代:分別是 A、B、S、X 和 R。)

mtln 1204
Figure 96. 通道圖
通道圖中的不確定性

正如你從 通道圖 中看到的,Selena 不知道任何通道的餘額。她的初始通道圖包含最高程度的不確定性。

但是等等:Selena 確實知道 一些 通道餘額!她知道她自己的節點與其他節點連接的通道的餘額。雖然這看起來不多,但它實際上是構建路徑的非常重要的資訊——Selena 知道她自己通道的實際流動性。讓我們更新通道圖以顯示這些資訊。我們將使用「?」符號來表示未知餘額,如 帶有已知和未知餘額的通道圖 所示。

mtln 1205
Figure 97. 帶有已知和未知餘額的通道圖

雖然「?」符號看起來很不祥,但缺乏確定性與完全無知不同。我們可以 量化 不確定性,並通過用我們嘗試的成功/失敗 HTLC 更新圖來 減少 它。

不確定性可以被量化,因為我們知道最大和最小可能的流動性,並且可以計算更小(更精確)範圍的機率。

一旦我們嘗試發送 HTLC,我們就可以了解更多關於通道餘額的資訊:如果我們成功,那麼餘額 至少 足以傳輸特定金額。同時,如果我們收到「臨時通道失敗」錯誤,最可能的原因是特定金額的流動性不足。

你可能在想,「從成功的 HTLC 學習有什麼意義?」畢竟,如果成功了我們就「完成了」。但請考慮到我們可能正在發送多部分付款的一部分。我們也可能在短時間內發送其他單部分付款。我們了解到的關於流動性的任何資訊對下一次嘗試都是有用的!

12.4.2. 流動性不確定性和機率

為了量化通道流動性的不確定性,我們可以應用機率論。付款傳遞機率的基本模型將導致一些相當明顯但重要的結論:

  • 較小的付款通過路徑成功傳遞的機會更大。

  • 較大容量的通道將為我們提供特定金額付款傳遞的更好機會。

  • 通道(跳數)越多,成功的機會越低。

雖然這些可能很明顯,但它們有重要的影響,特別是對於多部分付款的使用(見 多部分付款)。數學並不難理解。

讓我們使用機率論來看看我們如何得出這些結論。

首先,讓我們假設容量為 c 的通道在一側有一個未知值在 (0, c) 範圍內的流動性,即「0 到 c 之間的範圍」。例如,如果容量是 5,那麼流動性將在 (0, 5) 範圍內。現在,從這裡我們看到如果我們想發送 5 聰,我們成功的機會只有 1/6(16.66%),因為我們只有在流動性恰好是 5 時才會成功。

更簡單地說,如果流動性的可能值是 0、1、2、3、4 和 5,這六個可能值中只有一個足以發送我們的付款。繼續這個例子,如果我們的付款金額是 3,那麼如果流動性是 3、4 或 5,我們就會成功。所以我們成功的機會是 3/6(50%)。用數學表示,單個通道的成功機率函數是:

\[$P_c(a) = (c + 1 - a) / (c + 1)$\]

其中 a 是金額,c 是容量。

從方程式中我們看到,如果金額接近 0,機率接近 1,而如果金額超過容量,機率為零。

換句話說:「較小的付款成功傳遞的機會更大」或「較大容量的通道為我們提供特定金額的更好傳遞機會」以及「你無法在容量不足的通道上發送付款」。

現在讓我們考慮通過由多個通道組成的路徑成功的機率。假設我們的第一個通道有 50% 的成功機會(P = 0.5)。然後如果我們的第二個通道有 50% 的成功機會(P = 0.5),直覺上我們的整體機會是 25%(P = 0.25)。

我們可以將此表示為一個方程式,將付款成功的機率計算為路徑中每個通道機率的乘積:

\[$P_{payment} = \prod_{i=1}^n P_i$\]

其中 Pi 是通過一條路徑或通道成功的機率,Ppayment 是通過所有路徑/通道成功付款的整體機率。

從方程式中我們看到,由於通過單個通道成功的機率總是小於或等於 1,通過多個通道的機率將 呈指數下降

換句話說,「你使用的通道(跳數)越多,成功的機會越低。」

關於通道流動性不確定性的數學理論和建模有很多。關於通道流動性不確定性區間建模的基礎工作可以在 Pickhardt 等人(本書合著者)的論文 《Security and Privacy of Lightning Network Payments with Uncertain Channel Balances》 中找到。

12.4.3. 費用和其他通道指標

接下來,我們的發送者將根據從中間節點收到的 channel_update 訊息向圖添加資訊。提醒一下,channel_update 包含關於通道和通道合作夥伴之一期望的豐富資訊。

通道圖費用和其他通道指標 中,我們看到 Selena 如何根據來自 A、B、X 和 Y 的 channel_update 訊息更新通道圖。請注意,通道 ID 和通道方向(包含在 channel_flags 中)告訴 Selena 這個更新指的是哪個通道和哪個方向。每個通道合作夥伴八卦一條或多條 channel_update 訊息來宣告他們的費用期望和關於通道的其他資訊。例如,在左上角我們看到 Alice 為通道 A—​B 和方向 A 到 B 發送的 channel_update。通過這個更新,Alice 告訴網路她將收取多少費用來通過該特定通道將 HTLC 路由到 Bob。Bob 可能會為相反方向宣告一個通道更新(未在此圖中顯示),具有完全不同的費用期望。任何節點都可以隨時發送新的 channel_update 來更改費用或時間鎖期望。

mtln 1206
Figure 98. 通道圖費用和其他通道指標

費用和時間鎖資訊非常重要,不僅作為路徑選擇指標。正如我們在 洋蔥路由 中看到的,發送者需要在每一跳添加費用和時間鎖(cltv_expiry_delta)來製作洋蔥。計算費用的過程是從接收者到發送者 向後 沿著路徑進行的,因為每個中間跳期望傳入的 HTLC 具有比他們將發送給下一跳的傳出 HTLC 更高的金額和到期時間鎖。因此,例如,如果 Bob 想要 1,000 聰的費用和 30 個區塊的到期時間鎖增量來向 Rashid 發送付款,那麼該金額和到期增量必須添加到 來自 Alice 的 HTLC 中。

同樣重要的是要注意,通道必須具有不僅足夠付款金額而且足夠所有後續跳的累計費用的流動性。即使 Selena 到 Xavier 的通道(S→X)有足夠的流動性用於 1M 聰的付款,一旦我們考慮費用,它 就沒有 足夠的流動性了。我們需要知道費用,因為只有具有 付款和所有費用 足夠流動性的路徑才會被考慮。

12.5. 尋找候選路徑

通過像這樣的有向圖找到合適的路徑是一個研究得很充分的電腦科學問題(廣泛稱為 最短路徑問題),可以通過各種演算法來解決,具體取決於所需的優化和資源約束。

解決這個問題最著名的演算法是由荷蘭數學家 E. W. Dijkstra 於 1956 年發明的,簡稱為 Dijkstra 演算法。除了原始的 Dijkstra 演算法外,還有許多變體和優化,例如 A*(「A-star」),這是一種基於啟發式的演算法。

如前所述,「搜尋」必須 向後 應用以計算從接收者到發送者累積的費用。因此,Dijkstra、A* 或其他演算法會從接收者搜尋到發送者的路徑,使用費用、估計流動性和時間鎖增量(或某種組合)作為每一跳的成本函數。

使用這樣的演算法,Selena 計算出幾條可能到達 Rashid 的路徑,按最短路徑排序:

  1. S→A→B→R

  2. S→X→Y→R

  3. S→X→B→R

  4. S→A→B→X→Y→R

但是,正如我們之前看到的,一旦考慮費用,通道 S→X 沒有足夠的流動性用於 1M 聰的付款。所以路徑 2 和 3 不可行。這留下路徑 1 和 4 作為付款的可能路徑。

有了兩條可能的路徑,Selena 準備嘗試傳遞!

12.6. 付款傳遞(試錯迴圈)

Selena 的節點通過構建 HTLC、建立洋蔥並嘗試傳遞付款來開始試錯迴圈。對於每次嘗試,有三種可能的結果:

  • 成功結果(update_fulfill_htlc)

  • 錯誤(update_fail_htlc)

  • 「卡住」的付款,沒有回應(既沒有成功也沒有失敗)

如果付款失敗,可以通過更新圖(更改通道的指標)並重新計算替代路徑來通過不同的路徑重試。

我們在 卡住的付款 中研究了付款「卡住」時會發生什麼。重要的細節是,卡住的付款是最糟糕的結果,因為我們無法用另一個 HTLC 重試,因為兩者(卡住的那個和重試的那個)最終都可能通過並導致雙重付款。

12.6.1. 第一次嘗試(路徑 #1)

Selena 嘗試第一條路徑(S→A→B→R)。她構建洋蔥並發送它,但收到 Bob 節點的失敗代碼。Bob 報告了 temporary channel failure,並帶有 channel_update 識別通道 B→R 為無法傳遞的通道。這次嘗試如 路徑 #1 嘗試失敗 所示。

mtln 1207
Figure 99. 路徑 #1 嘗試失敗
從失敗中學習

從這個失敗代碼,Selena 將推斷 Bob 在該通道上沒有足夠的流動性來向 Rashid 傳遞付款。重要的是,這個失敗縮小了該通道流動性的不確定性!之前,Selena 的節點假設 Bob 那邊通道的流動性在 (0, 4M) 範圍內。現在,她可以假設流動性在 (0, 999999) 範圍內。同樣,Selena 現在可以假設 Rashid 那邊該通道的流動性在 (1M, 4M) 範圍內,而不是 (0, 4M)。Selena 從這次失敗中學到了很多。

12.6.2. 第二次嘗試(路徑 #4)

現在 Selena 嘗試第四條候選路徑(S→A→B→X→Y→R)。這是一條更長的路徑,將產生更多費用,但它現在是傳遞付款的最佳選擇。

幸運的是,Selena 從 Alice 收到 update_fulfill_htlc 訊息,表示付款成功,如 路徑 #4 嘗試成功 所示。

mtln 1208
Figure 100. 路徑 #4 嘗試成功
從成功中學習

Selena 也從這次成功的付款中學到了很多。她現在知道路徑 S→A→B→X→Y→R 上的所有通道都有足夠的流動性來傳遞付款。此外,她現在知道這些通道中的每一個都已將 HTLC 金額(1M + 費用)移動到通道的另一端。這允許 Selena 重新計算該路徑中所有通道接收端的流動性範圍,用 1M + 費用替換最小流動性。

過時的知識?

Selena 現在對閃電網路有了更好的「地圖」(至少就這七個通道而言)。這些知識對 Selena 嘗試進行的任何後續付款都很有用。

然而,當其他節點發送或路由付款時,這些知識會變得有些「過時」。Selena 永遠不會看到這些付款(除非她是發送者)。即使她參與路由付款,洋蔥路由機制意味著她只能看到一跳(她自己的通道)的變化。

因此,Selena 的節點必須考慮在假設這些知識已過時且不再有用之前保留多長時間。

12.7. 多部分付款

多部分付款(MPP) 是 2020 年在閃電網路中引入的功能,現在已經非常廣泛可用。多部分付款允許將付款拆分為多個 部分,這些部分作為 HTLC 通過幾條不同的路徑發送給預期接收者,同時保持整體付款的 原子性。在這個上下文中,原子性意味著付款的所有 HTLC 部分最終要麼全部完成,要麼整個付款失敗並且所有 HTLC 部分都失敗。不存在部分成功付款的可能性。

多部分付款是閃電網路的重大改進,因為它使發送不「適合」任何單個通道的金額成為可能,方法是將它們拆分為具有足夠流動性的較小金額。此外,與單路徑付款相比,多部分付款已被證明可以增加成功付款的機率。

現在 MPP 可用了,最好將單路徑付款視為 MPP 的子類別。本質上,單路徑只是大小為一的多部分。所有付款都可以被視為多部分付款,除非付款的大小和可用的流動性使得可以用單個部分傳遞。

12.7.1. 使用 MPP

MPP 不是用戶會選擇的東西,而是一種節點路徑尋找和付款傳遞策略。相同的基本步驟被實現:創建圖、選擇路徑和試錯迴圈。區別在於,在路徑選擇期間,我們還必須考慮如何拆分付款以優化傳遞。

在我們的例子中,我們可以看到使用 MPP 可以對我們的路徑尋找問題進行一些直接的改進。首先,我們可以利用 S→X 通道,它已知流動性不足以傳輸 1M 聰加上費用。通過在該通道上發送較小的部分,我們可以使用以前不可用的路徑。其次,我們有 B→R 通道的未知流動性,它不足以傳輸 1M 金額,但可能足以傳輸較小的金額。

拆分付款

根本問題是如何拆分付款。更具體地說,拆分的最佳數量和每次拆分的最佳金額是多少?

這是一個正在進行研究的領域,新的策略正在出現。多部分付款導致與單路徑付款不同的演算法方法,即使單路徑解決方案可以從多部分優化中出現(即,多部分路徑尋找演算法可能建議單路徑作為最優解決方案)。

如果你還記得,我們發現流動性/餘額的不確定性導致了一些(有些明顯的)結論,我們可以在 MPP 路徑尋找中應用,即:

  • 較小的付款成功的機會更高。

  • 你使用的通道越多,成功的機會(呈指數)越低。

從第一個見解,我們可能得出結論,將大額付款(例如 1 百萬聰)拆分為小額付款會增加這些較小付款中每一個成功的機會。如果我們發送較小的金額,具有足夠流動性的可能路徑數量將更大。

將這個想法推向極端,為什麼不將 1M 聰的付款拆分為一百萬個獨立的一聰部分?答案在於我們的第二個見解:由於我們將使用更多的通道/路徑來發送我們的一百萬個單聰 HTLC,我們成功的機會將呈指數下降。

如果不明顯,前面兩個見解創造了一個「最佳點」,我們可以在其中最大化我們成功的機會:拆分成較小的付款但不要拆分太多!

量化給定通道圖的大小/拆分數量的這種最優平衡超出了本書的範圍,但這是一個活躍的研究領域。一些當前的實現使用非常簡單的策略,將付款拆分為兩半、四分之一等。

要閱讀更多關於將付款拆分為不同大小並將它們分配到路徑時涉及的最小成本流優化問題,請參閱 René Pickhardt(本書合著者)和 Stefan Richter 的論文 《Optimally Reliable & Cheap Payment Flows on the Lightning Network》

在我們的例子中,Selena 的節點將嘗試將 1M 聰的付款拆分為 2 個部分,分別為 600k 和 400k 聰,並在 2 條不同的路徑上發送它們。這如 發送多部分付款的兩個部分 所示。

因為 S→X 通道現在可以使用,並且(對 Selena 來說幸運的是),B→R 通道有足夠的流動性用於 600k 聰,這 2 個部分在以前不可能的路徑上成功了。

mtln 1209
Figure 101. 發送多部分付款的兩個部分

12.7.2. 多「回合」的試錯

多部分付款導致付款傳遞的試錯迴圈有所修改。因為我們在每次嘗試中嘗試多條路徑,我們有四種可能的結果:

  • 所有部分都成功,付款成功

  • 一些部分成功,一些失敗並返回錯誤

  • 所有部分都失敗並返回錯誤

  • 一些部分「卡住」,沒有返回錯誤

在第二種情況下,一些部分失敗並返回錯誤,一些部分成功,我們現在可以 重複 試錯迴圈,但 只針對剩餘金額

例如,假設 Selena 有一個更大的通道圖,有數百條可能的路徑到達 Rashid。她的路徑尋找演算法可能找到一個由 26 個不同大小的部分組成的最佳付款拆分。在第一輪嘗試發送所有 26 個部分後,其中 3 個部分失敗並返回錯誤。

如果這 3 個部分包含,比如說 155k 聰,那麼 Selena 將重新開始路徑尋找工作,只針對 155k 聰。下一輪可以找到完全不同的路徑(針對 155k 的剩餘金額進行優化),並將 155k 金額拆分成完全不同的拆分!

雖然 26 個拆分部分看起來很多,但在閃電網路上的測試已經成功地通過將 0.3679 BTC 拆分成 345 個部分來傳遞付款。

此外,Selena 的節點將使用從第一輪的成功和錯誤中收集的資訊來更新通道圖,以找到第二輪的最優路徑和拆分。

假設 Selena 的節點計算出發送 155k 剩餘金額的最佳方式是 6 個部分,拆分為 80k、42k、15k、11k、6.5k 和 500 聰。在下一輪中,Selena 只收到一個錯誤,表示 11k 聰部分失敗了。Selena 再次根據收集的資訊更新通道圖,並再次運行路徑尋找以發送 11k 剩餘金額。這一次,她成功了,分別用 6k 和 5k 聰的 2 個部分。

這個使用 MPP 發送付款的多回合範例如 使用 MPP 在多回合中發送付款 所示。

mtln 1210
Figure 102. 使用 MPP 在多回合中發送付款

最後,Selena 的節點使用 3 回合的路徑尋找將 1M 聰分成 30 個部分發送。

12.8. 結論

在本章中,我們研究了路徑尋找和付款傳遞。我們看到了如何使用通道圖來找到從發送者到接收者的路徑。我們還看到了發送者如何嘗試在候選路徑上傳遞付款並在試錯迴圈中重複。

我們還檢視了通道流動性的不確定性(從發送者的角度)以及這對路徑尋找的影響。我們看到了如何量化不確定性並使用機率論得出一些有用的結論。我們還看到了如何通過從成功和失敗的付款中學習來減少不確定性。

最後,我們看到了新部署的多部分付款功能如何允許我們將付款拆分成部分,即使對於較大的付款也能增加成功的機率。


1. 維基百科 賽局理論條目 提供了更多資訊。
2. Joseph Poon and Thaddeus Dryja. "The Bitcoin Lightning Network: Scalable Off-Chain Instant Payments." DRAFT Version 0.5.9.2. January 14, 2016. https://lightning.network/lightning-network-paper.pdf.
1. Andreas M. Antonopoulos, Mastering Bitcoin, 2nd Edition, Chapter 1 (O’Reilly)
2. ACINQ:Eclair Mobile 閃電網路錢包的開發者。
3. 通常不建議為多筆付款重複使用同一個比特幣地址,因為所有比特幣交易都是公開的。 經過的好奇者可能會掃描 Alice 的 QR 碼,並在比特幣區塊鏈上看到 Alice 已經收到了多少小費到這個地址。 幸運的是,閃電網路為此提供了更私密的解決方案,將在本書後面討論!
4. Eclair 錢包不提供自動計算必要費用並將最大資金分配給通道的選項,所以 Alice 必須自己計算。
1. 雖然原始閃電網路白皮書描述了由兩個通道夥伴注資的通道,但截至 2020 年的當前規範假設只有一個夥伴向通道承諾資金。截至 2021 年 5 月,雙重注資閃電網路通道在 c-lightning 閃電網路實現中處於實驗階段。
2. George Danezis and Ian Goldberg, "Sphinx: A Compact and Provably Secure Mix Format," in IEEE Symposium on Security and Privacy (New York: IEEE, 2009), 269–282.
3. 「洋蔥」一詞最初由 Tor 專案使用。此外,Tor 網路也被稱為洋蔥網路,該專案使用洋蔥作為其標誌。Tor 服務在網際網路上使用的頂級域名是 onion
1. George Danezis and Ian Goldberg, "Sphinx: A Compact and Provably Secure Mix Format," in IEEE Symposium on Security and Privacy (New York: IEEE, 2009), 269–282.