比特幣隱私保護技術實戰:CoinJoin、PayJoin 與 Schnorr 簽名完整程式碼指南

深入分析比特幣隱私保護技術的密碼學原理與實作方式,提供完整的 Python 程式碼範例,涵蓋 CoinJoin、Chaumian CoinJoin、PayJoin、Schnorr 簽名聚合、BIP-47 隱私地址等技術,協助開發者與進階用戶理解並應用這些隱私增強技術。

比特幣隱私保護技術實戰:CoinJoin、PayJoin 與 Schnorr 簽名完整程式碼指南

概述

比特幣常被誤認為是完全匿名的加密貨幣,事實上比特幣區塊鏈是公開可驗證的,每一筆交易都可以被追蹤。鏈上分析公司已開發出高度成熟的追蹤技術,能夠透過交易圖分析、地址聚類與行為模式識別追蹤用戶資金。這種「偽匿名」特性促使比特幣隱私保護技術持續發展,形成了一套完整的隱私增強協議族。

比特幣隱私保護的核心目標是打破交易圖分析(Transaction Graph Analysis)。當前主流技術包括 CoinJoin(混幣)、PayJoin(pay-to-endpoint)、Schnorr 簽名聚合與 BIP-47 隱私地址。這些技術各有特點,適用於不同場景。理解這些技術的原理與實現方式,對於需要保護財務隱私的比特幣用戶至關重要。

本文將深入分析這些隱私保護技術的密碼學原理,並提供可直接運用的 Python 程式碼範例,協助開發者與進階用戶理解並應用這些技術。所有範例均為原理性實現,實際使用時應使用經過審計的成熟錢包軟體。

CoinJoin 技術深度分析

技術原理與設計目標

CoinJoin 是一種將多個用戶的交易輸入與輸出混合的技術,透過打破交易圖的確定性鏈接,使外部觀察者難以確定資金的流向。CoinJoin 的核心思想是:當多個用戶共同創建一筆交易時,輸入與輸出的對應關係被混淆,只有交易參與者知道資金的實際流向。

CoinJoin 的基本流程如下:首先,多個用戶協商確定交易的輸入金額與輸出地址;然後,用戶各自提供簽名,共同構建一筆有效的比特幣交易;最後,交易被廣播到網路中。由於所有輸入與輸出都由多個用戶貢獻,外部觀察者無法確定哪個輸入對應哪個輸出。

# CoinJoin 實現框架
import hashlib
import secrets
import ecdsa
from typing import List, Dict, Tuple
from dataclasses import dataclass

@dataclass
class CoinJoinInput:
    """CoinJoin 輸入"""
    txid: str
    vout: int
    amount_sats: int
    privkey: str  # 實際應使用更安全的方式管理私鑰
    pubkey: str
    
@dataclass
class CoinJoinOutput:
    """CoinJoin 輸出"""
    address: str
    amount_sats: int
    is_change: bool  # 是否為找零輸出

class CoinJoinTransaction:
    """CoinJoin 交易建構類"""
    
    def __init__(self):
        self.inputs: List[CoinJoinInput] = []
        self.outputs: List[CoinJoinOutput] = []
        self.fee_rate_sats_vb = 10  # 默認費用率
    
    def add_input(self, txid: str, vout: int, amount_sats: int, privkey: str):
        """添加輸入"""
        # 從私鑰派生公鑰
        pubkey = CoinJoinTransaction.privkey_to_pubkey(privkey)
        self.inputs.append(CoinJoinInput(txid, vout, amount_sats, privkey, pubkey))
    
    def add_output(self, address: str, amount_sats: int, is_change: bool = False):
        """添加輸出"""
        self.outputs.append(CoinJoinOutput(address, amount_sats, is_change))
    
    @staticmethod
    def privkey_to_pubkey(privkey_hex: str) -> str:
        """從私鑰派生公鑰"""
        # 實際實現需要 secp256k1 曲線運算
        # 此為框架實現
        return hashlib.sha256(bytes.fromhex(privkey_hex)).hexdigest()[:66]
    
    @staticmethod
    def sign_input(input_idx: int, privkey: str, tx_hash: str) -> str:
        """簽名輸入"""
        # 實際實現需要 ECDSA 簽名
        # 此為框架實現
        return f"304402{secrets.token_hex(20)}"
    
    def calculate_fees(self) -> int:
        """計算交易費用"""
        # 估算交易大小
        # 每個輸入約 149 bytes, 每個輸出約 31 bytes
        estimated_size = len(self.inputs) * 149 + len(self.outputs) * 31 + 10
        return int(estimated_size * self.fee_rate_sats_vb)
    
    def build_transaction(self) -> Dict:
        """建構最終交易"""
        total_input = sum(inp.amount_sats for inp in self.inputs)
        total_output = sum(out.amount_sats for out in self.outputs)
        fees = self.calculate_fees()
        
        # 驗證費用
        if total_input < total_output + fees:
            raise ValueError("輸入金額不足")
        
        # 構建交易
        tx = {
            'version': 2,
            'inputs': [
                {
                    'txid': inp.txid,
                    'vout': inp.vout,
                    'scriptSig': f"{inp.pubkey}"
                }
                for inp in self.inputs
            ],
            'outputs': [
                {
                    'value': out.amount_sats,
                    'scriptPubKey': f"OP_DUP OP_HASH160 {out.address} OP_EQUALVERIFY OP_CHECKSIG"
                }
                for out in self.outputs
            ],
            'locktime': 0
        }
        
        return tx

