比特幣錢包開發完整指南:從 BIP 規範到硬體錢包整合的深度實戰教學

比特幣錢包是使用者與比特幣網路交互的核心介面,其開發涉及密碼學、區塊鏈資料結構、交易建構與簽名、硬體安全模組整合等多個技術領域。本指南從比特幣改進提案(BIP)的底層規範出發,系統性介紹錢包開發的各個面向:階層式確定性錢包(HD Wallet)的 BIP-32 實作、金鑰衍生機制的 BIP-39 助記詞規範、BIP-44 多帳戶錢包架構、PSBT 部分簽名比特幣交易的完整流程、Wallet Descriptor 格式說明,以及 Coldcard、BitBox02 等主流硬體錢包的 SDK 整合實例。

比特幣錢包開發完整指南:從基礎架構到安全實現

摘要

比特幣錢包是使用者與比特幣網路互動的核心介面,其安全性直接關係到使用者的資產安全。本指南深入探討比特幣錢包的完整開發知識體系,涵蓋錢包類型比較、金鑰管理、位址生成、交易簽名、備份還原、以及安全最佳實踐。讀者將學習如何從零開始構建一個安全可靠的比特幣錢包應用,包括熱錢包、冷錢包和硬體錢包的整合方案。

1. 比特幣錢包基礎架構

1.1 錢包的定義與核心概念

比特幣錢包本質上是一個密鑰管理系統,它存儲和管理用戶的私鑰,並根據這些私鑰生成對應的比特幣地址。比特幣本身並不「存儲」在錢包中,而是記錄在區塊鏈的 UTXO(未花費交易輸出)上,錢包只是提供了訪問和控制這些資產的鑰匙。

核心組件

組件說明
私鑰(Private Key)用於簽署交易的機密資訊,必須妥善保管
公鑰(Public Key)由私鑰推導,用於生成地址和驗證簽名
位址(Address)區塊鏈上的識別符,用於接收比特幣
助記詞(Mnemonic)私鑰的人類可讀備份形式,通常是 12 或 24 個單詞
派生路徑(Derivation Path)從主鑰匙派生特定金鑰的路徑格式

1.2 錢包類型詳解

根據私鑰存儲位置分類

類型說明優點缺點
熱錢包(Hot Wallet)私鑰存儲在連網設備使用方便、交易快速易受網路攻擊
冷錢包(Cold Wallet)私鑰完全離線安全性高操作不便
硬體錢包(Hardware Wallet)專用設備存儲私鑰安全且便攜需要購買設備
托管錢包(Custodial Wallet)第三方托管私鑰簡單易用需要信任第三方

根據地址格式分類

格式範例比特數量特性
P2PKH1A1zP1eP5QGefi2DMPTfTL5SLmv7DivfNa1原始格式,費用較高
P2SH3J98t1WpEZ73CNmQviecrnyiWrnqRhWNLy3支援多簽和隔離見證
P2WPKHbc1qar0srrr7xfkvy5l643lydnw9re59gtzzwf5mdq42原生隔離見證,費用最低
P2TRbc1p62Taproot,隱私和效率最佳

1.3 錢包開發技術棧

# Python 比特幣錢包開發核心依賴
BITCOIN_LIBS = {
    # 密碼學基礎庫
    "secp256k1": "https://github.com/bitcoin-core/secp256k1",
    "cryptography": "https://github.com/pyca/cryptography",
    
    # 比特幣專用庫
    "bitcoinlib": "https://github.com/1200wd/bitcoinlib",
    "btclib": "https://github.com/btccom/btclib",
    "python-bitcoinlib": "https://github.com/petertodd/python-bitcoinlib",
    
    # HD 錢包支援
    "bip32utils": "https://github.com/jleni/bip32utils",
}

2. 密鑰管理與生成

2.1 橢圓曲線密碼學基礎

比特幣使用 secp256k1 橢圓曲線,其方程為:

y² = x³ + 7 (在有限域Fp上)

核心參數

# secp256k1 曲線參數
CURVE_PARAMS = {
    "p": 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFC2F,
    "n": 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141,
    "G": (
        0x79BE667EF9DCBBAC55A06295CE870B07029BFCDB2DCE28D959F2815B16F81798,
        0x483ADA7726A3C4655DA4FBFC0E1108A8FD17B448A68554199C47D08FFB10D4B8
    ),
    "a": 0,
    "b": 7,
}

金鑰生成原理

