比特幣錢包開發完整指南:從 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) | 第三方托管私鑰 | 簡單易用 | 需要信任第三方 |
根據地址格式分類:
| 格式 | 範例 | 比特數量 | 特性 |
|---|---|---|---|
| P2PKH | 1A1zP1eP5QGefi2DMPTfTL5SLmv7DivfNa | 1 | 原始格式,費用較高 |
| P2SH | 3J98t1WpEZ73CNmQviecrnyiWrnqRhWNLy | 3 | 支援多簽和隔離見證 |
| P2WPKH | bc1qar0srrr7xfkvy5l643lydnw9re59gtzzwf5mdq | 42 | 原生隔離見證,費用最低 |
| P2TR | bc1p | 62 | Taproot,隱私和效率最佳 |
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)允許從單一種子派生無限數量的金鑰對,極大地簡化了金鑰管理和備份。
核心優勢:
- 單一備份:只需備份種子即可恢復所有金鑰
- 金鑰分層:支持組織內部的權限分工
- 衍生追溯:可從公鑰推導所有子公鑰(僅限硬化衍生)
- 離線金鑰派生:冷錢包可派生只讀的觀看錢包
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. 結論與延伸學習
比特幣錢包開發是一個複雜但極具價值的領域。本指南涵蓋了從基礎密碼學原理到完整錢包實現的核心知識。
關鍵學習點總結
- 密碼學基礎:理解 secp256k1 橢圓曲線和 ECDSA 簽名
- 地址格式:掌握 P2PKH、P2SH、P2WPKH、P2TR 的原理
- HD 錢包標準:熟悉 BIP-32/39/44/84/86 的實現
- 交易構建:理解 UTXO 模型和交易簽名流程
- 安全實踐:建立完善的安全架構和操作流程
延伸閱讀建議
- BIP-174:PSBT 格式規範
- BIP-174:部分簽名比特幣交易
- BIP-340/341/342:Taproot 升級
- Bitcoin Optech:錢包安全最佳實踐
標籤:比特幣錢包、HD錢包、BIP-39、BIP-32、私鑰、公鑰、地址、交易簽名、多籤錢包、安全、開發者、教程
難度等級:進階
預估閱讀時間:65 分鐘
相關文章:
- Bitcoin Core RPC 完整開發者指南
- 比特幣 Taproot 錢包開發完全指南
- 比特幣硬體錢包與多籤錢包完整實作指南
- 比特幣隱私錢包進階設定完整實戰手冊
相關文章
- 比特幣 HD 錢包 BIP-32/39/44 完整實作教程:從助記詞到派生地址的深度技術指南 — 本教程完整實作比特幣 HD 錢包的各個組件,包括 secp256k1 曲線運算、BIP-39 助記詞標準、BIP-32 密鑰衍生和 BIP-44 多帳戶結構。提供的程式碼涵蓋從密碼學基礎到實際應用的完整流程,可直接用於錢包開發。所有實現都遵循比特幣改進提案的官方規範,並通過了標準測試向量的驗證。
- 比特幣錢包 PSBT 交易流程互動式開發者教學:從基礎到生產環境實踐 — 完整的比特幣 PSBT(部分簽署比特幣交易)開發者指南,涵蓋 BIP-174 標準定義、Bitcoin Core RPC 指令完整範例(walletcreatefundedpsbt、decodepsbt、analyzepsbt、walletprocesspsbt、combinepsbt、finalizepsbt)、PSBT 生命周期管理、硬體錢包離線簽署、多簽錢包協調、CoinJoin 實作、以及生產環境部署的最佳實踐。提供可立即執行的命令列範例。
- Bitcoin Core 開發工作流程完整指南:從源碼到貢獻的實戰攻略 — 深入介紹 Bitcoin Core 的開發環境設置、代碼結構分析、審查流程、以及如何有效地參與貢獻。涵蓋開發環境配置、編譯流程、代碼結構解析、Pull Request 審查機制、測試框架使用、社群參與方式等實用內容,幫助讀者從零開始成為 Bitcoin Core 的有效貢獻者。
- 比特幣階層確定性錢包(HD Wallet)技術深度解析:從 BIP-32 到 BIP-44 完整指南 — 深入解析比特幣 HD 錢包的密碼學原理與 BIP 標準家族,涵蓋 BIP-32 密鑰派生、BIP-39 助記詞標準、BIP-44 多用途路徑結構,以及在冷熱錢包隔離、多重簽名和企業級比特幣管理中的實際應用。
- HD Wallet 完整實作指南:從 BIP-32/39/44 到 testnet 金鑰派生 — 深入探討階層式確定性錢包(HD Wallet)的技術原理與實作。涵蓋 BIP-32 派生机制的數學推導、BIP-39 助記詞規範、完整的 BIP-44 多帳戶錢包結構,以及從零開始建構 HD Wallet 的 Python 程式碼範例。提供可直接在 testnet 上驗證的完整實作流程。
延伸閱讀與來源
這篇文章對您有幫助嗎?
請告訴我們如何改進:
評論
發表評論
注意:由於這是靜態網站,您的評論將儲存在本地瀏覽器中,不會公開顯示。
目前尚無評論,成為第一個發表評論的人吧!