class CoinJoinCoordinator:
    """CoinJoin 協調器"""
    
    def __init__(self, coordinator_pubkey: str):
        self.coordinator_pubkey = coordinator_pubkey
        self.participants: Dict[str, Dict] = {}
        self.phase = 'registration'  # registration -> signing -> broadcast
    
    def register_participant(self, participant_id: str, inputs: List[Tuple], output_address: str):
        """註冊參與者"""
        if self.phase != 'registration':
            raise Exception("當前不是註冊階段")
        
        # 驗證輸入
        total_input = sum(inp[2] for inp in inputs)
        
        self.participants[participant_id] = {
            'inputs': inputs,
            'output_address': output_address,
            'total_input': total_input,
            'signed': False
        }
    
    def create_unsigned_transaction(self) -> Dict:
        """創建未簽名交易"""
        cj_tx = CoinJoinTransaction()
        
        # 添加所有參與者的輸入
        for pid, participant in self.participants.items():
            for txid, vout, amount in participant['inputs']:
                cj_tx.add_input(txid, vout, amount, f"privkey_{pid}")
        
        # 添加所有參與者的輸出(需要打亂順序以增加隱私)
        output_addresses = [p['output_address'] for p in self.participants.values()]
        
        # 簡單分配:總輸入 - 費用 = 總輸出
        total_input = sum(p['total_input'] for p in self.participants.values())
        fees = cj_tx.calculate_fees()
        total_output = total_input - fees
        
        # 每人平均輸出
        per_person = total_output // len(self.participants)
        
        for addr in output_addresses:
            cj_tx.add_output(addr, per_person, is_change=False)
        
        # 任何剩餘作為費用
        remainder = total_output % len(self.participants)
        
        return cj_tx.build_transaction()
    
    def collect_signatures(self, participant_id: str, signatures: List[str]) -> bool:
        """收集簽名"""
        if participant_id not in self.participants:
            raise Exception("未知參與者")
        
        # 驗證簽名(框架實現)
        if len(signatures) != len(self.participants[participant_id]['inputs']):
            raise Exception("簽名數量不正確")
        
        self.participants[participant_id]['signed'] = True
        self.participants[participant_id]['signatures'] = signatures
        
        # 檢查是否所有參與者都已簽名
        all_signed = all(p.get('signed', False) for p in self.participants.values())
        
        return all_signed

# 使用範例
coordinator = CoinJoinCoordinator("02coordinatorpubkey")

# 參與者註冊
coordinator.register_participant(
    "alice",
    [("txid1", 0, 100000), ("txid2", 1, 50000)],  # 輸入
    "bc1qalice..."  # 輸出地址
)

coordinator.register_participant(
    "bob",
    [("txid3", 0, 80000)],  # 輸入
    "bc1qbob..."  # 輸出地址
)

# 協調器創建交易
unsigned_tx = coordinator.create_unsigned_transaction()
print("=== CoinJoin 未簽名交易 ===")
print(f"輸入數量: {len(unsigned_tx['inputs'])}")
print(f"輸出數量: {len(unsigned_tx['outputs'])}")

# 模擬簽名收集
all_signed = coordinator.collect_signatures("alice", ["sig1", "sig2"])
all_signed = coordinator.collect_signatures("bob", ["sig3"])

print(f"\n所有參與者已簽名: {all_signed}")

Chaumian CoinJoin 實現

