錢包指紋如何摧毀 Payjoin 隱私性

作者:Armin Sabouri

來源:https://payjoin.org/blog/2026/03/25/wallet-fingerprints-payjoin-privacy/

錢包軟件在構造一筆交易的時候,會作出數十個微小的決策:輸入的排序、錢幣的挑選、手續費估計,還有簽名編碼;等等。這些決策所形成的模式,叫做 “錢包指紋”,在不同的軟件實現之間有系統性的不同,因此可以用來識別製作區塊鏈上交易的源頭錢包軟件。

一些指紋是確定性的。比如,Bitcoin Core 在生成 ECDSA 簽名時,會不斷嘗試不同的隨機數(“研磨”),以使最終得到的簽名具有 “低 r 值”(這樣的簽名的字節數會少一些),因此,只要一個簽名是 72 字節長的,就可以立即排除使用 Bitcoin Core 作為簽名器的可能。有一些指紋則是概率性的:各錢包軟件給出的手續費率會有一些典型的分佈。Bug 也會成為指紋。每一個維護都會提供獨立的證據,這些證據複合起來會變得非常有利。Ishaana Misra 曾經作過錢包指紋的綜合性研究中文譯本)。

指紋可以增強錢包聚類分析的效果 —— 聚類分析指的是使用行為線索來歸集有關聯的交易輸出(錢幣)。有關聚類分析的背景知識,請看 Yuval Kogman 的講述錢包聚類分析歷史的博文(中文譯本)。最近,關於使用錢包指紋來支撐已有的線索分析(比如找零輸出識別)的工作,證明了可以在粗糙的啟發式分析之上獲得巨大提升。Kappos 等人證明了,結合來自臨近交易的指紋與數值分析,可以提升聚類分析的準確性;這是用真實場景中的數據驗證了這種方法的可行性。這就直接威脅到了 payjoin 交易的隱私性模型:它的基礎是無法甄別支付發送方和接收方的輸入。能夠區分輸入來源的錢包指紋,帶回了 payjoin 嘗試打破的聚類分析。

(譯者注:“heuristics” 在本文中譯為 “線索分析” 或 “啟發式分析”。)

本文就使用這一視角來觀察真實的 payjoin 交易。

兩層關聯

理論上,Payjoin 交易跟標準的單方交易看起來沒有分別。分析者如果使用傳統的啟發式分析,就會錯誤地歸類這些交易、將發送者和接收者的輸入(錢幣)都劃分到同一個集群。所以,要感知 payjoin 交易,第一步就是偵測出合作式交易的人工痕跡。

Simin Ghesmati 等人的工作證明了,挑選輸入時候的怪癖(具體來說是安排多餘的輸入)如何能用於偵測 payjoin 交易、以及區分輸入和輸出的主人。錢包指紋就是在相同的任務上提供了另一種信號。

如果發送者和接收者使用了不同的錢包軟件,指紋就可能會揭曉哪個輸入和輸出屬於誰。一旦你推斷出所有權的區別,標準的 “輸入所有權同一性(CIOH)” 聚類方法就能應用在各個參與者的輸入上,從而,從分析的角度看,payjoin 就等同於一對常規(單方支付)交易。

錢包指紋信號在兩個層面上釋放了信息:

  • 單筆交易內部:這些信號幫助區分了單筆交易內各個輸入和輸出的主人。哪個輸出是找零(回到了發送者手上)、哪個是支付(發送給了收款方)?正確的找零輸出識別是極為關鍵的,因為它將發送者當前這筆交易與他們的下一筆交易關聯起來。指紋可能會直接揭示這一點:因為發送者使用的錢包軟件,找零輸出可能會繼承(跟某個輸入)一樣的特徵。
  • 交易之間:信號在交易圖上顯示出意義:
    • 後向:每一個輸入都是由一些前序交易創建的,這些前序交易可能攜帶獨特的指紋。
    • 前向:每一個輸出都可能在未來被花費,而花費它們的交易也可能帶有指紋。

因此,針對一筆 payjoin 交易的區塊鏈分析的目標是:

  1. 偵測出合作構造交易的痕跡。
  2. 使用交易內信號和交易間信號找回 發送者/接收者 的區隔。
  3. 在區分後的各個集群中應用標準的啟發式分析。

案例 1:Samourai Payjoin

交易8dba6657...

觀察這筆交易的各個字段的值,看不出任何可疑的地方。兩個輸入的 nSequence(字段的數值)是相同的,也都使用 P2WPKH 腳本公鑰類型,它們的見證數據堆棧看起來也相似。信號來自簽名的長度(字節數)。

0 號輸入的 DER 編碼的(ECDSA)簽名是 71 字節(它是低 r 的);1 號輸入的簽名是 72 字節(它是高 r 的)。一個會研磨的錢包軟件只會產生低 r 簽名;不研磨的錢包軟件則只有 50% 的概率產生低 r 簽名。所以,在一筆交易內出現了一對 低 r/高 r 簽名,還不是合作的強力證據。不過,如果其中一個輸入來自一個總是研磨的集群,這次卻跟 高 r 輸入一起出現,它們分屬兩個錢包集群的概率就更大了。

