HD Wallet 完整實作指南:從 BIP-32/39/44 到 testnet 金鑰派生
深入探討階層式確定性錢包(HD Wallet)的技術原理與實作。涵蓋 BIP-32 派生机制的數學推導、BIP-39 助記詞規範、完整的 BIP-44 多帳戶錢包結構,以及從零開始建構 HD Wallet 的 Python 程式碼範例。提供可直接在 testnet 上驗證的完整實作流程。
HD Wallet 完整實作指南:從 BIP-32/39/44 到 testnet 金鑰派生
概述
階層式確定性錢包(Hierarchical Deterministic Wallet,HD Wallet)是現代比特幣錢包的標準技術基礎。HD Wallet 允許從單一種子(Seed)派生出無限數量的金鑰對,極大地簡化了錢包備份流程,並為用戶提供了清晰的帳戶組織結構。本指南將深入探討 HD Wallet 的技術原理、BIP-32/39/44 規範細節,並提供完整的 Python 實作程式碼範例,讓讀者能夠從零開始建構自己的 HD Wallet 並在 testnet 上驗證。
HD Wallet 的設計動機
傳統比特幣錢包的局限性
在 HD Wallet 出現之前,比特幣錢包面臨幾個重要的實際問題:
金鑰管理困難:傳統錢包(Nondaeterministic Wallet)為每筆交易生成新的金鑰對。理論上,一個錢包可能包含數千個獨立金鑰。備份這些金鑰意味著必須定期備份整個錢包文件,否則可能丟失比特幣。
隱私洩露風險:如果攻擊者獲得了錢包的備份,可以推斷出錢包的所有金鑰。此外,若錢包的隱私策略不佳,使用同一金鑰的多次交易可能產生關聯。
金鑰派生的不連續性:使用傳統錢包時,新生成的金鑰無法從舊金鑰推導出來。這意味著錢包必須維護一個完整的金鑰池,增加了同步和存儲的複雜性。
HD Wallet 的核心優勢
HD Wallet 通過階層式確定性派生解決了上述問題:
單一種子備份:從種子(Seed)可以派生出整個錢包的所有金鑰對。用戶只需備份12或24個單詞的助記詞(Mnemonic),即可恢復所有金鑰。
確定性派生:給定相同的種子,總是產生相同的金鑰序列。這消除了「金鑰池同步」的問題。
階層式結構:HD Wallet 具有樹狀結構,允許用戶將不同用途的金鑰組織到不同分支。例如,一個分支用於日常支付,另一個分支用於冷存儲。
隱私保護:不同分支的金鑰之間沒有關聯性。用戶可以將不同分支的公鑰提供給不同的服務商,而不會洩露它們屬於同一錢包。
BIP-32:階層式確定性錢包
技術規範
BIP-32(Bitcoin Improvement Proposal 32)定義了 HD Wallet 的核心技術標準。根據 BIP-32 原始規範:
Master Seed:主種子是一個 128-512 位的隨機數。錢包軟體將其轉換為助記詞,方便人類記憶和抄寫。
Master HMAC:使用 HMAC-SHA512 從主種子派生出:
- Master Private Key(主私鑰,256 位)
- Master Chain Code(主鏈碼,256 位)
子金鑰派生函數 CKDpriv:從父金鑰派生子金鑰,使用以下公式:
CKDpriv((k_par, c_par), i) → (k_i, c_i)
其中:
- k_par: 父私鑰
- c_par: 父鏈碼
- i: 子金鑰索引(32 位整數)
強化派生 vs 普通派生:
普通派生(i < 2^31)允許從公鑰派生子公鑰,無需暴露私鑰。這對接收地址場景很有用。
強化派生(i ≥ 2^31)需要私鑰參與派生。這提供了更高的安全性,因為無法從子公鑰推導父公鑰。建議用於高層次分支(如 m/i')。
BIP-32 派生算法推導
以下是 BIP-32 定義的 CKDpriv 函數詳細推導:
普通派生(Normal Derivation,i ∈ [0, 2^31 - 1]):
I = HMAC-SHA512(Key = c_par, Data = ser_P(point(k_par)) || ser_32(i))
其中:
- ser_P(point(k_par)): 父公鑰的壓縮格式(33 位元組)
- ser_32(i): 子索引的大端序32位元表示
- I_L: 前256位作為子私鑰 k_i = parse_256(I_L)
- I_R: 後256位作為子鏈碼 c_i
強化派生(Hardened Derivation,i ∈ [2^31, 2^32 - 1]):
I = HMAC-SHA512(Key = c_par, Data = 0x00 || ser_256(k_par) || ser_32(i))
其中:
- 0x00: 32位元組的0x00前綴
- ser_256(k_par): 父私鑰(256位)
- k_i = parse_256(I_L)
- c_i = I_R
推導函數的 Python 實現:
import hashlib
import hmac
import ecdsa
import binascii
from ecdsa import SigningKey, SECP256k1
def hmac_sha512(key, data):
"""HMAC-SHA512 計算"""
return hmac.new(key, data, hashlib.sha512).digest()
def point(pk):
"""將私鑰轉換為公鑰點"""
signing_key = SigningKey.from_string(
binascii.unhexlify(pk), curve=SECP256k1
)
verifying_key = signing_key.get_verifying_key()
# 返回壓縮格式公鑰(33位元組)
x = verifying_key.pubkey.point.x()
y = verifying_key.pubkey.point.y()
if y % 2 == 0:
return binascii.hexlify(bytes([0x02]) + x.to_bytes(32, 'big')).decode()
else:
return binascii.hexlify(bytes([0x03]) + x.to_bytes(32, 'big')).decode()
def ser_32(n):
"""將32位元整數序列化為大端序32位元組"""
return n.to_bytes(32, 'big')
def parse_256(s):
"""將32位元組轉換為256位整數(模運算)"""
return int.from_bytes(s, 'big') % ecdsa.SECP256k1.order
def derive_child_private_key(parent_key, parent_chain_code, index, hardened=False):
"""
從父私鑰派生子私鑰
參數:
- parent_key: 父私鑰(64位元組十六進位字串)
- parent_chain_code: 父鏈碼(64位元組十六進位字串)
- index: 子金鑰索引(0 到 2^32-1)
- hardened: 是否為強化派生
返回:(子私鑰, 子鏈碼)
"""
parent_key_bytes = binascii.unhexlify(parent_key)
parent_chain_bytes = binascii.unhexlify(parent_chain_code)
if hardened:
# 強化派生:使用 0x00 || 私鑰 || 索引
data = b'\x00' + parent_key_bytes + ser_32(index)
else:
# 普通派生:使用 公鑰 || 索引
pubkey = point(parent_key)
data = binascii.unhexlify(pubkey) + ser_32(index)
# HMAC-SHA512
I = hmac_sha512(parent_chain_bytes, data)
I_L = I[:32] # 子私鑰
I_R = I[32:] # 子鏈碼
# 計算子私鑰:k_i = parse_256(I_L) + k_par (mod n)
il_int = parse_256(I_L)
parent_int = parse_256(parent_key_bytes)
child_int = (il_int + parent_int) % ecdsa.SECP256k1.order
child_key = child_int.to_bytes(32, 'big')
child_chain_code = I_R
return binascii.hexlify(child_key).decode(), binascii.hexlify(child_chain_code).decode()
Master Key 生成
從 Entropy 生成 Master Key 的完整流程:
def generate_master_key(entropy):
"""
從 Entropy 生成 Master Key
參數:
- entropy: 128/256/512 位元的隨機種子
返回:(master_private_key, master_chain_code)
"""
# 固定字串 "Bitcoin seed"
I = hmac_sha512(b'Bitcoin seed', entropy)
master_key = I[:32]
master_chain_code = I[32:]
return binascii.hexlify(master_key).decode(), \
binascii.hexlify(master_chain_code).decode()
def generate_random_entropy(bits=256):
"""生成隨機 Entropy"""
import secrets
num_bytes = bits // 8
return secrets.token_bytes(num_bytes)
BIP-39:助記詞規範
技術原理
BIP-39 定義了從隨機 Entropy 到人類可讀助記詞的轉換標準。根據 BIP-39 原始規範:
Entropy 長度與助記詞數量對應關係:
| Entropy (bits) | Checksum (bits) | Total (bits) | Mnemonic (words) |
|---|---|---|---|
| 128 | 4 | 132 | 12 |
| 160 | 5 | 165 | 15 |
| 192 | 6 | 198 | 18 |
| 224 | 7 | 231 | 21 |
| 256 | 8 | 264 | 24 |
助記詞詞庫:BIP-39 定義了 2048 個常用英文單詞。根據原始規範,所有助記詞均為精心挑選的低重複率單詞列表,確保:
- 第一個字母具有唯一性(便於輸入纠错)
- 不包含性別歧視詞彙
- 不包含易混淆字符(如 "l" vs "1", "O" vs "0")
Entropy 到助記詞的轉換
# BIP-39 英語詞庫(完整列表有 2048 個單詞)
# 以下為示例片段
WORDLIST = [
"abandon", "ability", "able", "about", "above", "absent", "absorb", "abstract",
# ... 共 2048 個單詞
"zoo", "zooKeeper", "zoom", "zoologist", "zygote"
]
def entropy_to_mnemonic(entropy):
"""
將 Entropy 轉換為助記詞
"""
# 計算 SHA256 校驗和
entropy_hash = hashlib.sha256(entropy).hexdigest()
# Entropy 位元數
entropy_bits = len(entropy) * 8
# Checksum 位元數 = entropy_bits / 32
checksum_bits = entropy_bits // 32
# 總位元數
total_bits = entropy_bits + checksum_bits
# 將校驗和的前 checksum_bits 位附加到 Entropy
checksum_bitstring = bin(int(entropy_hash, 16))[2:].zfill(256)[:checksum_bits]
bitstring = bin(int.from_bytes(entropy, 'big'))[2:].zfill(entropy_bits) + checksum_bitstring
# 每 11 位元為一組,轉換為單詞索引
words = []
for i in range(0, len(bitstring), 11):
bits = bitstring[i:i+11]
index = int(bits, 2)
words.append(WORDLIST[index])
return ' '.join(words)
def mnemonic_to_entropy(mnemonic):
"""
將助記詞轉換為 Entropy
"""
words = mnemonic.split()
# 驗證助記詞長度
if len(words) not in [12, 15, 18, 21, 24]:
raise ValueError("Invalid mnemonic length")
# 將單詞轉換為位元串
bitstring = ''
for word in words:
index = WORDLIST.index(word)
bitstring += bin(index)[2:].zfill(11)
# 提取 Entropy 和 Checksum
entropy_bits = (len(words) * 11) - (len(words) // 3)
entropy_bytes = entropy_bits // 8
entropy = int(bitstring[:entropy_bits], 2).to_bytes(entropy_bytes, 'big')
checksum = bitstring[entropy_bits:]
# 驗證校驗和
expected_checksum = bin(int(hashlib.sha256(entropy).hexdigest(), 16))[2:].zfill(256)[:len(checksum)]
if checksum != expected_checksum:
raise ValueError("Invalid checksum")
return entropy
助記詞到 Seed 的轉換
def mnemonic_to_seed(mnemonic, passphrase=''):
"""
將助記詞轉換為 Seed(使用 PBKDF2)
BIP-39 定義:
- password: UTF-8 NFKD 編碼的助記詞
- salt: UTF-8 NFKD 編碼的 "mnemonic" + passphrase
- iterations: 2048
- key length: 512 bits (64 bytes)
- algorithm: HMAC-SHA512
"""
import unicodedata
# NFKD 正規化
mnemonic = unicodedata.normalize('NFKD', mnemonic)
passphrase = unicodedata.normalize('NFKD', passphrase or '')
# UTF-8 編碼
password = mnemonic.encode('utf-8')
salt = ('mnemonic' + passphrase).encode('utf-8')
# PBKDF2
seed = hashlib.pbkdf2_hmac(
'sha512', password, salt, 2048, dklen=64
)
return binascii.hexlify(seed).decode()
# 完整範例
def generate_hd_wallet():
"""
生成完整的 HD Wallet
返回:助記詞, Seed, Master Key, Master Chain Code
"""
# 1. 生成 256 位元 Entropy
entropy = generate_random_entropy(256)
# 2. 轉換為 24 單詞助記詞
mnemonic = entropy_to_mnemonic(entropy)
# 3. 從助記詞生成 Seed
seed = mnemonic_to_seed(mnemonic)
# 4. 從 Seed 生成 Master Key
entropy_bytes = binascii.unhexlify(seed)
master_key, master_chain_code = generate_master_key(entropy_bytes)
return {
'mnemonic': mnemonic,
'seed': seed,
'master_key': master_key,
'master_chain_code': master_chain_code
}
# 使用範例
wallet = generate_hd_wallet()
print(f"24-Word Mnemonic: {wallet['mnemonic']}")
print(f"Seed (512 bits): {wallet['seed']}")
print(f"Master Private Key: {wallet['master_key']}")
print(f"Master Chain Code: {wallet['master_chain_code']}")
BIP-44:多帳戶錢包結構
路徑層級定義
BIP-44 定義了 HD Wallet 的標準路徑結構(path levels)。根據原始規範:
m / purpose' / coin_type' / account' / change / address_index
各層級含義:
m: Master Keypurpose': BIP 編號(44' 表示 BIP-44)coin_type': 硬幣類型(BTC = 0', Testnet = 1')account': 帳戶索引(從 0' 開始)change: 0 = 外部鏈(接收地址),1 = 內部鏈(找零地址)address_index: 地址索引(從 0 開始)
常見硬幣類型:
| 硬幣 | coin_type |
|---|---|
| Bitcoin | 0' |
| Bitcoin Testnet | 1' |
| Litecoin | 2' |
| Ethereum | 60' |
BIP-44 路徑推導實現
# BIP-44 定義的常量
COIN_BITCOIN = 0
COIN_TESTNET = 1
PURPOSE_BIP44 = 0x8000002C # 44' = 0x8000002C = 2147483692
def bip44_path_to_indices(path):
"""
將 BIP-44 路徑轉換為派生子金鑰的索引列表
例如:"m/44'/1'/0'/0/0" -> [2147483692, 2147483693, 2147483648, 0, 0]
"""
parts = path.strip().split('/')
if parts[0] != 'm':
raise ValueError("Path must start with 'm'")
indices = []
for part in parts[1:]:
hardened = part.endswith("'")
index = int(part.rstrip("'"))
if hardened:
index += 0x80000000 # 強化派生標誌
indices.append(index)
return indices
def derive_path(master_key, master_chain_code, path):
"""
從 Master Key 沿路徑派生子金鑰
"""
indices = bip44_path_to_indices(path)
current_key = master_key
current_chain = master_chain_code
for index in indices:
hardened = index >= 0x80000000
actual_index = index - 0x80000000 if hardened else index
current_key, current_chain = derive_child_private_key(
current_key, current_chain, actual_index, hardened
)
return current_key, current_chain
# 完整的 BIP-44 路徑派生活動
def bip44_derive_addresses(master_key, master_chain_code, count=10):
"""
派生前 N 個接收地址
BIP-44: m/44'/1'/0'/0/address_index
"""
addresses = []
for i in range(count):
# 派生子私鑰:m/44'/1'/0'/0/i
path = f"m/44'/1'/0'/0/{i}"
child_key, child_chain = derive_path(master_key, master_chain_code, path)
# 從私鑰計算公鑰
child_pubkey = point(child_key)
# 計算 P2PKH 地址(testnet)
address = pubkey_to_p2pkh_address(child_pubkey, testnet=True)
addresses.append({
'path': path,
'private_key': child_key,
'public_key': child_pubkey,
'address': address
})
return addresses
def pubkey_to_p2pkh_address(pubkey_hex, testnet=False):
"""
將公鑰轉換為 P2PKH 地址
"""
import base58
# SHA-256
h = hashlib.sha256(binascii.unhexlify(pubkey_hex)).digest()
# RIPEMD-160
r = hashlib.new('ripemd160')
r.update(h)
hash160 = r.hexdigest()
# 版本位元組
version = '6f' if testnet else '00' # testnet = 0x6f, mainnet = 0x00
# 校驗和
payload = version + hash160
checksum = hashlib.sha256(hashlib.sha256(
binascii.unhexlify(payload)
).digest()).hexdigest()[:8]
# Base58Check 編碼
data = binascii.unhexlify(payload + checksum)
return base58_encode(data)
def base58_encode(data):
"""Base58 編碼"""
alphabet = '123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz'
leading_zeros = len(data) - len(data.lstrip(b'\x00'))
num = int.from_bytes(data, 'big')
result = []
while num > 0:
num, rem = divmod(num, 58)
result.append(alphabet[rem])
return 'n' * leading_zeros + ''.join(reversed(result))
# 使用範例
wallet = generate_hd_wallet()
addresses = bip44_derive_addresses(
wallet['master_key'],
wallet['master_chain_code'],
count=5
)
print("=" * 80)
print("BIP-44 Testnet Addresses (m/44'/1'/0'/0/i)")
print("=" * 80)
for addr in addresses:
print(f"\nPath: {addr['path']}")
print(f"Address: {addr['address']}")
print(f"Public Key: {addr['public_key']}")
完整實作:比特幣 HD Wallet 類
以下是 HD Wallet 的完整 Python 類實現:
class HDWallet:
"""完整的 HD Wallet 實現"""
def __init__(self, mnemonic=None, seed=None, passphrase=''):
"""
初始化 HD Wallet
參數:
- mnemonic: 24 或 12 單詞助記詞
- seed: 512 位元 Seed(可選)
- passphrase: 可選的密碼短語
"""
if mnemonic:
self.mnemonic = mnemonic
self.seed_hex = mnemonic_to_seed(mnemonic, passphrase)
elif seed:
self.seed_hex = seed
self.mnemonic = None
else:
# 生成新的錢包
entropy = generate_random_entropy(256)
self.mnemonic = entropy_to_mnemonic(entropy)
self.seed_hex = mnemonic_to_seed(self.mnemonic, passphrase)
# 從 Seed 生成 Master Key
entropy_bytes = binascii.unhexlify(self.seed_hex)
self.master_key, self.master_chain_code = generate_master_key(entropy_bytes)
# 錢包元數據
self.accounts = {}
def derive_private_key(self, path):
"""沿指定路徑派生子私鑰"""
return derive_path(self.master_key, self.master_chain_code, path)[0]
def derive_public_key(self, path):
"""沿指定路徑派生子公鑰"""
private_key = self.derive_private_key(path)
return point(private_key)
def derive_address(self, path, address_type='p2pkh', testnet=True):
"""
派生子地址
地址類型:
- 'p2pkh': Legacy 地址(1 或 m/n)
- 'p2sh-p2wpkh': 嵌套隔離見證(3 或 2)
- 'p2wpkh': 原生隔離見證(bc1q 或 tb1q)
"""
public_key = self.derive_public_key(path)
if address_type == 'p2pkh':
return pubkey_to_p2pkh_address(public_key, testnet)
elif address_type == 'p2sh-p2wpkh':
return pubkey_to_p2sh_p2wpkh_address(public_key, testnet)
elif address_type == 'p2wpkh':
return pubkey_to_p2wpkh_address(public_key, testnet)
else:
raise ValueError(f"Unknown address type: {address_type}")
def get_receive_addresses(self, account=0, change=0, count=10, address_type='p2pkh', testnet=True):
"""派生成年接收地址"""
addresses = []
for i in range(count):
path = f"m/44'/1'/{account}'/{change}/{i}"
addr = self.derive_address(path, address_type, testnet)
addresses.append({
'path': path,
'address': addr,
'private_key': self.derive_private_key(path)
})
return addresses
def get_change_addresses(self, account=0, count=10, address_type='p2pkh', testnet=True):
"""派生成年找零地址"""
return self.get_receive_addresses(account, 1, count, address_type, testnet)
def to_dict(self):
"""導出錢包資訊"""
return {
'mnemonic': self.mnemonic,
'seed': self.seed_hex,
'master_key': self.master_key,
'master_chain_code': self.master_chain_code
}
# 輔助函數:P2SH-P2WPKH 和 P2WPKH 地址
def pubkey_to_p2sh_p2wpkh_address(pubkey_hex, testnet=False):
"""生成 P2SH-P2WPKH(嵌套隔離見證)地址"""
import base58
# 1. 計算 P2WPKH 腳本哈希
h = hashlib.sha256(binascii.unhexlify(pubkey_hex)).digest()
r = hashlib.new('ripemd160')
r.update(h)
witness_program = r.hexdigest()
# 2. 構建 P2WSH 腳本
witness_script = '0020' + witness_program # OP_0 <20 bytes>
# 3. 計算腳本哈希
script_hash = hashlib.sha256(binascii.unhexlify(witness_script)).digest()
r2 = hashlib.new('ripemd160')
r2.update(script_hash)
script_hash160 = r2.hexdigest()
# 4. P2SH 地址
version = 'c4' if testnet else '05'
payload = version + script_hash160
checksum = hashlib.sha256(hashlib.sha256(
binascii.unhexlify(payload)
).digest()).hexdigest()[:8]
return base58_encode(binascii.unhexlify(payload + checksum))
def pubkey_to_p2wpkh_address(pubkey_hex, testnet=False):
"""生成原生隔離見證(P2WPKH)地址"""
import base58
# 1. 計算 P2WPKH 腳本哈希
h = hashlib.sha256(binascii.unhexlify(pubkey_hex)).digest()
r = hashlib.new('ripemd160')
r.update(h)
witness_program = r.hexdigest()
# 2. Bech32 編碼
return bech32_encode(pubkey_hex, witness_program, testnet)
def bech32_encode(prefix, data, testnet=False):
"""Bech32 編碼(用於隔離見證地址)"""
# Bech32 字符集
charset = "qpzry9x8gf2tvdw0s3jn54khce6mua7l"
# Bech32m(用於版本 0+)
hrp = "tb" if testnet else "bc"
# 轉換為 5 位元基
combined = convertbits(binascii.unhexlify('00' + data), 8, 5)
# 計算校驗和
values = [ord(c) >> 5 for c in hrp] + [0] + [ord(c) & 31 for c in hrp] + combined
polymod = bech32_polymod(values + [0, 0, 0, 0, 0, 0]) ^ 1
checksum = [(polymod >> 5 * (5 - i)) & 31 for i in range(6)]
return hrp + '1' + ''.join([charset[v] for v in combined + checksum])
def convertbits(data, frombits, tobits, pad=True):
"""將位元組轉換為不同的基"""
acc = 0
bits = 0
ret = []
maxv = (1 << tobits) - 1
max_acc = (1 << (frombits + tobits - 1)) - 1
for value in data:
if value < 0 or (value >> frombits):
return None
acc = ((acc << frombits) | value) & max_acc
bits += frombits
while bits >= tobits:
bits -= tobits
ret.append((acc >> bits) & maxv)
if pad:
if bits:
ret.append((acc << (tobits - bits)) & maxv)
elif bits >= frombits or ((acc << (tobits - bits)) & maxv):
return None
return ret
def bech32_polymod(values):
"""Bech32 校驗和多項式"""
generator = [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 ^= generator[i] if ((top >> i) & 1) else 0
return chk
testnet 驗證完整指南
使用 testnet 驗證 HD Wallet
以下是使用 testnet 驗證 HD Wallet 的完整流程:
import requests
def test_wallet_on_testnet(hd_wallet):
"""
在 testnet 上測試 HD Wallet
流程:
1. 從水龍頭取得 testnet 比特幣
2. 接收比特幣到錢包地址
3. 將比特幣轉移到另一地址
"""
print("=" * 80)
print("Testnet HD Wallet 驗證")
print("=" * 80)
# 1. 生成 5 個接收地址
addresses = hd_wallet.get_receive_addresses(
account=0,
change=0,
count=5,
address_type='p2wpkh',
testnet=True
)
print("\n生成的 Testnet 地址(原生隔離見證):")
for i, addr in enumerate(addresses):
print(f" [{i}] {addr['address']}")
# 2. 從水龍頭領取測試比特幣
test_address = addresses[0]['address']
print(f"\n請從以下水龍頭領取測試比特幣到此地址:")
print(f" {test_address}")
print(f"\n水龍頭連結:")
print(f" - Blockstream: https://blockstream.info/testnet/faucet")
print(f" - Bitcoin Faucet: https://bitcoinfaucet.uo1.net/")
# 3. 查詢餘額
balance = get_testnet_balance(test_address)
print(f"\n當前餘額:{balance} satoshis")
# 4. 構造並廣播交易
if balance > 1000:
# 發送 1000 satoshis 到另一地址
dest_address = addresses[1]['address']
txid = send_testnet_transaction(
private_key=addresses[0]['private_key'],
from_address=test_address,
to_address=dest_address,
amount_sats=1000
)
print(f"\n交易已廣播:{txid}")
else:
print("\n請先領取測試比特幣")
return addresses
def get_testnet_balance(address):
"""查詢 testnet 地址餘額"""
url = f"https://blockstream.info/testnet/api/address/{address}"
try:
response = requests.get(url, timeout=10)
if response.status_code == 200:
data = response.json()
return data['chain_stats']['funded_txo_sum'] - data['chain_stats']['spent_txo_sum']
except:
pass
return 0
def send_testnet_transaction(private_key, from_address, to_address, amount_sats):
"""在 testnet 上發送交易"""
# 此函數需要完整的交易構造和簽名邏輯
# 參見 bitcoin-script-basics.md 中的交易建構範例
pass
# 完整範例執行
def main():
# 使用標準 BIP-39 助記詞創建錢包
# 警告:此助記詞僅用於測試,勿用於實際資金
test_mnemonic = "abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about"
print("創建 HD Wallet...")
wallet = HDWallet(mnemonic=test_mnemonic)
print(f"助記詞: {wallet.mnemonic}")
print(f"Master Key: {wallet.master_key[:16]}...")
# 派生成年 BIP-44 地址
print("\n派生成年接收地址 (m/44'/1'/0'/0/i):")
addresses = wallet.get_receive_addresses(
account=0,
change=0,
count=3,
address_type='p2wpkh',
testnet=True
)
for addr in addresses:
print(f" {addr['path']}: {addr['address']}")
# testnet 驗證
test_wallet_on_testnet(wallet)
if __name__ == "__main__":
main()
測試指令
在 testnet 上驗證 HD Wallet 的完整指令:
# 1. 創建測試錢包並導出地址
python hd_wallet.py
# 2. 從水龍頭領取測試比特幣
# 將上面生成的 tb1q 地址粘貼到水龍頭表單
# 3. 使用 blockstream 查詢餘額
curl https://blockstream.info/testnet/api/address/tb1qw508d6qejxtdg4y5r3zarvary0c5xw7kxpjzsx
# 4. 使用 bitcoin-cli 驗證(需運行 testnet 節點)
bitcoin-cli -testnet getnewaddress "" "bech32"
bitcoin-cli -testnet generate 1
bitcoin-cli -testnet listunspent
安全性最佳實踐
助記詞保護
備份策略:
- 將助記詞手抄在紙上,存放於多個安全位置
- 建議使用不鏽鋼板刻錄,防火防水
- 避免數位化存儲(截圖、雲端、郵件等)
密碼短語(Passphrase):
- BIP-39 支持可選的 passphrase,增加安全性
- 即使助記詞被盜,攻擊者仍無法派生正確的金鑰
- 請牢記 passphrase,丟失後無法恢復
def create_secure_wallet():
"""創建安全的 HD Wallet"""
import secrets
# 生成高 Entropy 的助記詞
entropy = secrets.token_bytes(32) # 256 位元
mnemonic = entropy_to_mnemonic(entropy)
# 用戶設置密碼短語
passphrase = secrets.token_urlsafe(16)
# 創建錢包
wallet = HDWallet(mnemonic=mnemonic, passphrase=passphrase)
return wallet, passphrase
# 警告:passphrase 必須牢記!
wallet, passphrase = create_secure_wallet()
print(f"Passphrase (請牢記!): {passphrase}")
金鑰管理策略
冷存儲錢包:
def create_cold_storage():
"""
創建冷存儲錢包
使用網路隔離設備生成,之後永不連網
"""
# 在完全離線的環境中執行
mnemonic = generate_hd_wallet()['mnemonic']
# 僅將 BIP-44 派生的最後一個地址索引導出
# 其餘資訊(助記詞、金鑰)永不接觸網路
return {
'mnemonic_hash': hashlib.sha256(mnemonic.encode()).hexdigest(),
'purpose': 44,
'coin': 0,
'account': 0
}
多籤錢包:
def create_multisig_wallet(threshold, total_signers):
"""
創建多籤 HD Wallet
每個參與者持有自己的 HD Wallet
通過 M-of-N 派生存款地址
"""
wallets = [HDWallet() for _ in range(total_signers)]
# 收集所有參與者的第一個公鑰
pubkeys = [wallet.derive_public_key("m/44'/0'/0'/0/0") for wallet in wallets]
# 生成多籤 P2SH 地址
multisig_script = create_multisig_script(pubkeys, threshold, total_signers)
return {
'wallets': wallets,
'pubkeys': pubkeys,
'threshold': threshold,
'multisig_address': script_to_p2sh_address(multisig_script)
}
常見問題解答
Q: 為什麼要使用強化派生(hardened derivation)?
A: 強化派生需要父私鑰才能派生子金鑰,而普通派生可以從父公鑰派生子公鑰。如果使用普通派生於高層級分支,一旦子公鑰被洩露,攻擊者可能通過暴力搜索父公鑰來推導整個錢包。建議對 purpose'、coin_type' 和 account' 使用強化派生。
Q: BIP-39 助記詞是否安全?
A: 12 單詞助記詞提供 128 位元 Entropy(2^128 種可能性),24 單詞提供 256 位元。考慮到硬體錢包的迭代速度,BIP-39 助記詞在可預見的未來是安全的。但請確保使用真正的隨機種子,而非人為選擇的助記詞。
Q: 錢包升級時如何保持相容性?
A: 只要遵循 BIP-44 標準,錢包可以在不同軟體之間完全相容。從助記詞可以恢復所有派生的金鑰。建議使用經過審計的開源錢包軟體。
結論
HD Wallet 是比特幣生態系統中最重要的技術創新之一。通過 BIP-32/39/44 標準,用戶可以安全、便捷地管理比特幣金鑰。本指南提供了從理論到實作的完整解説,包括助記詞生成、金鑰派生、地址計算和 testnet 驗證。建議讀者在 testnet 上反覆練習,熟悉完整流程後再在 mainnet 上操作。
如需進一步學習,建議閱讀 BIP-32、BIP-39 和 BIP-44 原始規範,並研究比特幣核心錢包的實現代碼。掌握 HD Wallet 原理,是深入理解比特幣錢包安全的必要前提。
參考文獻
- BIP-32: Hierarchical Deterministic Wallets - https://github.com/bitcoin/bips/blob/master/bip-0032.mediawiki
- BIP-39: Mnemonic Code for Generating Deterministic Keys - https://github.com/bitcoin/bips/blob/master/bip-0039.mediawiki
- BIP-44: Multi-Account Hierarchy for Deterministic Wallets - https://github.com/bitcoin/bips/blob/master/bip-0044.mediawiki
- Bitcoin Wiki: Technical background of version 1 Bitcoin addresses - https://en.bitcoin.it/wiki/Technicalbackgroundofversion1Bitcoinaddresses
- Mastering Bitcoin by Andreas Antonopoulos, Chapter 4 - Keys, Addresses, Wallets
相關文章
- 比特幣錢包內部運作機制完整指南 — 深入解析比特幣錢包的內部運作原理,包括 HD 錢包 BIP-32/39/44 標準、私鑰管理、地址生成、交易建構與簽章流程,以及錢包備份與安全策略。
- 比特幣 BIP 升級進展追蹤:2024-2026 年技術演進完整報告 — 系統性追蹤 2024-2026 年比特幣網路的重要 BIP 進展,包括共識層面、應用層、Layer 2 協議等各維度的技術演進,並分析 Taproot 升級的長期影響與未來發展趨勢。
- 比特幣衍生性金融商品深度技術分析:期貨、選擇權與永續合約的市場結構與定價機制 — 深入分析比特幣期貨、選擇權與永續合約的運作機制、定價模型與市場數據,涵蓋CME比特幣期貨、Deribit選擇權市場、永續合約資金費率機制等專業內容,並提供風險管理策略與機構級風控實踐。
- 比特幣階層確定性錢包(HD Wallet)技術深度解析:從 BIP-32 到 BIP-44 完整指南 — 深入解析比特幣 HD 錢包的密碼學原理與 BIP 標準家族,涵蓋 BIP-32 密鑰派生、BIP-39 助記詞標準、BIP-44 多用途路徑結構,以及在冷熱錢包隔離、多重簽名和企業級比特幣管理中的實際應用。
- 比特幣節點運作完整實戰指南:從安裝到維運的詳細命令手冊 — 比特幣節點是比特幣網路的基石,本文提供從軟硬體準備、軟體安裝、配置文件優化到日常維運的完整實戰指南,包含可直接執行的命令範例與詳細參數說明。
延伸閱讀與來源
這篇文章對您有幫助嗎?
請告訴我們如何改進:
評論
發表評論
注意:由於這是靜態網站,您的評論將儲存在本地瀏覽器中,不會公開顯示。
目前尚無評論,成為第一個發表評論的人吧!