Chaumian CoinJoin 是由匿名數位現金發明人 David Chaum 提出的改進方案,引入了「盲簽名」(Blind Signature)技術。在 Chaumian CoinJoin 中,協調器無法將輸入與輸出關聯,因為用戶在提交輸入證明時使用了盲化技術。這確保了即使協調器被攻破或被強制協助追蹤,也無法破壞用戶的隱私。

# Chaumian CoinJoin 實現
import hashlib
import secrets
from typing import Dict, List, Optional

class ChaumianCoinJoin:
    """Chaumian CoinJoin 實現類"""
    
    def __init__(self):
        self.phase = 'idle'
        self.registered_inputs: Dict[str, Dict] = {}  # 盲化後的輸入
        self.registered_outputs: Dict[str, Dict] = {}  # 盲化後的輸出
        self.unblinded_proofs: Dict[str, Dict] = {}   # 未盲化的證明
        self.phase_id = secrets.token_hex(8)
    
    def generate_blind_factor(self) -> str:
        """生成盲化因子"""
        return secrets.token_hex(32)
    
    def blind_input_commitment(self, txid: str, vout: int, amount: int, blind_factor: str) -> Dict:
        """盲化輸入承諾"""
        # 承諾 = Blind(txid || vout || amount || blind_factor)
        message = f"{txid}:{vout}:{amount}:{blind_factor}"
        commitment = hashlib.sha256(message.encode()).hexdigest()
        
        return {
            'commitment': commitment,
            'blind_factor': blind_factor,
            'txid': txid,
            'vout': vout,
            'amount': amount
        }
    
    def register_blinded_input(self, user_id: str, commitment: str, proof_of_knowledge: str) -> Dict:
        """註冊盲化輸入"""
        if self.phase != 'input_registration':
            raise Exception("當前不是輸入註冊階段")
        
        self.registered_inputs[user_id] = {
            'commitment': commitment,
            'proof': proof_of_knowledge,
            'verified': False
        }
        
        return {
            'status': 'registered',
            'user_id': user_id,
            'phase_id': self.phase_id
        }
    
    def blind_output(self, output_address: str, amount: int, blind_factor: str) -> Dict:
        """盲化輸出"""
        # 輸出盲化承諾
        message = f"{output_address}:{amount}:{blind_factor}"
        commitment = hashlib.sha256(message.encode()).hexdigest()
        
        return {
            'commitment': commitment,
            'blind_factor': blind_factor,
            'output_address': output_address,
            'amount': amount
        }
    
    def register_blinded_output(self, user_id: str, blinded_output: Dict):
        """註冊盲化輸出"""
        if self.phase != 'output_registration':
            raise Exception("當前不是輸出註冊階段")
        
        self.registered_outputs[user_id] = blinded_output
    
    def verify_blind_signature_knowledge(self, user_id: str, input_commitment: str, output_commitment: str) -> str:
        """
        驗證並生成盲簽名
        這是一個框架實現
        """
        # 協調器驗證用戶確實知道輸入的內容
        # 但不知道輸出是什麼
        
        if user_id not in self.registered_inputs:
            raise Exception("用戶未註冊輸入")
        
        # 生成盲簽名
        # 實際實現需要 RSA 或 ECDSA 盲簽名
        message_to_sign = f"{input_commitment}:{output_commitment}:{self.phase_id}"
        blind_signature = hashlib.sha256(message_to_sign.encode()).hexdigest()
        
        return blind_signature
    
    def unblind_and_verify(self, user_id: str, blind_factor: str, output_address: str, amount: int, blind_signature: str) -> bool:
        """解除盲化並驗證"""
        # 用戶移除盲化因子
        expected_commitment = hashlib.sha256(
            f"{output_address}:{amount}:{blind_factor}".encode()
        ).hexdigest()
        
        # 驗證盲簽名
        expected_sig = hashlib.sha256(
            f"{self.registered_inputs.get(user_id, {}).get('commitment', '')}:{expected_commitment}:{self.phase_id}".encode()
        ).hexdigest()
        
        return blind_signature == expected_sig
    
    def start_round(self):
        """開始新一輪 CoinJoin"""
        self.phase = 'input_registration'
        self.phase_id = secrets.token_hex(8)
        self.registered_inputs = {}
        self.registered_outputs = {}
        
        return {
            'phase': 'input_registration',
            'phase_id': self.phase_id
        }
    
    def switch_to_output_registration(self):
        """切換到輸出註冊階段"""
        if len(self.registered_inputs) < 2:
            raise Exception("參與人數不足")
        
        self.phase = 'output_registration'
        
        return {
            'phase': 'output_registration',
            'participant_count': len(self.registered_inputs)
        }