將簽名的不對稱性作為候選的區隔一句:參與者 A 持有低 r 輸入,參與者 B 持有高 r 輸入;前者的面額是 5 0000 聰,後者是 399 9216 聰。我們可以測試輸出的兩種可能情形:

情形 1:0 號輸出(9752 聰)屬於 A,1 號輸出(403 9216 聰)屬於 B 。

  • 參與者 A:5 0000 進, 9752 出;淨流出 4 0248 聰;
  • 參與者 B:399 9216 進,403 9216 出;淨流入 4 0000 聰。
  • 含義:A 給 B 支付了 4 0000 聰(另外 248 聰是手續費)

情形 2:0 號輸出(9752 聰)屬於 B,1 號輸出(403 9216 聰)屬於 A 。

  • 參與者 A:5 0000 進, 403 9216 出;淨流入大約 400 萬聰;
  • 參與者 B:399 9216 進,9752 出;淨流出 398 9464 聰。
  • 含義:A 給 B 支付了 4 0000 聰(另外 248 聰是手續費)

情形 1 意味著支付的數額恰好是 4 萬聰;情形 2 則意味著是 398 9216 聰。整數啟發式分析傾向於情形 1 。後來的花費交易鞏固了這個結論:0 號輸出也被一個低 r 簽名花費(與本交易中 A 的輸入一致);1 號輸出也被一個高 r 簽名花費(與本交易中 B 的輸入一致)。

Payjoin 就這樣被瓦解了:輸入/輸出 的所有權區別被推斷出來了,支付的數額也被找出來了。

案例 2:PDK Demo Payjoin

交易3c5436f1...

兩個輸入都採用 P2TR 密鑰路徑花費。根據 P2TR 上的 Taproot 花費規則,默認的 sighash 標籤是 SIGHASH_ALL,然後這個 sighash 字節就可以省略掉。省略它是正統的形式,但不省略它在共識上也是有效的。0 號輸入的見證數據是 64 字節 —— 它省去了 sighash 字節。但 1 號輸入的見證數據是 65 字節 —— 出現了 0x01(顯式表示 SIGHASH_ALL 的字節)。出現這個字節通常是一個實現上的 bug ,而不是有意為之。

兩個輸入如果來自同一個錢包(使用相同的錢包軟件),應該會使用一致的 sighash 策略。這種不一致,將這兩個輸入區別開來:參與者 A 持有這個省去 sighash 字節的輸入;參與者 B 持有這個帶有顯式 sighash 字節的輸出。

與案例 1 不同的是,在這個案例中,數值線索並不能區分輸出的主人。兩種歸屬情形在含義上一樣:

情形 1:0 號輸出(5 9014 聰)屬於 A,1 號輸出(86 4506 聰)屬於 B 。

  • 參與者 A:5 1514 進,5 9014 出;淨流入 7500 聰;
  • 參與者 B:87 2224 進,86 4506 出;淨流出 7718 聰。
  • 含義:B 給 A 支付了 7500 聰(另外 218 聰是手續費)

情形 2:0 號輸出(5 9014 聰)屬於 B,1 號輸出(86 4506 聰)屬於 A 。

  • 參與者 A:5 1514 進, 86 4506 出;淨流入 81 2992 聰;
  • 參與者 B:87 2224 進,5 9014 出;淨流出813210 聰。
  • 含義:B 給 A 支付了 81 2992 聰(另外 218 聰是手續費)

整數線索傾向於情形 1,但這種辦法也不是萬無一失。我們有很大的把握,這是一筆多方構造的交易,因為存在錢包指紋信號;但是,所有權區別則無法肯定。Sighash 處理方式的不一致是一個殘留的漏洞,但也就到此為止。

不過,後來,第二個輸出的花費交易再次出現了顯式表示 SIGHASH_ALL 的字節,與本交易中的第二個輸入一致。這強烈表明,第二個輸出跟第二個輸入屬於同一個交易,所以情形 1 是更有可能的。

案例 3:Cake Wallet → Bull Bitcoin Mobile Payjoin

交易8fb80573...

首先映入眼簾的是,兩個輸入的 nSequence 字段的值都是 0x01。Cake Wallet 根據 BIP-68 設置了一個相對時間鎖,而 Bull Bitcoim Mobile 也匹配了這個數值,所以兩個輸入不存在交易內部的不對稱性。簽名也是同質的:兩個輸入都使用了低 r 簽名,並且使用了相同的 sighash 標籤。單單從這筆交易看,指紋分析卡住了。

但是,金額歸屬,提供了一種可能的區隔。兩種情形如下:

情形 1:0 號輸出(2 9358 聰)屬於 B,1 號輸出(42 9919 聰)屬於 A。

  • 參與者 A:44 0337 進,42 9919 出;淨流出 1 0418 聰;
  • 參與者 B:1 9538 進,2 9358 出;淨流出 1 0000 聰。
  • 含義:A 給 B 支付了 1 0000 聰(另外 418 聰是手續費)

情形 2:0 號輸出(2 9358 聰)屬於 A,1 號輸出(42 9919 聰)屬於 B 。

  • 參與者 A:44 0337 進, 2 9358 出;淨流出 41 0979 聰;
  • 參與者 B:1 9538 進,42 9919 出;淨流入 41 0561 聰。
  • 含義:A 給 B 支付了 41 0561 聰(另外 418 聰是手續費)

支付面額整數假設壓倒性傾向於情形 1 。此外,接收者的輸入(1 9358 聰)小於發送者的找零(42 9919 聰),這跟 UIH2 (多餘輸入)假設一致:發送者沒有合適面額的錢幣,因此只好提供一個大額輸入並創建一個大額找零,同時,接收方的錢幣是恰好能覆蓋支付數額的。一種可能的區隔是:0 號輸出(2 9358 聰)是支付,1 號輸入(1 9358 聰)是收款方的 UTXO,1 號輸出(42 9919 聰)是發送者的找零。

跨交易分析也支持這種結論。0 號輸入的前序交易有一個輸入的 nSequence 數值是 0x01,但另一個輸入的 nSequence 數值是 0xfffffffd。這種不對稱讓該交易的輸入有了區別,使我們知道它的面值為 44 0337 的輸出屬於使用 nSequence 數值為 0x01 的參與者;這個輸出作為 0 號輸入,進入了我們視察的這筆 payjoin 交易。

1 號輸入的前序交易的所有輸出都只使用 0xfffffffd 作為 nSequence 數值。此外,花費 1 號輸出的交易也將nSequence 數值設為 0xfffffffd。這跟 Bull Bitcoin Mobile 的典型動作一致,因此是 1 號輸出及 1 號輸入屬於同一個錢包的有力證據。

┌──────────────────────────┐│ PRIOR TX (9ecd77...)     ││                          ││ in_0 [seq=1]             ││ in_1 [seq=MAX-2]         ││ ──────────────────────── ││ out_0: 204,326           ││ out_1: 440,337 ──────────────────┐└──────────────────────────┘       │                                   │   ┌──────────────────────────┐                                   │   │ PAYJOIN TX (8fb805...)   │                                   │   │ ──────────────────────── │                                   └──►│ in_0 [seq=1]             │                                   ┌──►│ in_1 [seq=1]             │┌──────────────────────────┐       │   │ ──────────────────────── ││ PRIOR TX (3fbe17...)     │       │   │ out_0:  29,358           ││                          │       │   │ out_1: 429,919 ──────────────────┐│ in_0 [seq=MAX-2]         │       │   └──────────────────────────┘       ││ in_1 [seq=MAX-2]         │       │                                      ││ ──────────────────────── │       │                                      ││ out_0: 430,856           │       │   ┌──────────────────────────┐       ││ out_1:  19,358 ──────────────────┘   │ SUBSEQUENT TX (9232d5...)│       │└──────────────────────────┘           │ ──────────────────────── │       │                                       │ in_0 [seq=MAX-2] ◄───────────────┘                                       │ ...                      │                                       └──────────────────────────┘

兩個輸入的指紋都在交易圖譜中持久存在。Cake 的 0x01 可以追溯到前序交易,Bull Bitcoin Mobile 的 0xfffffffd 可以前向追溯,也可以後向追溯。單從這一筆 payjoin 交易可能看不出什麼,一旦視察相鄰交易,就會變得清楚。

結論

這些觀察告訴我們,payjoin 的隱私保護效果,只有在參與者的錢包的指紋都同質化的前提下,才能保存。交易層面的指紋同質性是必要的,但還不夠。發送者和接收者在任何維度上的差異,都可能成為一種區分他們的信號。分析者的工作簡化成了在錢包動作中找出這些差異,而交易圖提供了不定數量的觀測值,讓分析者可以找到它們。

雖然一些錢包指紋相對容易消除,還有一些指紋內生於該錢包的一些具體的設計抉擇和目標,無法簡單 “修復”。在集成 payjoin 到自己的軟件時,錢包開發者應該意識到潛在的隱私性洩露。

本文中的分析進關注單筆 payjoin 交易,但方法可以普遍化。錢包指紋的分佈可以成為集群的一個屬性,具有一致指紋的一個集群,比內部存在不兼容特徵的集群更加可信。這指出了對 payjoin 隱私性的一類廣泛的攻擊。未來的工作將形成一套自動化的大規模分析,使用指紋分佈來給集群的可信度評分。我們正在開發這樣的工具,作為我們的隱私性指標框架的一部分。

(完)

相关赛道:
來源
免責聲明:以上內容僅為作者觀點,不代表Followin的任何立場,不構成與Followin相關的任何投資建議。
喜歡
收藏
評論