Payjoin 深度解析
Payjoin 隱私交易機制詳解
PayJoin 實作:比特幣隱私的進階技術
PayJoin(又稱 Pay-to-End-Point,P2EP)是比特幣隱私技術中最被低估但最有效的方案之一。本文深入探討 PayJoin 的技術原理、實現方式、使用場景以及其在比特幣隱私生態系統中的獨特地位。
PayJoin 的核心價值
為什麼 PayJoin 與眾不同
隱私技術比較:
┌─────────────────────────────────────────────────────────────┐
│ 隱私技術比較圖 │
├─────────────────────────────────────────────────────────────┤
│ │
│ 技術 外部可識別性 合法外觀 效率 │
│ ───────────────────────────────────────────────────────── │
│ 傳統交易 ✓ 完全可識別 ✓ 普通 ✓ 高 │
│ │
│ CoinJoin ✗ 可識別混合 ✗ 可識別 ✓ 中 │
│ │
│ PayJoin ✗ 不可識別 ✓ 普通 ✓ 中 │
│ │
│ ⚠️ 關鍵差異:PayJoin 看起來就像普通的 P2P 交易 │
│ │
└─────────────────────────────────────────────────────────────┘
與其他隱私技術的互補性
PayJoin 與其他隱私技術可以組合使用:
PayJoin + 其他技術:
1. PayJoin + CoinJoin
┌─────────────────────────────────────────┐
│ 先進行 PayJoin,再進行 CoinJoin │
│ 雙重隱私保護 │
│ • 交易對手不知道資金來源 │
│ • 區塊鏈分析不知道真實金額 │
└─────────────────────────────────────────┘
2. PayJoin + Lightning
┌─────────────────────────────────────────┐
│ 通過 Lightning 發送 PayJoin 輸出 │
│ 增加路由混淆 │
│ • Lightning 節點不知道最終目的地 │
│ • 區塊鏈分析看不到通道餘額 │
└─────────────────────────────────────────┘
3. PayJoin + Tor
┌─────────────────────────────────────────┐
│ 通過 Tor 網路進行 PayJoin 通訊 │
│ • IP 地址隱藏 │
│ • 網路級別隱私 │
└─────────────────────────────────────────┘
技術原理深度解析
交易結構分析
PayJoin 的核心創新在於輸入輸出金額的不對稱性:
class PayJoinTransaction:
"""PayJoin 交易結構分析"""
@staticmethod
def analyze_coinjoin_appearance(payjoin_tx):
"""
分析 PayJoin 的外觀特徵
為什麼外部觀察者無法識別:
"""
# 1. 多元輸入結構
input_structure = {
"num_inputs": payjoin_tx.input_count,
# 2+ 個輸入表示可能是 CoinJoin 或 PayJoin
# 但無法確定是哪種
# 普通 P2P 交易也可能有 2+ 輸入
# (如合併 UTXO)
}
# 2. 金額不對稱(關鍵特徵)
amount_analysis = {
# 輸入金額總和 > 輸出金額總和(礦工費)
"input_sum": payjoin_tx.total_input_value,
"output_sum": payjoin_tx.total_output_value,
"difference": payjoin_tx.miner_fee,
# 這與 CoinJoin 不同!
# CoinJoin: 輸入 ≈ 輸出(各方得到相近金額)
# PayJoin: 輸出明顯小於輸入
}
# 3. 輸出分析
output_analysis = {
# 通常只有 2 個輸出
# • 實際支付金額
# • 找零金額
#
# 這看起來像普通交易!
}
return {
"likely_coinjoin": False, # 無法識別為 CoinJoin
"likely_payjoin": False, # 也無法識別為 PayJoin
"appearance": "ordinary_p2p" # 像是普通交易
}
協定流程
PayJoin 協定完整流程:
┌─────────────────────────────────────────────────────────────┐
│ 協定參與者 │
│ │
│ [付款方 Alice] ◄─────► [收款方 Bob] │
│ │ │ │
│ │ ┌────────────────────┐ │ │
│ └────► PayJoin 協調器 ◄───┘ │
│ └────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────┘
步驟 1:提議(Alice → Bob)
┌─────────────────────────────────────────┐
│ { │
│ "action": "create_payjoin", │
│ "amount": "0.05 BTC", │
│ "receiver_output": "bc1q...", │
│ "payment_id": "uuid..." │
│ } │
└─────────────────────────────────────────┘
步驟 2:準備輸入(Bob → Alice)
┌─────────────────────────────────────────┐
│ { │
│ "utxo": "outpoint:0", │
│ "value": "0.1 BTC", │
│ "pubkey": "02...", │
│ "psbt": "psbt_base64..." │
│ } │
└─────────────────────────────────────────┘
步驟 3:構造交易(Alice)
┌─────────────────────────────────────────┐
│ 交易結構: │
│ │
│ 輸入: │
│ • Alice UTXO: 0.2 BTC │
│ • Bob UTXO: 0.1 BTC │
│ │
│ 輸出: │
│ • Bob: 0.05 BTC(實際支付) │
│ • Alice: 0.24 BTC(找零) │
│ • 礦工費: 0.01 BTC │
└─────────────────────────────────────────┘
步驟 4:盲化簽名(雙方)
┌─────────────────────────────────────────┐
│ • Alice 盲化 Bob 的輸出 │
│ • Bob 用私鑰簽名自己的輸入 │
│ • 雙方交換盲化因子 │
└─────────────────────────────────────────┘
步驟 5:揭示與廣播(Alice)
┌─────────────────────────────────────────┐
│ • 揭示盲化輸出 │
│ • 收集所有簽名 │
│ • 廣播到比特幣網路 │
└─────────────────────────────────────────┘
密碼學基礎
PayJoin 使用盲化技術確保隱私:
class PayJoinBlinding:
"""PayJoin 盲化機制"""
def __init__(self):
self.r = None # 盲化因子(隨機數)
def generate_blinding_factor(self):
"""生成盲化因子"""
self.r = secrets.randbelow(2**256)
return self.r
def blind_output(self, output_amount, receiver_pubkey):
"""
盲化輸出地址
公式:blinded = amount * H(pubkey || r)
"""
# 計算盲化輸出
h = sha256(receiver_pubkey + str(self.r).encode())
blind_factor = int.from_bytes(h, 'big')
blinded_amount = output_amount * blind_factor % SECP256K1_ORDER
return blinded_amount
def unblind_output(self, blinded_amount, receiver_privkey):
"""
揭示盲化輸出
公式:amount = blinded * H(pubkey || r)^(-1) mod n
"""
# 計算盲化因子的逆元
r_inv = pow(self.r, -1, SECP256K1_ORDER)
# 恢復原始金額
h = sha256(self.get_pubkey() + str(self.r).encode())
blind_factor = int.from_bytes(h, 'big')
amount = blinded_amount * pow(blind_factor, -1, SECP256K1_ORDER) % SECP256K1_ORDER
return amount
實作教學
使用 Samourai Wallet
Samourai Wallet 是支援 PayJoin 最完整的錢包之一:
Samourai Wallet PayJoin 操作流程:
1. 發送準備
┌─────────────────────────────────────────┐
│ Samourai Wallet │
│ │
│ 發送比特幣 → 選擇收款人 │
│ │
│ ⚠️ 對方必須也使用支援 │
│ PayJoin 的錢包 │
└─────────────────────────────────────────┘
│
▼
2. 選擇 PayJoin 選項
┌─────────────────────────────────────────┐
│ 發送選項: │
│ │
│ ○ 普通發送 │
│ ● PayJoin(增強隱私) │
│ │
│ 選擇「PayJoin」 │
└─────────────────────────────────────────┘
│
▼
3. 創建提案
┌─────────────────────────────────────────┐
│ 填寫收款人資訊: │
│ │
│ 收款人地址:bc1q... │
│ 金額:0.05 BTC │
│ │
│ 點擊「創建 PayJoin 請求」 │
└─────────────────────────────────────────┘
│
▼
4. 對方確認
┌─────────────────────────────────────────┐
│ 付款人分享請求給收款人 │
│ │
│ 收款人: │
│ • 選擇要貢獻的 UTXO │
│ • 確認金額 │
│ • 完成簽名 │
└─────────────────────────────────────────┘
│
▼
5. 完成交易
┌─────────────────────────────────────────┐
│ 付款人: │
│ • 添加自己的輸入 │
│ • 收集雙方簽名 │
│ • 廣播交易 │
│ │
│ ✅ PayJoin 交易完成 │
│ 看起來像普通 P2P 交易 │
└─────────────────────────────────────────┘
使用 JoinMarket
JoinMarket 也支援 PayJoin 功能:
# JoinMarket PayJoin 命令行操作
# 1. 啟動 JoinMarket
cd joinmarket
python wallet_tool.py create
# 2. 餘額顯示
python wallet_tool.py show
# 3. 執行 PayJoin(maker-taker 模式)
# maker 等待交易,taker 主動發起
# 發起 PayJoin
python paynjoin.py
# 選項:
# -j, --maker 作為 maker 運行
# -m, --taker 作為 taker 運行
# -p, --price-factor 價格加成
開發者實現
對於開發者,以下是 PayJoin 的關鍵實現邏輯:
from btcpay import BTCPayClient
import binascii
class PayJoinClient:
"""PayJoin 用戶端實現"""
def __init__(self, wallet, endpoint):
self.wallet = wallet
self.endpoint = endpoint # BTCPay Server 或其他兼容端點
def request_payjoin(self, amount_sats, destination):
"""
請求 PayJoin
流程:
1. 獲取提議
2. 添加輸入
3. 完成簽名
4. 廣播
"""
# Step 1: 創建基礎交易
psbt = self.create_unsigned_transaction(
amount_sats,
destination
)
# Step 2: 發送到 PayJoin 端點
response = self.endpoint.create_payjoin_proposal(
psbt.to_base64(),
{
"sendAmounts": {
"BTC": amount_sats / 100_000_000
},
"outputs": [
{"destination": destination}
]
}
)
# Step 3: 端點返回包含額外輸入的 PSBT
enriched_psbt = PSBT.from_base64(response["psbt"])
# Step 4: 簽名我們的輸入
enriched_psbt = self.wallet.sign_psbt(enriched_psbt)
# Step 5: 廣播
txid = self.endpoint.broadcast_payjoin(
enriched_psbt.to_base64()
)
return txid
class PayJoinServer:
"""PayJoin 伺服器端實現"""
def __init__(self, wallet):
self.wallet = wallet
def create_proposal(self, client_psbt):
"""
創建 PayJoin 提案
伺服器(收款方)添加自己的輸入
"""
# 解析客戶的 PSBT
psbt = PSBT.from_base64(client_psbt)
# 選擇要貢獻的 UTXO
our_utxo = self.wallet.select_utxo(
target_amount=psbt.output_amount,
exclude=[]
)
if not our_utxo:
raise NoUtxoError("沒有可用的 UTXO")
# 添加我們的輸入
psbt.add_input(our_utxo)
# 計算新的輸出金額
# 客戶支付的金額 = 所有輸入 - 找零 - 礦工費
total_in = psbt.total_input_value
miner_fee = self.estimate_fee(psbt)
client_amount = total_in - miner_fee
# 添加輸出(不改變客戶指定的輸出金額)
# 確保輸出金額與原提議一致
# 簽名我們的輸入
psbt.sign_input(our_utxo.outpoint, self.wallet.key)
return {
"psbt": psbt.to_base64(),
"output_amount": client_amount
}
風險與限制
技術限制
| 限制 | 說明 | 緩解措施 |
|---|---|---|
| 雙方在線 | 必須雙方同時在線 | 使用非同步消息隊列 |
| 錢包相容性 | 需要雙方都支援 PayJoin | 推廣標準化 |
| 金額限制 | 需要足夠的 UTXO | 提前準備 UTXO |
| 隱私泄露 | 對手可能成為弱點 | 選擇可信對手 |
安全考量
PayJoin 安全檢查清單:
□ 對手識別
□ 確認對手身份(如必要)
□ 評估對手誠實度
□ 金額驗證
□ 確認收到正確金額
□ 驗證找零金額正確
□ UTXO 管理
□ 選擇合適的 UTXO
□ 避免價值過高/過低的 UTXO
□ 網路安全
□ 使用 Tor(推薦)
□ 避免 IP 泄露
□ 交易驗證
□ 廣播前驗證簽名
□ 檢查輸入輸出正確
對手模型
PayJoin 對手模型:
1. 誠實對手(正確執行協定)
✓ 雙方獲得隱私收益
2. 惡意對手(試圖學習信息)
• 可能嘗試學習對方的 UTXO
• 可能嘗試操縱金額
3. 協調者(如使用)
• 不應該知道任何輸入輸出對應關係
• 使用盲化技術防止信息泄露
4. 區塊鏈分析
• 看到的是普通 P2P 交易外觀
• 無法識別為 PayJoin
生態系統與標準化
BIP 78 標準
BIP 78 定義了 PayJoin 的標準化協議:
# BIP 78 消息格式示例
# 請求消息(payment request)
{
"url": "https://example.com/payjoin",
"psbt": "cHNidP8BA...", # Base64 編碼的 PSBT
# 可選字段
"memo": "Payment for order #12345",
"payeeEndpoint": "https://receiver.com",
"payeeName": "Merchant Name"
}
# 響應消息
{
"psbt": "cHNidP8BA...", # 包含額外輸入的 PSBT
"error": null // 或錯誤信息
}
兼容性錢包
| 錢包 | PayJoin 版本 | 說明 |
|---|---|---|
| Samourai Wallet | BIP 78 | 完整支持 |
| BlueWallet | BIP 78 | 完整支持 |
| BTCPay Server | BIP 78 | 商戶解決方案 |
| JoinMarket | 自定義 | maker-taker 模式 |
| Armory | 有限支持 | 舊版錢包 |
商家集成
對於商家,PayJoin 可以無縫集成:
# 使用 BTCPay Server 接收 PayJoin
from btcpay import BTCPayClient
# 初始化客戶端
client = BTCPayClient(
url="https://your-btcpay-server.com",
api_key="your-api-key"
)
# 創建 PayJoin 付款請求
invoice = client.create_invoice(
amount=0.05,
currency="BTC",
checkout={"speedPolicy": "mediumSpeed"},
receipt_data={"orderId": "12345"}
)
# 客戶可以使用任何 PayJoin 兼容錢包支付
# BTCPay Server 會自動處理 PayJoin 邏輯
# 查詢付款狀態
status = client.get_invoice(invoice["id"])
print(f"狀態: {status['status']}")
常見問題
Q: PayJoin 與 CoinJoin 有什麼區別?
A:
- CoinJoin:多個用戶合併交易,但金額通常相同
- PayJoin:交易雙方參與,輸入輸出金額不對稱
- PayJoin 更難被識別,外觀像普通 P2P 交易
Q: 雙方都需要使用相同錢包嗎?
A: 不需要,只要雙方都支持 BIP 78 標準即可互通。
Q: PayJoin 是否有金額限制?
A: 取決於錢包可用 UTXO,理論上沒有上限。
Q: 使用 PayJoin 違法嗎?
A: 在大多數國家,PayJoin 是合法的隱私工具。使用前請了解當地法規。
Q: PayJoin 可以完全隱藏交易嗎?
A: PayJoin 增加了極大的隱私,但無法完全隱藏。建議結合其他工具(Tor、CoinJoin)使用。
總結
PayJoin 是比特幣隱私工具箱中強大且獨特的一員:
- 獨特優勢:看起來像普通交易,無法被區塊鏈分析識別
- 雙向隱私:交易雙方都受益
- 標準化:BIP 78 確保了跨錢包兼容性
- 實用性:無需特殊準備,適合日常使用
將 PayJoin 與其他隱私技術(CoinJoin、Lightning、Tor)結合使用,可以構建強大的比特幣隱私保護方案。
更新日期:2026-02-23
版本:1.0
本文包含
相關文章
- WabiSabi 協議:比特幣隱私的金額不可見解決方案 — 深入解析 WabiSabi 隱私協議的密碼學原理、協議流程、實現方式與實際應用。
- 比特幣隱私技術 — 比特幣隱私保護技術介紹
- 隱私錢包深度比較與使用指南 — 全面比較 Wasabi Wallet、Samourai Wallet、JoinMarket、Sparrow Wallet 等主流隱私錢包的功能、優缺點和使用方式。
- CashFusion 協議:比特幣隱私保護的革命性解決方案 — 深入分析 CashFusion 協議的技術原理、密碼學基礎、金額分割機制,以及在比特幣隱私保護領域的創新價值與實際應用場景。
- 比特幣隱私工具實作教學:JoinMarket 流動性提供與 WabiSabi 協議實際操作指南 — 深入探討 JoinMarket Maker 角色與 WabiSabi 協議的實際操作,提供從基礎概念到完整部署步驟的詳細教學,幫助比特幣用戶實現更高級別的隱私保護。
延伸閱讀與來源
這篇文章對您有幫助嗎?
請告訴我們如何改進:
0 人覺得有帮助
評論
發表評論
注意:由於這是靜態網站,您的評論將儲存在本地瀏覽器中,不會公開顯示。
目前尚無評論,成為第一個發表評論的人吧!