# 使用範例
ccj = ChaumianCoinJoin()

# 開始新一輪
round_info = ccj.start_round()
print(f"=== Chaumian CoinJoin 第 {round_info['phase_id'][:8]} 輪 ===")
print(f"階段: {round_info['phase']}")

# Alice 註冊
alice_blind_factor = ccj.generate_blind_factor()
alice_input_commit = ccj.blind_input_commitment("txid1", 0, 100000, alice_blind_factor)
alice_reg = ccj.register_blinded_input("alice", alice_input_commit['commitment'], "proof_of_knowledge")

# Bob 註冊
bob_blind_factor = ccj.generate_blind_factor()
bob_input_commit = ccj.blind_input_commitment("txid2", 0, 80000, bob_blind_factor)
bob_reg = ccj.register_blinded_input("bob", bob_input_commit['commitment'], "proof_of_knowledge")

print(f"\n參與者數量: {len(ccj.registered_inputs)}")

# 切換到輸出註冊階段
output_phase = ccj.switch_to_output_registration()
print(f"階段切換: {output_phase['phase']}")

# Alice 註冊盲化輸出
alice_output = ccj.blind_output("bc1qnewaddress1", 90000, alice_blind_factor)
ccj.register_blinded_output("alice", alice_output)

# Bob 註冊盲化輸出
bob_output = ccj.blind_output("bc1qnewaddress2", 70000, bob_blind_factor)
ccj.register_blinded_output("bob", bob_output)

print(f"已註冊輸出: {len(ccj.registered_outputs)}")

PayJoin 隱私保護協議

P2EP 協議原理

PayJoin(又稱 P2EP - Pay to EndPoint)是一種更先進的隱私保護技術,與 CoinJoin 的「打亂輸出」不同,PayJoin 在交易中同時包含付款人與收款人的輸入,使外部觀察者無法確定哪個輸出是「付款」哪個是「找零」。

PayJoin 的核心創新是交易的「輸入覆蓋」。在傳統比特幣交易中,交易輸入總額必須大於輸出總額,差額作為費用。在 PayJoin 中,收款人會贡献一個輸入到交易中,這使得:即使外部觀察者知道交易雙方的地址,也无法确定实际的付款金额。

# PayJoin 實現
import hashlib
import secrets
from typing import Dict, List, Optional, Tuple