import secrets
from cryptography.hazmat.primitives.asymmetric import ec
from cryptography.hazmat.backends import default_backend

class KeyGenerator:
    """比特幣金鑰生成器"""
    
    @staticmethod
    def generate_private_key() -> int:
        """
        生成安全的隨機私鑰
        
        私鑰範圍:1 到 n-1
        其中 n = 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141
        """
        while True:
            # 生成 32 字節的密碼學安全的隨機數
            private_key_bytes = secrets.token_bytes(32)
            private_key_int = int.from_bytes(private_key_bytes, 'big')
            
            # 確保私鑰在有效範圍內(1 < k < n)
            if 1 <= private_key_int < CURVE_PARAMS["n"]:
                return private_key_int
    
    @staticmethod
    def private_key_to_wif(private_key: int, compressed: bool = True) -> str:
        """
        將私鑰轉換為 WIF(Wallet Import Format)
        
        WIF 是一種方便導入錢包的私鑰格式
        """
        # 添加前綴(主網:0x80)
        extended_key = bytes([0x80]) + private_key.to_bytes(32, 'big')
        
        if compressed:
            extended_key += bytes([0x01])  # 添加壓縮標記
        
        # Base58Check 編碼
        return base58check_encode(extended_key)
    
    @staticmethod
    def private_key_to_public_key(private_key: int, compressed: bool = True) -> bytes:
        """
        從私鑰推導公鑰
        
        使用橢圓曲線標量乘法:P = k * G
        """
        # 創建橢圓曲線
        curve = ec.SECP256K1()
        
        # 創建私鑰對象
        private_key_obj = ec.EllipticCurvePrivateNumbers(
            private_key,
            curve
        ).private_key(default_backend())
        
        # 推導公鑰
        public_key_obj = private_key_obj.public_key()
        public_numbers = public_key_obj.public_numbers()
        
        x = public_numbers.x.to_bytes(32, 'big')
        y = public_numbers.y.to_bytes(32, 'big')
        
        if compressed:
            # 壓縮格式:0x02 + x(y 為偶數)或 0x03 + x(y 為奇數)
            prefix = bytes([0x02 if public_numbers.y % 2 == 0 else 0x03])
            return prefix + x
        else:
            # 未壓縮格式:0x04 + x + y
            return bytes([0x04]) + x + y

2.2 Base58Check 編碼實現

import hashlib

# Base58 字元集(去除容易混淆的字元:0, O, I, l)
BASE58_ALPHABET = "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz"

def base58_encode(data: bytes) -> str:
    """Base58 編碼"""
    num = int.from_bytes(data, 'big')
    encoded = ""
    
    while num > 0:
        num, remainder = divmod(num, 58)
        encoded = BASE58_ALPHABET[remainder] + encoded
    
    # 添加前導零(作為字元 '1')
    for byte in data:
        if byte == 0:
            encoded = '1' + encoded
        else:
            break
    
    return encoded

def base58check_encode(data: bytes, version: bytes = b'') -> str:
    """
    Base58Check 編碼
    
    防止錯誤:通過添加校驗和(前 4 字節的雙重 SHA-256 哈希)
    """
    # 添加版本字節
    if version:
        data = version + data
    
    # 計算校驗和:SHA256(SHA256(data)) 的前 4 字節
    checksum = hashlib.sha256(hashlib.sha256(data).digest()).digest()[:4]
    
    # 完整數據 = 原始數據 + 校驗和
    full_data = data + checksum
    
    return base58_encode(full_data)

