跳至主要內容
高級

Sphinx 洋蔥封包

深入了解閃電網路使用的 Sphinx 洋蔥路由封包格式,如何實現支付路徑的隱私保護。

12 分鐘

什麼是 Sphinx?

Sphinx 是一種密碼學封包格式,用於實現洋蔥路由的隱私保護。 在閃電網路中,發送者使用 Sphinx 封裝支付資訊,每個中繼節點只能看到 下一跳的資訊,無法得知完整路徑或發送者/接收者的身份。

洋蔥類比: Sphinx 封包像洋蔥一樣有多層加密。每個節點剝開一層, 看到自己的指令,然後將剩餘部分傳遞給下一跳。

封包結構

Sphinx Onion Packet Structure (1366 bytes)

┌──────────────────────────────────────────────────────────┐
│ Version (1 byte)                                         │
├──────────────────────────────────────────────────────────┤
│ Ephemeral Public Key (33 bytes)                          │
├──────────────────────────────────────────────────────────┤
│ Routing Info (1300 bytes)                                │
│ ┌────────────────────────────────────────────────────┐  │
│ │ Hop 1 payload (encrypted)                          │  │
│ ├────────────────────────────────────────────────────┤  │
│ │ Hop 2 payload (double encrypted)                   │  │
│ ├────────────────────────────────────────────────────┤  │
│ │ Hop 3 payload (triple encrypted)                   │  │
│ ├────────────────────────────────────────────────────┤  │
│ │ ...                                                │  │
│ ├────────────────────────────────────────────────────┤  │
│ │ Padding (random data)                              │  │
│ └────────────────────────────────────────────────────┘  │
├──────────────────────────────────────────────────────────┤
│ HMAC (32 bytes) - verifies packet integrity              │
└──────────────────────────────────────────────────────────┘

Why Fixed Size Matters:
• Every node sees same size packet
• Cannot infer remaining hops from size
• Supports up to 20 hops

加密過程

Sender Builds Onion

Path: Alice -> Bob -> Carol -> Dave

1. Generate Ephemeral Key Pair
ephemeral_privkey = random()
ephemeral_pubkey = ephemeral_privkey * G

2. Compute Shared Secret for Each Hop (using ECDH)

shared_secret_bob = SHA256(
  ephemeral_privkey * Bob_pubkey
)

shared_secret_carol = SHA256(
  ephemeral_privkey' * Carol_pubkey
)
(ephemeral_privkey' is derived blinded key)

shared_secret_dave = SHA256(
  ephemeral_privkey'' * Dave_pubkey
)

3. Encrypt in Reverse Order (start from last hop)
• First encrypt Dave's payload
• Then encrypt entire packet with Carol's key
• Then encrypt entire packet with Bob's key
• Result: outermost layer is Bob's encryption

解密過程

Relay Node Processes Onion

Bob receives onion:

1. Compute shared secret
   shared_secret = SHA256(Bob_privkey * ephemeral_pubkey)

2. Verify HMAC
   expected_hmac = HMAC(shared_secret, routing_info)
   if hmac != expected_hmac: reject

3. Decrypt routing info
   decrypted = ChaCha20(routing_info, shared_secret)

4. Read own payload
   • short_channel_id: next hop channel
   • amt_to_forward: forward amount
   • outgoing_cltv_value: CLTV

5. Build new onion for next hop
   • Blind ephemeral pubkey
   • Remove own payload
   • Add padding
   • Update HMAC

Ephemeral Pubkey Blinding:
blinding_factor = SHA256(ephemeral_pubkey || shared_secret)
new_ephemeral = ephemeral_pubkey * blinding_factor

Each hop changes ephemeral pubkey, but next hop can still
compute correct shared secret

Payload 格式

TLV Payload Format (Modern)

┌──────────────────────────────────────────────────────────┐
│ length (BigSize): payload length                         │
├──────────────────────────────────────────────────────────┤
│ TLV Records:                                             │
│   type=2: amt_to_forward (tu64)                          │
│   type=4: outgoing_cltv_value (tu32)                     │
│   type=6: short_channel_id (8 bytes)                     │
│   type=8: payment_data (for final hop)                   │
│           -> payment_secret + total_msat                 │
│   [other optional TLVs...]                               │
├──────────────────────────────────────────────────────────┤
│ hmac (32 bytes): HMAC for next packet                    │
└──────────────────────────────────────────────────────────┘

Relay Node vs Final Node Payload:

Relay Node:
• short_channel_id: required
• amt_to_forward: required
• outgoing_cltv_value: required

Final Node:
• amt_to_forward: payment amount
• outgoing_cltv_value: final CLTV
• payment_secret: prevents probing
• total_msat: MPP total
• NO short_channel_id

隱私保護

發送者匿名

中繼節點不知道誰是發送者。第一跳只知道它是第一跳, 但不知道是發送者還是另一個中繼。

接收者匿名

中繼節點不知道誰是接收者。每跳只知道下一跳, 不知道剩餘多少跳。

路徑長度隱藏

固定大小的封包使得無法從大小推斷路徑長度。 填充確保短路徑看起來和長路徑一樣。

不可關聯性

臨時公鑰每跳變化,使得無法將入站和出站封包關聯。 (但 payment_hash 仍可關聯)

錯誤返回

Error Message Onion Encryption

When error occurs:

Failing node builds error message:
• failure_code: error type
• data: additional info

Hop-by-hop encrypted return:
1. Failing node encrypts error with shared secret
2. Each relay node re-encrypts with its shared secret
3. Sender receives multi-layer encrypted error

Sender decryption:
1. Try decrypting with each hop's key
2. Successfully decrypted layer corresponds to failing node
3. Read error code and info

Error Message Structure:
┌──────────────────────────────────────────────────────────┐
│ hmac (32 bytes)                                          │
│ failure_len (2 bytes)                                    │
│ failuremsg (failure_len bytes)                           │
│ pad_len (2 bytes)                                        │
│ pad (pad_len bytes)                                      │
└──────────────────────────────────────────────────────────┘

與 Route Blinding 結合

Route Blinding Enhancement

Traditional Sphinx:
• Sender knows complete path
• Sender knows receiver node
• Invoice exposes receiver location

Sphinx + Route Blinding:

Receiver pre-builds blinded path:
• Entry node pubkey (visible)
• Blinded subsequent nodes
• Encrypted payloads

Sender:
• Builds Sphinx to entry node
• Appends blinded path to onion end
• Does not know path after entry

Result: Sender does not know receiver's real location

限制: 雖然 Sphinx 保護路徑隱私,但 payment_hash 在所有跳間相同, 使得這些 HTLC 可被關聯。PTLCs 將解決此問題。

相關資源

下一步: 了解 網路拓撲 如何影響支付路由。

已複製連結
已複製到剪貼簿