class PayJoinSession:
    """PayJoin 會話類"""
    
    def __init__(self, sender_privkey: str, receiver_address: str):
        self.sender_privkey = sender_privkey
        self.sender_pubkey = PayJoinSession.privkey_to_pubkey(sender_privkey)
        self.receiver_address = receiver_address
        self.sender_inputs: List[Dict] = []
        self.receiver_inputs: List[Dict] = []
        self.outputs: List[Dict] = []
        self.session_id = secrets.token_hex(16)
        self.state = 'initialized'
    
    @staticmethod
    def privkey_to_pubkey(privkey: str) -> str:
        """私鑰轉公鑰"""
        return hashlib.sha256(bytes.fromhex(privkey)).hexdigest()[:66]
    
    @staticmethod
    def create_payjoin_proposal(sender_inputs: List[Dict], sender_output: str, 
                                  amount_sats: int) -> Dict:
        """
        創建 PayJoin 提議
        由付款人發起
        """
        # 選擇輸入
        selected_inputs = sender_inputs[:]  # 選擇部分輸入
        
        # 計算總輸入
        total_input = sum(inp['amount'] for inp in selected_inputs)
        
        # 創建提議
        proposal = {
            'session_type': 'payjoin',
            'inputs': [
                {
                    'txid': inp['txid'],
                    'vout': inp['vout'],
                    'amount': inp['amount'],
                    'sequence': 0xffffffff - 1  # 啟用 RBF
                }
                for inp in selected_inputs
            ],
            'outputs': [
                {
                    'address': sender_output,
                    'amount': total_input - amount_sats,  # 找零
                    'is_change': True
                },
                {
                    'address': '',
                    'amount': amount_sats,
                    'is_change': False  # 收款人輸出地址稍後填入
                }
            ],
            'min_fee_rate': 1,  # sat/vB
            'payment_id': secrets.token_hex(8)
        }
        
        return proposal
    
    @staticmethod
    def receiver_contributes_input(proposal: Dict, receiver_input: Dict) -> Dict:
        """
        收款人貢獻輸入
        這增加了交易的複雜性,使輸出難以追蹤
        """
        # 添加收款人輸入
        new_proposal = proposal.copy()
        new_proposal['inputs'].append({
            'txid': receiver_input['txid'],
            'vout': receiver_input['vout'],
            'amount': receiver_input['amount'],
            'sequence': 0xffffffff - 1
        })
        
        return new_proposal
    
    @staticmethod
    def finalize_outputs(proposal: Dict, receiver_address: str, 
                          sender_change_address: str) -> Dict:
        """
        完成輸出配置
        """
        final_proposal = proposal.copy()
        
        # 計算總輸入
        total_input = sum(inp['amount'] for inp in final_proposal['inputs'])
        
        # 計算費用(至少 1 sat/vB)
        estimated_vbytes = len(final_proposal['inputs']) * 149 + 2 * 31 + 10
        min_fee = estimated_vbytes * final_proposal.get('min_fee_rate', 1)
        
        # 輸出總額 = 總輸入 - 費用
        total_output = total_input - min_fee
        
        # 配置輸出
        # 收款人獲得金額(由雙方協商)
        # 付款人獲得找零
        
        # 這裡的關鍵是:輸出金額變得不明確
        # 外部觀察者無法確定哪個輸出是「付款」哪個是「找零」
        
        outputs = [
            {
                'address': sender_change_address,
                'amount': 0,  # 待計算
                'is_change': True
            },
            {
                'address': receiver_address,
                'amount': 0,  # 待計算
                'is_change': False
            }
        ]
        
        # 簡單分配:收款人獲得協議金額,其餘為找零
        # 實際實現需要雙方協商
        payment_amount = 50000  # 示例付款金額
        
        outputs[1]['amount'] = payment_amount
        outputs[0]['amount'] = total_output - payment_amount
        
        final_proposal['outputs'] = outputs
        final_proposal['fee'] = min_fee
        
        return final_proposal
    
    def sign_payjoin_transaction(self, proposal: Dict, signer_privkey: str) -> Dict:
        """
        簽名 PayJoin 交易
        """
        # 為輸入創建解鎖腳本
        signed_tx = {
            'version': 2,
            'inputs': [],
            'outputs': proposal['outputs'],
            'locktime': 0
        }
        
        for inp in proposal['inputs']:
            signed_tx['inputs'].append({
                'txid': inp['txid'],
                'vout': inp['vout'],
                'scriptSig': f"{self.sender_pubkey}",  # 簡化
                'sequence': inp.get('sequence', 0xffffffff)
            })
        
        # 計算交易 ID(用於簽名)
        tx_hex = self.tx_to_hex(signed_tx)
        
        # 簽名
        signature = PayJoinSession.sign_with_privkey(tx_hex, signer_privkey)
        
        # 添加簽名到所有輸入
        for inp in signed_tx['inputs']:
            inp['witness'] = [signature, self.sender_pubkey]
        
        return signed_tx
    
    @staticmethod
    def tx_to_hex(tx: Dict) -> str:
        """交易轉十六進制"""
        # 框架實現
        return "02" + "00" * 100
    
    @staticmethod
    def sign_with_privkey(message: str, privkey: str) -> str:
        """使用私鑰簽名"""
        # 框架實現
        return "30440220" + secrets.token_hex(20)

# PayJoin 使用範例
sender_key = "0000000000000000000000000000000000000000000000000000000000000001"
receiver_address = "bc1qreceiver..."  # 收款人地址

# 付款人創建提議
sender_inputs = [
    {'txid': "aaaa", 'vout': 0, 'amount': 100000},
    {'txid': "bbbb", 'vout': 1, 'amount': 50000}
]
sender_change = "bc1qchange..."

proposal = PayJoinSession.create_payjoin_proposal(
    sender_inputs,
    sender_change,
    80000  # 付款金額
)

print("=== PayJoin 提議 ===")
print(f"付款人輸入: {len(proposal['inputs'])}")
print(f"輸出數量: {len(proposal['outputs'])}")

# 收款人貢獻輸入
receiver_input = {'txid': "cccc", 'vout': 0, 'amount': 20000}
enhanced_proposal = PayJoinSession.receiver_contributes_input(proposal, receiver_input)

print(f"\n收款人貢獻後:")
print(f"總輸入數量: {len(enhanced_proposal['inputs'])}")
total_input = sum(inp['amount'] for inp in enhanced_proposal['inputs'])
print(f"總輸入金額: {total_input} sats")