def base58check_decode(address: str) -> bytes:
    """Base58Check 解碼"""
    alphabet_map = {c: i for i, c in enumerate(BASE58_ALPHABET)}
    
    # 解碼 Base58
    num = 0
    for char in address:
        num = num * 58 + alphabet_map[char]
    
    # 轉換為字節
    decoded = num.to_bytes((num.bit_length() + 7) // 8, 'big')
    
    # 移除前導零
    leading_zeros = len(address) - len(address.lstrip('1'))
    decoded = b'\x00' * leading_zeros + decoded
    
    # 驗證校驗和
    checksum = decoded[-4:]
    payload = decoded[:-4]
    
    expected_checksum = hashlib.sha256(
        hashlib.sha256(payload).digest()
    ).digest()[:4]
    
    if checksum != expected_checksum:
        raise ValueError("校驗和驗證失敗")
    
    return payload

2.3 地址生成實現

import hashlib

class AddressGenerator:
    """比特幣地址生成器"""
    
    @staticmethod
    def p2pkh_address(public_key: bytes, mainnet: bool = True) -> str:
        """
        生成 P2PKH 地址(Legacy)
        
        格式:1开头
        """
        version = b'\x00' if mainnet else b'\x6f'
        
        # RIPEMD160(SHA256(public_key))
        sha256_hash = hashlib.sha256(public_key).digest()
        ripemd160_hash = hashlib.new('ripemd160', sha256_hash).digest()
        
        return base58check_encode(ripemd160_hash, version)
    
    @staticmethod
    def p2sh_address(public_key_hash: bytes, mainnet: bool = True) -> str:
        """
        生成 P2SH 地址
        
        格式:3开头
        """
        version = b'\x05' if mainnet else b'\xc4'
        
        return base58check_encode(public_key_hash, version)
    
    @staticmethod
    def p2wpkh_address(public_key: bytes, mainnet: bool = True) -> str:
        """
        生成 P2WPKH 地址(Native SegWit)
        
        格式:bc1开头
        """
        version = b'\x00'
        
        # RIPEMD160(SHA256(public_key))
        sha256_hash = hashlib.sha256(public_key).digest()
        ripemd160_hash = hashlib.new('ripemd160', sha256_hash).digest()
        
        return bech32_encode(ripemd160_hash, version, mainnet)
    
    @staticmethod
    def p2tr_address(public_key: bytes, mainnet: bool = True) -> str:
        """
        生成 P2TR 地址(Taproot)
        
        使用 xonly 公鑰(僅 x 座標)
        BIP-340 標準
        """
        # x-only 公鑰:僅使用壓縮公鑰的 x 座標
        xonly_pubkey = public_key[1:33]  # 移除前綴,僅保留 x
        
        # Taproot 版本:0x01
        # taproot 使用 bech32m
        return bech32_encode(xonly_pubkey, version=1, mainnet=mainnet)

def bech32_encode(data: bytes, version: int, mainnet: bool = True) -> str:
    """
    Bech32/Bech32m 編碼
    
    用於隔離見證地址
    """
    # 轉換表
    BECH32_CHARSET = "qpzry9x8gf2tvdw0s3jn54khce6mua7l"
    
    # 解析數據
    def bech32_polymod(values):
        """BCH 碼多項式"""
        GEN = [0x3b6a57b2, 0x26508e6d, 0x1ea119fa, 0x3d4233dd, 0x2a1462b3]
        chk = 1
        for value in values:
            top = chk >> 25
            chk = (chk & 0x1ffffff) << 5 ^ value
            for i in range(5):
                chk ^= GEN[i] if ((top >> i) & 1) else 0
        return chk
    
    def bech32_hrp_expand(hrp):
        return [ord(x) >> 5 for x in hrp] + [0] + [ord(x) & 31 for x in hrp]
    
    def bech32_create_checksum(hrp, data, spec):
        values = bech32_hrp_expand(hrp) + data
        polymod = bech32_polymod(values + [0, 0, 0, 0, 0, 0]) ^ spec
        return [(polymod >> 5 * (5 - i)) & 31 for i in range(6)]
    
    hrp = "bc" if mainnet else "tb"
    
    # 轉換 5-bit 分組
    converted = []
    for byte in data:
        converted.append(byte & 31)
        converted.append(byte >> 5)
    # 最後一組填充
    converted[-1] = byte >> 5
    
    # 添加版本字節
    combined = [version] + converted
    
    # 計算校驗和
    checksum = bech32_create_checksum(hrp, combined, 1)  # 1 = bech32m
    
    return hrp + '1' + ''.join(BECH32_CHARSET[d] for d in combined + checksum)

3. HD 錢包與 BIP 標準

3.1 分層確定性錢包原理

HD 錢包(Hierarchical Deterministic Wallet)允許從單一種子派生無限數量的金鑰對,極大地簡化了金鑰管理和備份。

核心優勢

  1. 單一備份:只需備份種子即可恢復所有金鑰
  2. 金鑰分層:支持組織內部的權限分工
  3. 衍生追溯:可從公鑰推導所有子公鑰(僅限硬化衍生)
  4. 離線金鑰派生:冷錢包可派生只讀的觀看錢包

3.2 BIP-39 助記詞實現

# BIP-39 英語詞表(精簡展示)
WORDLIST = [
    "abandon", "ability", "able", "about", "above", "absent", "absorb", "abstract",
    "absurd", "abuse", "access", "accident", "account", "accuse", "achieve", "acid",
    "acoustic", "acquire", "across", "act", "action", "actor", "actress", "actual",
    # ... 共 2048 個單詞
]

def generate_mnemonic(strength: int = 128) -> list:
    """
    生成 BIP-39 助記詞
    
    Args:
        strength: 熵的位元數(128 = 12 詞,256 = 24 詞)
    
    Returns:
        助記詞列表
    """
    # 生成隨機熵
    entropy = secrets.token_bytes(strength // 8)
    
    # 計算校驗和(SHA-256 前 strength/32 位)
    checksum = hashlib.sha256(entropy).digest()[0] // (256 // (strength // 8))
    
    # 組合熵和校驗和
    bits = bin(int.from_bytes(entropy, 'big'))[2:].zfill(len(entropy) * 8)
    checksum_bits = bin(checksum)[2:].zfill(strength // 32)
    combined_bits = bits + checksum_bits
    
    # 分成 11 位一組,映射到詞表
    mnemonic = []
    for i in range(0, len(combined_bits), 11):
        index = int(combined_bits[i:i+11], 2)
        mnemonic.append(WORDLIST[index])
    
    return mnemonic

def mnemonic_to_seed(mnemonic: list, passphrase: str = "") -> bytes:
    """
    將助記詞轉換為種子
    
    使用 PBKDF2 函數,100,000 次迭代
    """
    mnemonic_str = ' '.join(mnemonic)
    salt = f"mnemonic{passphrase}".encode('utf-8')
    
    seed = hashlib.pbkdf2_hmac(
        'sha512',
        mnemonic_str.encode('utf-8'),
        salt,
        100_000,
        dklen=64
    )
    
    return seed  # 64 字節的主種子

3.3 BIP-32 派生引擎

from hmac import HMAC

class HDWallet:
    """HD 錢包實現"""
    
    # 硬化衍生前綴
    HARDENED = 0x80000000  # 2^31
    
    def __init__(self, seed: bytes):
        self.seed = seed
        self.master_key = self.derive_master_key(seed)
    
    def ckd_priv(self, parent_key: bytes, index: int) -> bytes:
        """
        派生子私鑰(Child Key Derivation)
        
        支援硬化衍生(index + HARDENED)和普通衍生
        """
        if index >= self.HARDENED:
            # 硬化衍生:使用 0x00 + parent_privkey
            data = bytes([0x00]) + parent_key
        else:
            # 普通衍生:使用 parent_pubkey
            parent_pubkey = self.private_key_to_public_key(parent_key)
            data = parent_pubkey
        
        data += index.to_bytes(4, 'big')
        
        # HMAC-SHA512(curve seed, data)
        hmac = HMAC(b"Bitcoin seed", data, hashlib.sha512).digest()
        
        il = int.from_bytes(hmac[:32], 'big')
        ir = hmac[32:]
        
        # 檢查派生是否有效
        if il >= CURVE_PARAMS["n"]:
            raise ValueError(f"無效的派生 index: {index}")
        
        # 計算子私鑰:il + parent_key (mod n)
        child_key = (il + int.from_bytes(parent_key, 'big')) % CURVE_PARAMS["n"]
        
        if child_key == 0:
            raise ValueError(f"無效的派生 child_key = 0")
        
        return child_key.to_bytes(32, 'big'), ir  # (私鑰, 鏈碼)
    
    def private_key_to_public_key(self, private_key: bytes) -> bytes:
        """從私鑰推導公鑰"""
        # 實現橢圓曲線標量乘法
        k = int.from_bytes(private_key, 'big')
        G = CURVE_PARAMS["G"]
        
        # P = k * G
        P = point_multiply(k, G)
        
        # 返回未壓縮公鑰
        return bytes([0x04]) + P[0].to_bytes(32, 'big') + P[1].to_bytes(32, 'big')
    
    def derive_path(self, path: str) -> bytes:
        """
        根據路徑派生金鑰
        
        格式:m/account/change/address_index
        例如:m/44'/0'/0'/0/0
        
        ' 表示硬化衍生
        """
        components = path.split('/')
        if components[0] != 'm':
            raise ValueError("路徑必須以 'm' 開頭")
        
        key = self.master_key[0]
        chain_code = self.master_key[1]
        
        for component in components[1:]:
            if component.endswith("'"):
                # 硬化衍生
                index = int(component[:-1]) + self.HARDENED
            else:
                # 普通衍生
                index = int(component)
            
            key, chain_code = self.ckd_priv(key, index)
        
        return key

# 常用派生路徑
DERIVATION_PATHS = {
    "legacy": "m/44'/0'/0'",       # BIP-44 Legacy
    "segwit": "m/84'/0'/0'",       # BIP-84 Native SegWit
    "taproot": "m/86'/0'/0'",      # BIP-86 Taproot
    "ledger_legacy": "m/44'/60'/0'",   # Ledger 兼容
    "ledger_segwit": "m/49'/0'/0'",    # BIP-49 P2SH-P2WPKH
}

4. 交易構建與簽名

4.1 UTXO 選擇策略

from typing import List, Tuple

class UTXOSelector:
    """UTXO 選擇器"""
    
    @staticmethod
    def select_all(unspent: List[dict], target_amount: int) -> List[dict]:
        """選擇所有 UTXO"""
        return unspent
    
    @staticmethod
    def select_smallest_first(unspent: List[dict], target_amount: int) -> List[dict]:
        """選擇最小的 UTXO"""
        sorted_utxo = sorted(unspent, key=lambda x: x['value'])
        
        selected = []
        total = 0
        
        for utxo in sorted_utxo:
            selected.append(utxo)
            total += utxo['value']
            
            if total >= target_amount:
                break
        
        return selected
    
    @staticmethod
    def select_random(unspent: List[dict], target_amount: int) -> List[dict]:
        """隨機選擇 UTXO"""
        selected = []
        total = 0
        
        # 隨機打亂
        shuffled = unspent.copy()
        random.shuffle(shuffled)
        
        for utxo in shuffled:
            selected.append(utxo)
            total += utxo['value']
            
            if total >= target_amount:
                break
        
        return selected
    
    @staticmethod
    def select_optimized(unspent: List[dict], target_amount: int,
                         fee_per_byte: int) -> List[dict]:
        """
        優化選擇:最小化總成本(金額 + 費用)
        
        使用貪心算法
        """
        # 計算每個 UTXO 的「性價比」
        # 優先選擇大額 UTXO(減少輸入數量)
        sorted_utxo = sorted(unspent, key=lambda x: -x['value'])
        
        selected = []
        total = 0
        
        for utxo in sorted_utxo:
            selected.append(utxo)
            total += utxo['value']
            
            # 估算選擇後的交易費用
            estimated_fee = (len(selected) * 148 + 2 * 34 + 10) * fee_per_byte
            
            if total >= target_amount + estimated_fee:
                break
        
        return selected

4.2 交易簽名實現

import struct

class TransactionBuilder:
    """交易構建器"""
    
    def __init__(self, network: str = "mainnet"):
        self.network = network
        self.inputs = []
        self.outputs = []
        self.version = 2
        self.locktime = 0
    
    def add_input(self, txid: str, vout: int, sequence: int = 0xffffffff):
        """添加交易輸入"""
        self.inputs.append({
            "txid": txid,
            "vout": vout,
            "sequence": sequence
        })
    
    def add_output(self, address: str, amount: int):
        """添加交易輸出"""
        self.outputs.append({
            "address": address,
            "amount": amount  # satoshis
        })
    
    def set_locktime(self, locktime: int):
        """設置鎖定時間"""
        self.locktime = locktime
    
    def create_unsigned_tx(self) -> bytes:
        """創建未簽名交易"""
        # 序列化交易
        tx = struct.pack("<I", self.version)  # 版本
        
        tx += struct.pack("VarInt", len(self.inputs))  # 輸入數量
        
        for inp in self.inputs:
            tx += bytes.fromhex(inp["txid"])[::-1]  # TXID(小端)
            tx += struct.pack("<I", inp["vout"])  # VOUT
            tx += struct.pack("VarInt", 0)  # scriptSig 長度(未簽名)
            tx += struct.pack("<I", inp["sequence"])  # 序號
        
        tx += struct.pack("VarInt", len(self.outputs))  # 輸出數量
        
        for out in self.outputs:
            # 獲取地址的腳本
            script_pubkey = address_to_scriptpubkey(out["address"])
            tx += struct.pack("<Q", out["amount"])  # 金額
            tx += struct.pack("VarInt", len(script_pubkey))  # scriptPubKey 長度
            tx += script_pubkey
        
        tx += struct.pack("<I", self.locktime)  # 鎖定時間
        
        return tx

def varint_encode(n: int) -> bytes:
    """Variable Integer 編碼"""
    if n < 0xfd:
        return bytes([n])
    elif n < 0x10000:
        return bytes([0xfd]) + struct.pack("<H", n)
    elif n < 0x100000000:
        return bytes([0xfe]) + struct.pack("<I", n)
    else:
        return bytes([0xff]) + struct.pack("<Q", n)

4.3 ECDSA 簽名

import hmac

def sign_message(message: bytes, private_key: int) -> bytes:
    """
    ECDSA 簽名
    
    使用 RFC 6979 確定的隨機數
    """
    # 計算消息哈希
    z = int.from_bytes(hashlib.sha256(message).digest(), 'big')
    
    # 曲線階 n
    n = CURVE_PARAMS["n"]
    G = CURVE_PARAMS["G"]
    
    # RFC 6979 確定性 k
    k = generate_k(private_key, z, n)
    
    # 計算 R
    R = point_multiply(k, G)
    r = R[0] % n
    
    if r == 0:
        raise ValueError("r = 0,簽名失敗")
    
    # 計算 s
    s = (pow(k, -1, n) * (z + r * private_key)) % n
    
    if s == 0:
        raise ValueError("s = 0,簽名失敗")
    
    # 確保 s 在曲線下半部分(低 s)
    if s > n // 2:
        s = n - s
    
    # 轉換為 DER 格式
    return der_encode_signature(r, s)

def generate_k(private_key: int, z: int, n: int) -> int:
    """
    RFC 6979 確定性 k 生成
    """
    # 使用 HMAC-DRBG
    v = b'\x01' * 32
    k = b'\x00' * 32
    
    # 初始化
    k = hmac.new(k, v + b'\x00' + private_key.to_bytes(32, 'big') + z.to_bytes(32, 'big'), hashlib.sha256).digest()
    v = hmac.new(k, v, hashlib.sha256).digest()
    k = hmac.new(k, v + b'\x01' + private_key.to_bytes(32, 'big') + z.to_bytes(32, 'big'), hashlib.sha256).digest()
    v = hmac.new(k, v, hashlib.sha256).digest()
    
    while True:
        # 生成候選 k
        t = b''
        while len(t) < 32:
            v = hmac.new(k, v, hashlib.sha256).digest()
            t += v
        
        k_candidate = int.from_bytes(t, 'big')
        
        if 1 <= k_candidate < n:
            return k_candidate
        
        # 重新計算
        k = hmac.new(k, v + b'\x00', hashlib.sha256).digest()
        v = hmac.new(k, v, hashlib.sha256).digest()

def der_encode_signature(r: int, s: int) -> bytes:
    """DER 格式簽名編碼"""
    # 將 r 和 s 轉換為 bytes
    r_bytes = int_to_der_integer(r)
    s_bytes = int_to_der_integer(s)
    
    # 組合
    signature = bytes([0x30]) + bytes([4 + len(r_bytes) + len(s_bytes)]) + r_bytes + s_bytes
    
    return signature

def int_to_der_integer(n: int) -> bytes:
    """將整數轉換為 DER INTEGER 格式"""
    n_bytes = n.to_bytes((n.bit_length() + 7) // 8, 'big')
    
    # 添加前導零如果最高位為 1(保持正數)
    if n_bytes[0] & 0x80:
        n_bytes = b'\x00' + n_bytes
    
    return bytes([0x02]) + bytes([len(n_bytes)]) + n_bytes

5. 錢包安全最佳實踐

5.1 助記詞安全存儲

class SecureSeedStorage:
    """安全種子存儲"""
    
    @staticmethod
    def create_metal_backup(mnemonic: list) -> str:
        """
        生成金屬備份板刻字內容
        
        使用 BIP-39 助記詞的金屬備份
        """
        return "\n".join([
            f"Word {i+1}: {word}"
            for i, word in enumerate(mnemonic)
        ]) + "\n\n日期:" + datetime.now().strftime("%Y-%m-%d")
    
    @staticmethod
    def encrypt_seed(mnemonic: list, password: str) -> bytes:
        """
        加密助記詞
        
        使用 Argon2id 導出密鑰,AES-256-GCM 加密
        """
        # 使用密碼導出密鑰
        salt = secrets.token_bytes(16)
        
        # Argon2id
        from cryptography.hazmat.primitives.kdf.argon2 import Argon2id
        kdf = Argon2id(
            length=32,
            salt=salt,
            iterations=3,
            memory_cost=65536,  # 64 MB
            parallelism=4
        )
        key = kdf.derive(password.encode())
        
        # AES-256-GCM 加密
        from cryptography.hazmat.primitives.ciphers.aead import AESGCM
        aesgcm = AESGCM(key)
        
        nonce = secrets.token_bytes(12)
        mnemonic_str = " ".join(mnemonic).encode()
        ciphertext = aesgcm.encrypt(nonce, mnemonic_str, None)
        
        # 返回 salt + nonce + ciphertext
        return salt + nonce + ciphertext
    
    @staticmethod
    def decrypt_seed(encrypted: bytes, password: str) -> list:
        """
        解密助記詞
        """
        salt = encrypted[:16]
        nonce = encrypted[16:28]
        ciphertext = encrypted[28:]
        
        # 導出密鑰
        from cryptography.hazmat.primitives.kdf.argon2 import Argon2id
        kdf = Argon2id(
            length=32,
            salt=salt,
            iterations=3,
            memory_cost=65536,
            parallelism=4
        )
        key = kdf.derive(password.encode())
        
        # 解密
        from cryptography.hazmat.primitives.ciphers.aead import AESGCM
        aesgcm = AESGCM(key)
        mnemonic_str = aesgcm.decrypt(nonce, ciphertext, None).decode()
        
        return mnemonic_str.split()

5.2 多籤錢包實現

class MultisigWallet:
    """多簽名錢包"""
    
    def __init__(self, required_sigs: int, pubkeys: list, address_type: str = "p2sh"):
        """
        初始化多簽錢包
        
        Args:
            required_sigs: 需要的簽名數
            pubkeys: 公鑰列表
            address_type: "p2sh" 或 "p2wsh"
        """
        self.required_sigs = required_sigs
        self.pubkeys = pubkeys
        self.address_type = address_type
        
        # 生成多簽腳本
        self.redeem_script = self.create_redeem_script()
        self.address = self.generate_address()
    
    def create_redeem_script(self) -> bytes:
        """創建兌換腳本"""
        # OP_M <pubkeys> OP_N OP_CHECKMULTISIG
        script = bytes([self.required_sigs])  # M
        
        for pubkey in self.pubkeys:
            script += bytes([len(pubkey)]) + pubkey
        
        script += bytes([len(self.pubkeys)])  # N
        script += bytes([0xae])  # OP_CHECKMULTISIG
        
        return script
    
    def generate_address(self) -> str:
        """生成多簽地址"""
        if self.address_type == "p2sh":
            # P2SH-P2WSH 嵌套
            # 首先創建 P2WSH 腳本哈希
            ws_script_hash = hashlib.sha256(self.redeem_script).digest()
            
            # 然後包裹在 P2SH 中
            script_hash = hashlib.new('ripemd160', 
                hashlib.sha256(bytes([0x00]) + ws_script_hash).digest()
            ).digest()
            
            return base58check_encode(script_hash, version=b'\x05')
        
        elif self.address_type == "p2wsh":
            # 原生 P2WSH
            ws_script_hash = hashlib.sha256(self.redeem_script).digest()
            return bech32_encode(ws_script_hash, version=0, mainnet=True)
        
        else:
            # 純 P2SH
            script_hash = hashlib.new('ripemd160',
                hashlib.sha256(self.redeem_script).digest()
            ).digest()
            return base58check_encode(script_hash, version=b'\x05')

5.3 安全性檢查清單

SECURITY_CHECKLIST = {
    "金鑰管理": [
        "✓ 私鑰從不使用網路傳輸",
        "✓ 助記詞在離線環境生成",
        "✓ 使用密碼學安全的隨機數生成器",
        "✓ 助記詞備份存放在安全位置",
        "✓ 考慮使用硬體錢包",
    ],
    
    "交易安全": [
        "✓ 交易前驗證地址",
        "✓ 小額交易測試後再大量轉帳",
        "✓ 使用合理的交易費用",
        "✓ 設置交易確認數要求",
        "✓ 監控大額交易的區塊確認",
    ],
    
    "錢包備份": [
        "✓ 定期備份錢包",
        "✓ 備份存放在多個安全位置",
        "✓ 測試備份的可恢復性",
        "✓ 考慮使用金屬備份板",
        "✓ 建立災難恢復計劃",
    ],
    
    "軟體安全": [
        "✓ 使用最新版本的錢包軟體",
        "✓ 驗證軟體簽名",
        "✓ 避免使用來路不明的軟體",
        "✓ 定期更新作業系統",
        "✓ 使用防火牆和防毒軟體",
    ],
}

6. 實用錢包應用架構

6.1 錢包應用分層架構

┌─────────────────────────────────────────┐
│           UI 層 (Presentation)           │
│    Flutter / React Native / Swift        │
├─────────────────────────────────────────┤
│           業務邏輯層 (Business Logic)     │
│   交易構建 │ 金鑰管理 │ 同步 │ 報價      │
├─────────────────────────────────────────┤
│           數據訪問層 (Data Access)        │
│   Bitcoin Core RPC │ 區塊瀏覽器 API      │
├─────────────────────────────────────────┤
│           密碼學層 (Cryptography)         │
│  secp256k1 │ BIP-32/39/44 │ 加密存儲     │
├─────────────────────────────────────────┤
│           平台整合層 (Platform)          │
│   Secure Enclave │ Keychain │ Keystore   │
└─────────────────────────────────────────┘

6.2 完整錢包類實現

class BitcoinWallet:
    """完整比特幣錢包類"""
    
    def __init__(self, seed: bytes = None, network: str = "mainnet"):
        self.network = network
        self.hdwallet = HDWallet(seed) if seed else None
        self.utxo_cache = {}
    
    @classmethod
    def from_mnemonic(cls, mnemonic: list, network: str = "mainnet"):
        """從助記詞創建錢包"""
        seed = mnemonic_to_seed(mnemonic)
        return cls(seed, network)
    
    @classmethod
    def from_private_key(cls, private_key: int, network: str = "mainnet"):
        """從私鑰創建錢包"""
        # 創建錢包
        wallet = cls(network=network)
        
        # 設置私鑰
        wallet.private_key = private_key
        wallet.public_key = AddressGenerator.p2wpkh_address(
            AddressGenerator.private_key_to_public_key(private_key),
            mainnet=(network == "mainnet")
        )
        
        return wallet
    
    def get_address(self, path: str = "m/84'/0'/0'/0/0") -> str:
        """獲取指定路徑的地址"""
        private_key = self.hdwallet.derive_path(path)
        
        # 生成公鑰
        public_key = AddressGenerator.private_key_to_public_key(private_key)
        
        # 根據路徑生成對應類型的地址
        if "84'" in path:
            return AddressGenerator.p2wpkh_address(public_key, 
                                                    self.network == "mainnet")
        elif "86'" in path:
            return AddressGenerator.p2tr_address(public_key,
                                                  self.network == "mainnet")
        else:
            return AddressGenerator.p2pkh_address(public_key,
                                                  self.network == "mainnet")
    
    def sign_transaction(self, unsigned_tx: bytes, input_path: str) -> bytes:
        """簽名交易"""
        # 派生私鑰
        private_key = self.hdwallet.derive_path(input_path)
        
        # 計算交易哈希
        sighash = self.calculate_sighash(unsigned_tx)
        
        # 簽名
        signature = sign_message(sighash, private_key)
        
        # 添加 SIGHASH_ALL (0x01)
        signature += bytes([0x01])
        
        return signature
    
    def broadcast_transaction(self, signed_tx: bytes) -> str:
        """廣播交易"""
        txid = hashlib.sha256(
            hashlib.sha256(signed_tx).digest()
        ).digest()[::-1].hex()
        
        # 通過 RPC 廣播
        rpc = BitcoinRPC(...)
        rpc.call("sendrawtransaction", signed_tx.hex())
        
        return txid

7. 結論與延伸學習

比特幣錢包開發是一個複雜但極具價值的領域。本指南涵蓋了從基礎密碼學原理到完整錢包實現的核心知識。

關鍵學習點總結

  1. 密碼學基礎:理解 secp256k1 橢圓曲線和 ECDSA 簽名
  2. 地址格式:掌握 P2PKH、P2SH、P2WPKH、P2TR 的原理
  3. HD 錢包標準:熟悉 BIP-32/39/44/84/86 的實現
  4. 交易構建:理解 UTXO 模型和交易簽名流程
  5. 安全實踐:建立完善的安全架構和操作流程

延伸閱讀建議


標籤:比特幣錢包、HD錢包、BIP-39、BIP-32、私鑰、公鑰、地址、交易簽名、多籤錢包、安全、開發者、教程

難度等級:進階

預估閱讀時間:65 分鐘

相關文章

延伸閱讀與來源

這篇文章對您有幫助嗎?

評論

發表評論

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

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