# 完成輸出配置
final_proposal = PayJoinSession.finalize_outputs(
    enhanced_proposal,
    receiver_address,
    sender_change
)

print(f"\n最終輸出:")
for out in final_proposal['outputs']:
    print(f"  {out['address'][:20]}...: {out['amount']} sats ({'找零' if out['is_change'] else '付款'})")
print(f"費用: {final_proposal['fee']} sats")

Schnorr 簽名與批量驗證

BIP-340 Schnorr 簽名

Schnorr 簽名是比特幣 2021 年 Taproot 升級引入的核心密碼學改進。相比傳統的 ECDSA 簽名,Schnorr 簽名具有可聚合性(Signature Aggregation)與更強的安全性證明。Schnorr 簽名的線性特性使得多個簽名可以被合併為單一簽名,大幅減少多簽名交易的空間佔用,同時提升隱私性。

# Schnorr 簽名實現
import hashlib
import secrets
import random
from typing import Tuple, List

class Point:
    """橢圓曲線點(簡化實現)"""
    
    def __init__(self, x: int, y: int):
        self.x = x
        self.y = y
    
    def __add__(self, other):
        # 曲線上的點加法
        # 框架實現
        return Point(0, 0)
    
    def __mul__(self, scalar: int):
        # 標量乘法
        # 框架實現
        return Point(0, 0)
    
    def __rmul__(self, scalar: int):
        return self.__mul__(scalar)

# secp256k1 曲線參數
P = 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFC2F
N = 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141
G = Point(
    0x79BE667EF9DCBBAC55A06295CE870B07029BFCDB2DCE28D959F2815B16F81798,
    0x483ADA7726A3C4655DA4FBFC0E1108A8FD17B448A68554199C47D08FFB10D4B8
)

class SchnorrSignature:
    """Schnorr 簽名類"""
    
    @staticmethod
    def hash_to_int(data: bytes) -> int:
        """將資料雜湊為整數"""
        return int.from_bytes(
            hashlib.sha256(data).digest(),
            'big'
        ) % N
    
    @staticmethod
    def sign(private_key: int, message: bytes, aux_randomness: bytes = None) -> Tuple[int, int]:
        """
        Schnorr 簽名
        private_key: 私鑰(整數)
        message: 訊息
        aux_randomness: 輔助隨機性
        """
        if aux_randomness is None:
            aux_randomness = secrets.token_bytes(32)
        
        # 計算公鑰
        P = private_key * G
        
        # 選擇隨機nonce
        k = secrets.randbelow(N)
        if k == 0:
            k = 1
        
        # R = k * G
        R = k * G
        
        # 計算挑戰 e = Hash(R || P || m)
        e_data = R.x.to_bytes(32, 'big') + P.x.to_bytes(32, 'big') + message
        e = SchnorrSignature.hash_to_int(e_data)
        
        # 計算回應 s = k + e * private_key (mod N)
        s = (k + e * private_key) % N
        
        return (R.x, s)  # 只需存儲 R 的 x 座標
    
    @staticmethod
    def verify(public_key: Point, message: bytes, signature: Tuple[int, int]) -> bool:
        """
        驗證 Schnorr 簽名
        """
        R_x, s = signature
        
        # 計算挑戰 e
        e_data = R_x.to_bytes(32, 'big') + public_key.x.to_bytes(32, 'big') + message
        e = SchnorrSignature.hash_to_int(e_data)
        
        # 計算 s * G
        sG = s * G
        
        # 計算 R' = s * G - e * P
        eP = e * public_key
        R_prime = sG + (-eP)  # 點減法
        
        # 驗證 R.x == R'
        return R_x == R_prime.x
    
    @staticmethod
    def aggregate_signatures(signatures: List[Tuple[int, int]], 
                             public_keys: List[Point], 
                             message: bytes) -> Tuple[int, int]:
        """
        聚合多個 Schnorr 簽名(MuSig 風格)
        """
        # 計算每個公鑰的權重係數
        L = b''.join(P.x.to_bytes(32, 'big') for P in public_keys)
        L_hash = hashlib.sha256(L).digest()
        
        coefficients = []
        for i, P in enumerate(public_keys):
            coeff_data = L_hash + P.x.to_bytes(32, 'big')
            coeff = SchnorrSignature.hash_to_int(coeff_data) % N
            coefficients.append(coeff)
        
        # 聚合公鑰
        X = sum(coeff * P for coeff, P in zip(coefficients, public_keys))
        
        # 聚合簽名
        R_sum = Point(0, 0)
        s_sum = 0
        
        for sig, coeff in zip(signatures, coefficients):
            R_x, s = sig
            R_sum += Point(R_x, 0)  # 簡化
            s_sum += coeff * s
        
        return (R_sum.x % N, s_sum % N)
    
    @staticmethod
    def batch_verify(public_keys: List[Point], messages: List[bytes], 
                      signatures: List[Tuple[int, int]]) -> bool:
        """
        批量驗證 Schnorr 簽名
        比逐一驗證更高效
        """
        # 計算隨機權重
        seed = b''.join(m for m in messages)
        weights = []
        
        for i in range(len(signatures)):
            weight_data = seed + i.to_bytes(4, 'big')
            weight = SchnorrSignature.hash_to_int(weight_data) % N
            weights.append(weight)
        
        # 聚合驗證
        # 框架實現
        return True

# Schnorr 簽名使用範例
print("=== Schnorr 簽名示例 ===")

# 生成私鑰
privkey = secrets.randbelow(N)
print(f"私鑰: {privkey}")

# 生成公鑰(簡化)
pubkey = privkey * G
print(f"公鑰 X: {pubkey.x}")

# 簽名
message = b"Hello, Bitcoin!"
signature = SchnorrSignature.sign(privkey, message)

print(f"\n簽名:")
print(f"  R.x: {signature[0]}")
print(f"  s: {signature[1]}")

# 驗證
is_valid = SchnorrSignature.verify(pubkey, message, signature)
print(f"\n驗證結果: {'有效' if is_valid else '無效'}")

# 多簽名聚合示例
privkeys = [secrets.randbelow(N) for _ in range(3)]
pubkeys = [k * G for k in privkeys]

print(f"\n=== 多簽名聚合 ===")
print(f"參與者數量: {len(pubkeys)}")

signatures = [SchnorrSignature.sign(k, message) for k in privkeys]
aggregated = SchnorrSignature.aggregate_signatures(signatures, pubkeys, message)

print(f"聚合簽名:")
print(f"  R.x: {aggregated[0]}")
print(f"  s: {aggregated[1]}")
print(f"\n原始簽名總大小: {len(signatures) * 64} bytes")
print(f"聚合後大小: {len(aggregated)} bytes")
print(f"空間節省: {(1 - 64/len(signatures)/64) * 100:.1f}%")

隱私保護最佳實踐

地址管理策略

比特幣隱私保護的首要原則是避免地址重複使用。每次重用地址都會使區塊鏈分析師更容易建立地址與身份的關聯。現代比特幣錢包預設採用 HD(Hierarchical Deterministic)架構,能夠從單一種子生成無限多個地址,這為每次交易使用新地址提供了便利。

# 隱私保護地址策略
class PrivacyAddressManager:
    """隱私保護地址管理類"""
    
    def __init__(self, seed: str):
        self.seed = seed
        self.used_addresses = set()
        self.internal_counter = 0
        self.external_counter = 0
    
    def derive_address(self, is_change: bool = False) -> str:
        """派生新地址"""
        if is_change:
            path = f"m/84'/0'/0'/1/{self.internal_counter}"
            self.internal_counter += 1
        else:
            path = f"m/84'/0'/0'/0/{self.external_counter}"
            self.external_counter += 1
        
        # 實際實現需要 BIP-32/BIP-44 派生
        address = PrivacyAddressManager.derive_from_path(path, self.seed)
        
        return address
    
    @staticmethod
    def derive_from_path(path: str, seed: str) -> str:
        """從路徑派生地址"""
        # 框架實現
        import hashlib
        data = f"{seed}:{path}"
        hash_val = hashlib.sha256(data.encode()).hexdigest()
        return f"bc1q{hash_val[:38]}"
    
    def mark_address_used(self, address: str):
        """標記地址已使用"""
        self.used_addresses.add(address)
    
    def get_fresh_address(self) -> str:
        """獲取新地址"""
        address = self.derive_address(is_change=False)
        return address
    
    def get_change_address(self) -> str:
        """獲取找零地址"""
        return self.derive_address(is_change=True)
    
    def should_request_new_address(self) -> bool:
        """檢查是否需要請求新地址"""
        # 經常更換地址可提升隱私
        return self.external_counter > 10

class CoinSelectionPrivacy:
    """隱私保護的 Coin Selection"""
    
    @staticmethod
    def select_coins_privacy(target_amount: int, available_coins: List[Dict], 
                              privacy_level: str = 'high') -> Dict:
        """
        隱私保護的 Coin Selection
        privacy_level: 'low', 'medium', 'high'
        """
        # 排序可用 UTXO
        sorted_coins = sorted(available_coins, key=lambda x: x['amount'])
        
        selected = []
        total_selected = 0
        
        if privacy_level == 'high':
            # 高隱私:優先選擇與目標金額接近的 UTXO
            # 減少找零金額
            for coin in sorted_coins:
                if total_selected >= target_amount:
                    break
                selected.append(coin)
                total_selected += coin['amount']
        
        elif privacy_level == 'medium':
            # 中隱私:隨機選擇
            import random
            shuffled = available_coins.copy()
            random.shuffle(shuffled)
            
            for coin in shuffled:
                if total_selected >= target_amount:
                    break
                selected.append(coin)
                total_selected += coin['amount']
        
        else:
            # 低隱私:最快路徑
            for coin in sorted_coins:
                if total_selected >= target_amount:
                    break
                selected.append(coin)
                total_selected += coin['amount']
        
        return {
            'selected_coins': selected,
            'total_amount': total_selected,
            'change_amount': total_selected - target_amount,
            'num_coins': len(selected),
            'privacy_level': privacy_level
        }

# 地址管理使用範例
print("=== 隱私保護地址管理 ===")
seed = "your_seed_phrase_here"
addr_manager = PrivacyAddressManager(seed)

# 生成多個地址
for i in range(5):
    addr = addr_manager.get_fresh_address()
    print(f"地址 {i+1}: {addr}")

# 模擬收到比特幣
addr_manager.mark_address_used(addr_manager.get_fresh_address())

# Coin Selection 隱私測試
available = [
    {'txid': 'tx1', 'vout': 0, 'amount': 50000},
    {'txid': 'tx2', 'vout': 1, 'amount': 100000},
    {'txid': 'tx3', 'vout': 0, 'amount': 200000},
    {'txid': 'tx4', 'vout': 2, 'amount': 80000},
    {'txid': 'tx5', 'vout': 1, 'amount': 150000}
]

target = 120000

for level in ['low', 'medium', 'high']:
    result = CoinSelectionPrivacy.select_coins_privacy(target, available, level)
    print(f"\n{level.upper()} 隱私級別:")
    print(f"  選擇 UTXO 數: {result['num_coins']}")
    print(f"  總金額: {result['total_amount']}")
    print(f"  找零金額: {result['change_amount']}")

鏈上行為隱私建議

除了使用專門的隱私協議,日常的比特幣使用習慣也會極大影響隱私保護效果。以下是一些重要的實踐建議:

避免金額標準化:比特幣金額,如 0.1、0.01 等整數,可能成為區塊鏈分析的線索。建議使用隨機金額。

謹慎處理找零地址:自動產生的找零地址應妥善管理,避免與主要地址關聯。

使用獨立錢包:不同用途(長期儲備、日常消費、交易)使用獨立錢包,降低單一錢包被識別的風險。

注意時間模式:避免在特定時間進行大額交易,這可能成為分析線索。

結論

比特幣隱私保護是一個持續演進的領域,技術發展與監管環境的變化都在推動著隱私解決方案的不斷創新。從基本的 CoinJoin 到先進的 PayJoin,再到 Schnorr 簽名聚合,每項技術都在不同層面提升了比特幣的隱私特性。

選擇合適的隱私保護方案需要根據具體需求權衡:對於日常小額支付,閃電網路提供了優秀的隱私保護;對於較大金額的混合需求,CoinJoin 服務更為適合;對於長期的財務隱私,BIP-47 隱私地址與 PayJoin 的組合可能是最佳選擇。

重要的是理解比特幣隱私的層次性:沒有絕對的匿名,只有不同程度的隱私保護。結合多種技術與良好的使用習慣,才能在這個公開的區塊鏈網路中保護好自己的財務隱私。隨著 Taproot 與未來更多升級的實施,比特幣的隱私能力將持續增強,為用戶提供更強大的財務主權保障。

延伸閱讀與來源

這篇文章對您有幫助嗎?

評論

發表評論

注意:由於這是靜態網站,您的評論將儲存在本地瀏覽器中,不會公開顯示。

目前尚無評論,成為第一個發表評論的人吧!