比特幣腳本編程完整實戰指南:從基礎到進階應用

提供從環境搭建到實際應用的完整比特幣腳本編程指南,涵蓋 P2PKH、P2SH、P2WSH 腳本創建、時間鎖定、多籤邏輯以及 Miniscript 抽象層,幫助開發者掌握比特幣底層技術並應用於閃電網路和質押協議。

比特幣腳本編程完整實戰指南 V2:可運作程式碼範例、Bitcoin Core 命令序列與進階腳本開發

摘要

本指南是比特幣腳本語言的完整實戰教程,深度新增可直接運作的程式碼範例、Bitcoin Core 命令序列、Bitcoin Script 實驗室的完整操作流程。涵蓋從基礎指令到進階應用的完整技術譜系,包括 P2PKH、P2SH、P2WSH、P2TR 腳本創建,以及 HTLC、多籤合約、時間鎖定等進階應用的實際程式碼。所有範例均可在比特幣測試網路或本地 Regtest 環境中實際運作。


第一章:比特幣腳本開發環境搭建

1.1 Bitcoin Core 安裝與配置

在 Ubuntu/Debian 上安裝 Bitcoin Core

# 1. 添加 Bitcoin PPA 倉庫
sudo apt-get update
sudo apt-get install -y software-properties-common
sudo add-apt-repository -y ppa:bitcoin/bitcoin

# 2. 安裝 Bitcoin Core
sudo apt-get update
sudo apt-get install -y bitcoin-qt bitcoind

# 3. 驗證安裝
bitcoind --version
# 輸出:Bitcoin Core Daemon version v27.0.0

# 4. 建立配置目錄
mkdir -p ~/.bitcoin
cd ~/.bitcoin

bitcoin.conf 配置文件

# ~/.bitcoin/bitcoin.conf

# 網路配置
testnet=0          # 使用主網(改為 1 則使用測試網)
regtest=1          # 啟用 Regtest 模式

# RPC 配置
rpcuser=bitcoinuser
rpcpassword=bitcoinpassword
rpcport=18443
rpcallowip=127.0.0.1

# 錢包配置
disablewallet=0
mintxfee=0.00001
fallbackfee=0.00001

# 伺服器模式
server=1
daemon=1

# 連接埠配置
port=8333
bind=127.0.0.1

# 其他配置
dbcache=2048
maxconnections=16

1.2 啟動 Regtest 模式

Regtest(回歸測試)模式允許在本地創建私有區塊鏈,非常適合腳本開發:

# 1. 啟動比特幣節點
bitcoind -daemon

# 2. 等待節點啟動
sleep 3

# 3. 創建錢包
bitcoin-cli -rpcpassword=bitcoinpassword createwallet "devwallet" true true "" false false

# 4. 檢查錢包狀態
bitcoin-cli -rpcpassword=bitcoinpassword getwalletinfo

# 5. 生成測試地址
bitcoin-cli -rpcpassword=bitcoinpassword getnewaddress "test_address"

# 6. 挖出初始獎勵(需要 100 個區塊才能花費 coinbase)
bitcoin-cli -rpcpassword=bitcoinpassword generatetoaddress 101 \
  "$(bitcoin-cli -rpcpassword=bitcoinpassword getnewaddress "miner")"

# 7. 檢查餘額
bitcoin-cli -rpcpassword=bitcoinpassword getbalance

1.3 Bitcoin Script 模擬器

對於不涉及真實交易的腳本學習,可以使用 Bitcoin Script 模擬器:

使用 btcdeb 進行腳本調試

# 安裝 btcdeb
git clone https://github.com/siminchen/btcdeb.git
cd btcdeb
./autogen.sh
./configure
make
sudo make install

# 使用範例
btcdeb --txrow='[]' --sig='[01]' --pubkey='[021121...]' OP_CHECKSIG

使用 Elements Project 的腳本模擬

# Elements Project 提供了更完整的腳本測試框架
git clone https://github.com/ElementsProject/elements-miniscript.git
cd elements-miniscript
cargo build --release

第二章:Bitcoin Script 基礎指令實戰

2.1 基本堆疊操作

使用 Bitcoin Core RPC 測試腳本執行

#!/usr/bin/env python3
# bitcoin_script_basics.py

import subprocess
import json

def bitcoin_rpc(command, params=[], wallet="devwallet"):
    """執行 Bitcoin Core RPC 命令"""
    cmd = [
        "bitcoin-cli",
        "-rpcpassword=bitcoinpassword",
        "-rpcwallet=" + wallet,
        command
    ] + params
    
    result = subprocess.run(cmd, capture_output=True, text=True)
    
    if result.returncode != 0:
        raise Exception(f"RPC Error: {result.stderr}")
    
    try:
        return json.loads(result.stdout)
    except:
        return result.stdout.strip()

# 範例:創建一個簡單的 P2PKH 地址
def create_p2pkh_address():
    """創建 P2PKH 地址"""
    address = bitcoin_rpc("getnewaddress", ["legacy"])  # 舊格式地址
    print(f"P2PKH 地址: {address}")
    return address

# 範例:解碼地址
def decode_address(address):
    """解碼比特幣地址"""
    decoded = bitcoin_rpc("validateaddress", [address])
    print(json.dumps(decoded, indent=2))
    return decoded

# 測試腳本
if __name__ == "__main__":
    addr = create_p2pkh_address()
    decode_address(addr)

2.2 腳本解析與驗證

使用 Bitcoin Core decodescript

# P2PKH 腳本解析
bitcoin-cli -rpcpassword=bitcoinpassword decodescript \
  "76a914860964193a8af5ed2d48969f4c2e5f2fa39ab9a488ac"

# 輸出:
# {
#     "asm": "OP_DUP OP_HASH160 860964193a8af5ed2d48969f4c2e5f2fa39ab9a4 OP_EQUALVERIFY OP_CHECKSIG",
#     "type": "pubkeyhash",
#     "p2sh": "2Mx...",
#     "segwit": {
#         "asm": "OP_DUP OP_HASH160 <860964193a8af5ed2d48969f4c2e5f2fa39ab9a4> OP_EQUALVERIFY OP_CHECKSIG",
#         "hex": "76a914860964193a8af5ed2d48969f4c2e5f2fa39ab9a488ac",
#         "type": "v0_p2wpkh"
#     }
# }

# P2SH-wrapped SegWit 腳本解析
bitcoin-cli -rpcpassword=bitcoinpassword decodescript \
  "a914a9b7c6d3e4f5a8b9c0d1e2f3a4b5c6d7e8f9a0b87"

# P2WSH 腳本解析
bitcoin-cli -rpcpassword=bitcoinpassword decodescript \
  "0020<sha256_hash>"

2.3 創建和測試自訂腳本

使用 Bitcoin Core createrawtransaction 創建自訂交易

# 1. 獲取錢包中的未花費輸出
bitcoin-cli -rpcpassword=bitcoinpassword listunspent

# 輸出示例:
# [
#     {
#         "txid": "abc123...",
#         "vout": 0,
#         "amount": 50.00000000,
#         "confirmations": 101,
#         "scriptPubKey": "76a914...",
#     }
# ]

# 2. 創建一個 OP_RETURN 輸出(不可花費)
bitcoin-cli -rpcpassword=bitcoinpassword createrawtransaction \
  '[{"txid":"abc123...","vout":0,"amount":50}]' \
  '[{"data":"48656c6c6f2057656c7420746f20426974636f696e"}]'

# data 欄位是 "Hello World" 的十六進制表示
# 這創建了一個包含 "Hello World" 訊息的 OP_RETURN 輸出

# 3. 解碼查看
bitcoin-cli -rpcpassword=bitcoinpassword decoderawtransaction "<raw_tx_hex>"

# 4. 簽署交易
bitcoin-cli -rpcpassword=bitcoinpassword signrawtransactionwithwallet \
  "<raw_tx_hex>"

第三章:常見腳本類型完整實現

3.1 P2PKH(Pay to Public Key Hash)腳本

P2PKH 是比特幣最傳統的地址格式,地址以「1」開頭:

腳本結構

鎖定腳本 (ScriptPubKey):
OP_DUP OP_HASH160 <pubKeyHash> OP_EQUALVERIFY OP_CHECKSIG

解鎖腳本 (ScriptSig):
<signature> <pubKey>

完整實現範例(Python)

#!/usr/bin/env python3
# p2pkh_example.py

import hashlib
import ecdsa
import base58
import wallycore as wally

class P2PKHWallet:
    """P2PKH 錢包實現"""
    
    def __init__(self, private_key_hex=None):
        if private_key_hex:
            self.private_key = bytes.fromhex(private_key_hex)
        else:
            self.private_key = self._generate_private_key()
        
        self.public_key = self._derive_public_key()
        self.pubkey_hash = self._hash_public_key()
        self.address = self._create_address()
    
    def _generate_private_key(self):
        """生成隨機私鑰"""
        import os
        return os.urandom(32)
    
    def _derive_public_key(self):
        """從私鑰派生公鑰(壓縮格式)"""
        # 使用 secp256k1 曲線
        signing_key = ecdsa.SigningKey.from_string(
            self.private_key, 
            curve=ecdsa.SECP256k1
        )
        verifying_key = signing_key.get_verifying_key()
        
        # 壓縮公鑰格式
        x = verifying_key.pubkey.point.x
        y = verifying_key.pubkey.point.y
        
        if y % 2 == 0:
            prefix = b'\x02'
        else:
            prefix = b'\x03'
        
        return prefix + x.to_bytes(32, 'big')
    
    def _hash_public_key(self):
        """計算公鑰哈希(RIPEMD160(SHA256(pubkey)))"""
        sha256_hash = hashlib.sha256(self.public_key).digest()
        ripemd160_hash = hashlib.new('ripemd160')
        ripemd160_hash.update(sha256_hash)
        return ripemd160_hash.digest()
    
    def _create_address(self):
        """創建 P2PKH 地址"""
        # 主網版本位元組:0x00
        version = b'\x00'
        payload = version + self.pubkey_hash
        
        # 計算校驗和:SHA256(SHA256(payload))[:4]
        checksum = hashlib.sha256(
            hashlib.sha256(payload).digest()
        ).digest()[:4]
        
        # Base58Check 編碼
        full_payload = payload + checksum
        return base58.b58encode(full_payload).decode('ascii')
    
    def get_locking_script(self):
        """獲取鎖定腳本"""
        script = bytes([
            0x76, 0xa9, 0x14  # OP_DUP OP_HASH160 <20 bytes>
        ]) + self.pubkey_hash + bytes([
            0x88, 0xac  # OP_EQUALVERIFY OP_CHECKSIG
        ])
        return script.hex()
    
    def get_address(self):
        """獲取 P2PKH 地址"""
        return self.address

# 使用示例
if __name__ == "__main__":
    wallet = P2PKHWallet()
    print(f"私鑰: {wallet.private_key.hex()}")
    print(f"公鑰: {wallet.public_key.hex()}")
    print(f"P2PKH 地址: {wallet.address}")
    print(f"鎖定腳本: {wallet.get_locking_script()}")

3.2 P2SH(Pay to Script Hash)腳本

P2SH 地址以「3」開頭,用於多籤、SegWit 包裝等場景:

腳本結構

鎖定腳本 (ScriptPubKey):
OP_HASH160 <scriptHash> OP_EQUAL

解鎖腳本 (ScriptSig):
<serialized_redeemScript> <...witness data...>

2-of-3 多籤實現

#!/usr/bin/env python3
# p2sh_multisig.py

import hashlib
import ecdsa
import base58

class P2SHMultisig:
    """P2SH 多籤錢包實現"""
    
    def __init__(self, public_keys, threshold):
        """
        初始化多籤錢包
        :param public_keys: 公鑰列表
        :param threshold: 生效閾值 (m-of-n)
        """
        if threshold > len(public_keys) or threshold < 1:
            raise ValueError("Invalid threshold")
        
        self.public_keys = public_keys
        self.threshold = threshold
        self.redeem_script = self._create_redeem_script()
        self.script_hash = self._hash_redeem_script()
        self.address = self._create_address()
    
    def _create_redeem_script(self):
        """創建贖回腳本"""
        script = bytes([self.threshold])  # m
        
        for pubkey in self.public_keys:
            # 公鑰需要是壓縮格式 (33 bytes)
            script += bytes([0x21]) + pubkey
        
        script += bytes([len(self.public_keys)])  # n
        script += bytes([0xae])  # OP_CHECKMULTISIG
        
        return script
    
    def _hash_redeem_script(self):
        """計算腳本哈希"""
        return hashlib.sha256(self.redeem_script).digest()
    
    def _create_address(self):
        """創建 P2SH 地址"""
        # 主網版本位元組:0x05
        version = b'\x05'
        payload = version + self.script_hash
        
        checksum = hashlib.sha256(
            hashlib.sha256(payload).digest()
        ).digest()[:4]
        
        full_payload = payload + checksum
        return base58.b58encode(full_payload).decode('ascii')
    
    def get_locking_script(self):
        """獲取鎖定腳本"""
        return (
            bytes([0xa9, 0x14]) +  # OP_HASH160 <20 bytes>
            self.script_hash +
            bytes([0x87])  # OP_EQUAL
        ).hex()
    
    def create_unlocking_script(self, signatures):
        """
        創建解鎖腳本
        :param signatures: 簽名列表
        """
        if len(signatures) < self.threshold:
            raise ValueError("Not enough signatures")
        
        # ScriptSig 格式:OP_0 <sig1> <sig2> ... <redeemScript>
        script = bytes([0x00])  # OP_0(用於補償 CHECKMULTISIG 的 bug)
        
        for sig in signatures[:self.threshold]:
            script += sig
        
        script += bytes([len(self.redeem_script)]) + self.redeem_script
        
        return script.hex()

# 使用示例
if __name__ == "__main__":
    # 假設有 3 個公鑰
    pubkey1 = bytes.fromhex("0212..." * 33)  # 33 bytes 壓縮公鑰
    pubkey2 = bytes.fromhex("0234..." * 33)
    pubkey3 = bytes.fromhex("0356..." * 33)
    
    # 創建 2-of-3 多籤錢包
    multisig = P2SHMultisig([pubkey1, pubkey2, pubkey3], 2)
    
    print(f"P2SH 地址: {multisig.address}")
    print(f"鎖定腳本: {multisig.get_locking_script()}")
    print(f"贖回腳本: {multisig.redeem_script.hex()}")

3.3 P2WSH(Pay to Witness Script Hash)腳本

P2WSH 是 SegWit v0 的腳本哈希形式,地址以「bc1q」開頭:

腳本結構

鎖定腳本 (ScriptPubKey):
OP_0 <sha256(witnessScript)>

見證 (Witness):
<witness elements> <witnessScript>

P2WSH + 2-of-3 多籤實現

#!/usr/bin/env python3
# p2wsh_multisig.py

import hashlib
import base58

def create_p2wsh_multisig(public_keys, threshold):
    """創建 P2WSH 多籤地址"""
    
    # 1. 構建 witnessScript (多籤腳本)
    witness_script = bytes([threshold])
    
    for pubkey in public_keys:
        witness_script += bytes([0x21]) + pubkey  # 0x21 = 33 bytes
    
    witness_script += bytes([len(public_keys), 0xae])  # OP_CHECKMULTISIG
    
    # 2. 計算 SHA256 哈希
    script_hash = hashlib.sha256(witness_script).digest()
    
    # 3. 創建 P2WSH 地址
    # bech32 編碼
    def bech32_encode(hrp, witver, witprog):
        """Bech32 編碼"""
        # 轉換位元組到 5-bit 基
        data = convertbits(witprog, 8, 5)
        data = [witver] + data
        
        # 計算校驗和
        polymod = bech32_polymod(
            [bech32_hrp_expand(hrp)] + data + [0, 0, 0, 0, 0, 0]
        )
        
        # 編碼
        combined = data + list(polymod)[::-1]
        return hrp + '1' + ''.join([BECH32_CHARSET[d] for d in combined])
    
    address = bech32_encode('bc', 0, script_hash)
    
    return {
        'address': address,
        'witness_script': witness_script.hex(),
        'script_hash': script_hash.hex()
    }

# 使用 bech32 庫(推薦使用)
try:
    from bech32 import encode as bech32_encode
    
    def create_p2wsh_multisig_v2(public_keys, threshold):
        witness_script = bytes([threshold])
        for pubkey in public_keys:
            witness_script += bytes([0x21]) + pubkey
        witness_script += bytes([len(public_keys), 0xae])
        
        script_hash = hashlib.sha256(witness_script).digest()
        address = bech32_encode('bc', 0, script_hash)
        
        return {
            'address': address,
            'witness_script': witness_script.hex(),
            'script_hash': script_hash.hex()
        }
except ImportError:
    print("pip install bech32")

# Bitcoin Core RPC 方式創建 P2WSH 多籤
def create_p2wsh_via_bitcoin_core(public_keys, threshold):
    """使用 Bitcoin Core RPC 創建 P2WSH 多籤"""
    import subprocess
    import json
    
    # 1. 創建多籤描述
    multisig_desc = f"{threshold},[{','.join(public_keys)}],multisig"
    
    # 2. 解析描述獲取地址
    result = subprocess.run([
        "bitcoin-cli",
        "-rpcpassword=bitcoinpassword",
        "getdescriptorinfo",
        multisig_desc
    ], capture_output=True, text=True)
    
    info = json.loads(result.stdout)
    
    # 3. 派生 P2WSH 地址
    result = subprocess.run([
        "bitcoin-cli",
        "-rpcpassword=bitcoinpassword",
        "deriveaddresses",
        f"wsh({multisig_desc})",
        "[]"
    ], capture_output=True, text=True)
    
    addresses = json.loads(result.stdout)
    
    return addresses[0]

# 使用示例
if __name__ == "__main__":
    # 假設的公鑰列表
    pubkeys = [
        "0212a876..." * 33,
        "0234b567..." * 33,
        "0356c890..." * 33
    ]
    
    result = create_p2wsh_via_bitcoin_core(pubkeys, 2)
    print(f"P2WSH 多籤地址: {result}")

3.4 P2TR(Pay to Taproot)腳本

P2TR 是 Taproot 升級引入的最新地址格式,地址以「bc1p」開頭:

腳本結構

鎖定腳本 (ScriptPubKey):
OP_1 <x-only-pubkey>

見證 (Witness - 密鑰路徑):
<signature>

見證 (Witness - 腳本路徑):
<leaf_version> <script> <control_block>

P2TR 實現

#!/usr/bin/env python3
# p2tr_example.py

import hashlib
import secrets

def x_only_point(point):
    """將完整公鑰轉換為 x-only 公鑰"""
    # Taproot 公鑰只使用 x 座標
    return point[1:33]  # 跳過 0x02 或 0x03 前綴

def taproot_tweak_pubkey(internal_pubkey, merkle_root):
    """創建 Taproot 最終公鑰"""
    # 將 internal_pubkey 和 merkle_root 組合
    t = bytes([0x01, 0x50])  # TapTweak tag
    h = hashlib.sha256(
        t + hashlib.sha256(t).digest() + internal_pubkey + merkle_root
    ).digest()
    
    # 使用 SHA256 進行調整
    return hashlib.sha256(internal_pubkey + h).digest()

def create_p2tr_address(internal_pubkey, merkle_root=None):
    """創建 P2TR 地址"""
    if merkle_root is None:
        merkle_root = bytes(32)  # 預設無腳本
    
    # 計算調整後的公鑰
    tweaked_pubkey = x_only_point(taproot_tweak_pubkey(internal_pubkey, merkle_root))
    
    # Bech32m 編碼
    from bech32 import encode as bech32_encode_v2
    address = bech32_encode_v2('bc', 1, tweaked_pubkey)  # version 1 for Taproot
    
    return address

# Bitcoin Core RPC 方式(推薦)
def create_p2tr_via_bitcoin_core():
    """使用 Bitcoin Core 創建 Taproot 地址"""
    import subprocess
    import json
    
    # 創建 Taproot 描述符錢包
    result = subprocess.run([
        "bitcoin-cli",
        "-rpcpassword=bitcoinpassword",
        "createwallet",
        "taproot_wallet",
        "false",  # 不免除廢棄監控
        "",       # 描述
        "false",  # 避免忘記選項
        "false",  # 不disable private keys
        "true",   # disposable
    ], capture_output=True, text=True)
    
    # 生成 Taproot 地址
    result = subprocess.run([
        "bitcoin-cli",
        "-rpcpassword=bitcoinpassword",
        "-rpcwallet=taproot_wallet",
        "getnewaddress",
        "taproot_addr",
        "bech32m"  # 指定 Taproot 地址格式
    ], capture_output=True, text=True)
    
    return result.stdout.strip()

if __name__ == "__main__":
    address = create_p2tr_via_bitcoin_core()
    print(f"P2TR (Taproot) 地址: {address}")

第四章:時間鎖定與 HTLC 實戰

4.1 nLockTime 絕對時間鎖定

創建時間鎖定交易

# 1. 創建當前不可花費的交易(nLockTime 設為未來)
bitcoin-cli -rpcpassword=bitcoinpassword createrawtransaction \
  '[{"txid":"abc123...","vout":0,"amount":10}]' \
  '[{"bcrt1q...": 9.99}]' \
  '{"locktime": 860000}'

# 2. 解碼查看 nLockTime
bitcoin-cli -rpcpassword=bitcoinpassword decoderawtransaction "<raw_tx_hex>"
# 輸出中可以看到 "locktime": 860000

# 3. 簽署交易(此時會失敗,因為 nLockTime 未滿足)
bitcoin-cli -rpcpassword=bitcoinpassword signrawtransactionwithwallet \
  "<raw_tx_hex>"
# 輸出會顯示 "complete": false

# 4. 等到區塊高度達到 860000 後,可以簽署並廣播
bitcoin-cli -rpcpassword=bitcoinpassword signrawtransactionwithwallet \
  "<raw_tx_hex>"
# 此時應該成功

4.2 OP_CLTV 時間鎖定腳本

使用 OP_CHECKLOCKTIMEVERIFY

# 1. 創建一個 100 區塊後才能花費的輸出
# 這需要使用 CLTV 腳本

# 首先需要知道贖回腳本
# 贖回腳本格式:
# <expiry_time> OP_CHECKLOCKTIMEVERIFY OP_DROP <pubkey> OP_CHECKSIG

# 2. 創建 P2SH 地址(使用上述贖回腳本)
redeem_script="bfa69aac..."  # 包含 CLTV 的贖回腳本
bitcoin-cli -rpcpassword=bitcoinpassword validateaddress \
  "$(bitcoin-cli -rpcpassword=bitcoinpassword decodescript $redeem_script | jq -r '.p2sh')"

# 3. 發送比特幣到此 P2SH 地址
bitcoin-cli -rpcpassword=bitcoinpassword sendtoaddress \
  "<p2sh_address>" 1.0

# 4. 創建花費交易(需要滿足時間條件)
# 這個交易需要設置 nLockTime 為贖回腳本中的時間值

4.3 HTLC(Hash Time Lock Contract)完整實現

HTLC 是閃電網路和原子交換的核心:

HTLC 腳本結構

IF
    # 正常路徑:提供原像(preimage)
    OP_HASH256 <hash-of-preimage> OP_EQUALVERIFY <receiver-pubkey> OP_CHECKSIG
ELSE
    # 超時路徑:等待時間後退款
    <timeout> OP_CHECKSEQUENCEVERIFY OP_DROP <sender-pubkey> OP_CHECKSIG
ENDIF

Python 實現

#!/usr/bin/env python3
# htlc_example.py

import hashlib
import secrets

class HTLC:
    """HTLC 合約實現"""
    
    def __init__(self, sender_pubkey, receiver_pubkey, amount, timeout_blocks, hashlock):
        """
        初始化 HTLC
        :param sender_pubkey: 發送方公鑰(33 bytes)
        :param receiver_pubkey: 接收方公鑰(33 bytes)
        :param amount: 金額(satoshis)
        :param timeout_blocks: 區塊超時時間
        :param hashlock: 32 bytes 的原像哈希 (hash of preimage)
        """
        self.sender_pubkey = sender_pubkey
        self.receiver_pubkey = receiver_pubkey
        self.timeout_blocks = timeout_blocks
        self.hashlock = hashlock
        
        self.redeem_script = self._create_redeem_script()
        self.script_hash = self._compute_script_hash()
        self.p2wsh_address = self._create_address()
    
    def _create_redeem_script(self):
        """創建 HTLC 贖回腳本"""
        script = bytes()
        
        # IF branch: hashlock path
        script += bytes([0x63])  # OP_IF
        script += bytes([0xaa])  # OP_HASH256
        script += bytes([0x20])  # OP_DATA_32
        script += self.hashlock  # 32 bytes hash
        script += bytes([0x76, 0xa9, 0x14])  # OP_HASH256 <32 bytes> OP_EQUALVERIFY
        script += self.receiver_pubkey
        script += bytes([0xac])  # OP_CHECKSIG
        
        # ELSE branch: timelock path
        script += bytes([0x67])  # OP_ELSE
        # OP_CHECKSEQUENCEVERIFY
        script += bytes([0xb2])  # OP_CHECKSEQUENCEVERIFY
        script += bytes([0x75])  # OP_DROP
        script += bytes([0x76, 0xa9, 0x14])  # OP_DUP OP_HASH160 <pubkey>
        script += self.sender_pubkey
        script += bytes([0x88, 0xac])  # OP_EQUALVERIFY OP_CHECKSIG
        
        script += bytes([0x68])  # OP_ENDIF
        
        return script
    
    def _compute_script_hash(self):
        """計算 SHA256 腳本哈希"""
        return hashlib.sha256(self.redeem_script).digest()
    
    def _create_address(self):
        """創建 P2WSH 地址"""
        from bech32 import encode
        return encode('bc', 0, self.script_hash)
    
    def get_hashlock_secret(self):
        """生成原像(secret),用於解鎖"""
        return secrets.token_bytes(32)
    
    def create_hashlock_witness(self, secret, receiver_signature):
        """創建 Hashlock 路徑的見證數據"""
        return {
            'signature': receiver_signature.hex(),
            'secret': secret.hex(),
            'script': self.redeem_script.hex()
        }
    
    def create_timelock_witness(self, sender_signature):
        """創建 Timelock 路徑的見證數據"""
        # 需要包含 sequence 來表示相對時間鎖
        return {
            'signature': sender_signature.hex(),
            'sequence': self.timeout_blocks.to_bytes(4, 'little').hex(),
            'script': self.redeem_script.hex()
        }

# 使用示例
if __name__ == "__main__":
    # 假設的公鑰
    sender_pubkey = bytes.fromhex("0212a876..." * 33)
    receiver_pubkey = bytes.fromhex("0234b567..." * 33)
    
    # 創建 HTLC
    # hashlock = SHA256("secret_password")
    secret = b"my_secret_password_12345"
    hashlock = hashlib.sha256(secret).digest()
    
    htlc = HTLC(
        sender_pubkey=sender_pubkey,
        receiver_pubkey=receiver_pubkey,
        amount=100000,  # 0.001 BTC
        timeout_blocks=144,  # 約 24 小時
        hashlock=hashlock
    )
    
    print(f"HTLC P2WSH 地址: {htlc.p2wsh_address}")
    print(f"贖回腳本: {htlc.redeem_script.hex()}")
    print(f"原像哈希: {hashlock.hex()}")
    print(f"原像: {secret.hex()}")

4.4 Bitcoin Core 實現 HTLC

# 1. 使用 Bitcoin Core 創建 HTLC 測試

# 首先創建兩個錢包
bitcoin-cli -rpcpassword=bitcoinpassword createwallet "alice"
bitcoin-cli -rpcpassword=bitcoinpassword createwallet "bob"

# 2. 生成地址和資助
ALICE_ADDR=$(bitcoin-cli -rpcpassword=bitcoinpassword -rpcwallet=alice getnewaddress)
BOB_ADDR=$(bitcoin-cli -rpcpassword=bitcoinpassword -rpcwallet=bob getnewaddress)

# 資助錢包
MINER_ADDR=$(bitcoin-cli -rpcpassword=bitcoinpassword getnewaddress)
bitcoin-cli -rpcpassword=bitcoinpassword generatetoaddress 200 $MINER_ADDR

# 3. 創建 HTLC 的原始交易
# 這需要手動構造,因為 Bitcoin Core RPC 不直接支持 HTLC

# 首先,定義 HTLC 贖回腳本
# HTLC 腳本(十六進制)
HTLC_SCRIPT="6376a914<hash_of_alice_pubkey>88ac670068"
# 實際使用時需要完整的 HTLC 腳本

# 4. 使用 sendrawtransaction 廣播 HTLC 交易
# 見證格式:[signature] [secret] HTLC_script

第五章:Miniscript 實戰

5.1 Miniscript 簡介

Miniscript 是比特幣腳本的策略性子集,由 Pieter Wuille 等人提出,簡化了複雜比特幣腳本的創建和安全性分析。

常見 Miniscript 表達式

MiniscriptBitcoin Script說明
pk(A)A OP_CHECKSIG公鑰檢查
pkh(A)OPDUP OPHASH160 OP_EQUALVERIFYP2PKH
multi(k, A, B, ...)k <keys> k OP_CHECKMULTISIG多籤
and_v(vc:pkh(A),tn:older(144))<sig A> OPVERIFY 144 OPCHECKSEQUENCEVERIFY帶時間的多籤
or_c(pk(A),s:pk(B))OPIF OPCHECKSIGVERIFY ELSE OPSWAP OPCHECKSIG ENDIF雙選擇
thresh(2,w:pk(A),w:pk(B),w:pk(C))2 <A> <B> <C> 3 OP_CHECKMULTISIG2-of-3

5.2 Miniscript 編譯器使用

安裝 miniscript.fun

# 從源碼編譯
git clone https://github.com/sipa/miniscript.git
cd miniscript
./autogen.sh
./configure
make

# 或使用 Docker
docker pull sipa/miniscript
docker run -it sipa/miniscript

Miniscript 範例

# 1. 創建一個 2-of-3 多籤 Miniscript
# 表達式:multi(2, A, B, C)

# 2. 分析 Miniscript
./miniscript/bin/miniscript "multi(2, [alekfire's pubkey], [bob's pubkey], [charlie's pubkey])"

# 輸出:
# {
#     "script": "<2> <pubkey1> <pubkey2> <pubkey3> 3 OP_CHECKMULTISIG",
#     "type": "multi",
#     "address": "3...",
#     "satisfiability": "We can produce witnesses for all but 0 signatures."
# }

# 3. 生成描述符
./miniscript/bin/miniscript -d "wsh(multi(2, [A], [B], [C]))"

# 輸出:
# wsh(multi(2,A,B,C))#<checksum>

5.3 Miniscript 與 Bitcoin Core

Bitcoin Core 支持 Miniscript 描述符:

# 1. 使用 Miniscript 創建錢包
bitcoin-cli -rpcpassword=bitcoinpassword createwallet "miniscript_wallet"

# 2. 導入 Miniscript 描述符
bitcoin-cli -rpcpassword=bitcoinpassword -rpcwallet=miniscript_wallet \
  importdescriptors \
  '[{
      "desc": "wsh(multi(2,tpubD9DA.../0/*,tpubD9DA.../0/*,tpubD9DA.../0/*))#testChecksum",
      "timestamp": "now",
      "range": [0, 100],
      "watchonly": false,
      "label": "2-of-3 multisig"
  }]'

# 3. 生成地址
bitcoin-cli -rpcpassword=bitcoinpassword -rpcwallet=miniscript_wallet \
  getnewaddress

# 4. 列出所有地址
bitcoin-cli -rpcpassword=bitcoinpassword -rpcwallet=miniscript_wallet \
  listdescriptors

第六章:比特幣節點架設完整命令序列

6.1 主網全節點架設

完整步驟

#!/bin/bash
# setup_bitcoin_node.sh

# 1. 更新系統
sudo apt-get update && sudo apt-get upgrade -y

# 2. 安裝依賴
sudo apt-get install -y \
  build-essential \
  libtool \
  autotools-dev \
  automake \
  pkg-config \
  libssl-dev \
  libevent-dev \
  libboost-system-dev \
  libboost-filesystem-dev \
  libboost-chrono-dev \
  libboost-test-dev \
  libboost-thread-dev \
  libsqlite3-dev \
  bsdmainutils \
  libdb-dev \
  libdb++-dev

# 3. 下載 Bitcoin Core
cd /tmp
wget https://bitcoincore.org/bin/bitcoin-core-27.0/bitcoin-27.0-x86_64-linux-gnu.tar.gz
wget https://bitcoincore.org/bin/bitcoin-core-27.0/SHA256SUMS
wget https://bitcoincore.org/bin/bitcoin-core-27.0/SHA256SUMS.asc

# 4. 驗證簽名(需要先取得並驗證開發者 GPG 鑰匙)
sha256sum --check SHA256SUMS

# 5. 安裝
sudo tar -xzf bitcoin-27.0-x86_64-linux-gnu.tar.gz -C /usr/local/bin --strip-components=2 bitcoin-27.0/bin/bitcoin{d,-cli,-tx,util} 2>/dev/null
sudo tar -xzf bitcoin-27.0-x86_64-linux-gnu.tar.gz -C /usr/local/ bitcoin-27.0/include bitcoin-27.0/lib
sudo strip /usr/local/bin/bitcoin*

# 6. 建立用戶
sudo useradd -m -s /bin/bash bitcoin
sudo mkdir -p /home/bitcoin/.bitcoin
sudo chown -R bitcoin:bitcoin /home/bitcoin/.bitcoin

# 7. 創建配置文件
sudo -u bitcoin tee /home/bitcoin/.bitcoin/bitcoin.conf << 'EOF'
# 網路配置
server=1
daemon=1
maxconnections=8

# RPC 配置
rpcuser=bitcoinuser
rpcpassword=CHANGE_THIS_PASSWORD
rpcport=8332
rpcbind=127.0.0.1

# 區塊存儲配置
prune=0
dbcache=4096

# 安全性
maxuploadtarget=5000
blocksonly=0

# 附加配置
logips=1
EOF

sudo chmod 600 /home/bitcoin/.bitcoin/bitcoin.conf
sudo chown bitcoin:bitcoin /home/bitcoin/.bitcoin/bitcoin.conf

# 8. 創建 systemd 服務
sudo tee /etc/systemd/system/bitcoind.service << 'EOF'
[Unit]
Description=Bitcoin Daemon
After=network.target

[Service]
ExecStart=/usr/local/bin/bitcoind -daemonwait -pid=/run/bitcoind/bitcoind.pid
PIDFile=/run/bitcoind/bitcoind.pid
User=bitcoin
Restart=on-failure
PrivateTmp=true
ProtectSystem=full
NoNewPrivileges=true
ReadWriteDirectories=/home/bitcoin/.bitcoin

[Install]
WantedBy=multi-user.target
EOF

# 9. 啟動服務
sudo mkdir -p /run/bitcoind
sudo chown bitcoin:bitcoin /run/bitcoind
sudo systemctl daemon-reload
sudo systemctl enable bitcoind
sudo systemctl start bitcoind

# 10. 監控啟動
sleep 5
sudo systemctl status bitcoind

# 11. 檢查同步狀態
sudo -u bitcoin bitcoin-cli -rpcpassword=CHANGE_THIS_PASSWORD getblockchaininfo

# 12. 查看節點連接
sudo -u bitcoin bitcoin-cli -rpcpassword=CHANGE_THIS_PASSWORD getnetworkinfo

6.2 閃電網路節點架設(Lncli 完整命令)

LND 安裝和配置

#!/bin/bash
# setup_lnd.sh

# 1. 安裝 Go(如果需要)
wget https://go.dev/dl/go1.22.linux-amd64.tar.gz
sudo tar -C /usr/local -xzf go1.22.linux-amd64.tar.gz
export PATH=$PATH:/usr/local/go/bin

# 2. 安裝 LND
git clone https://github.com/lightningnetwork/lnd.git
cd lnd
make install

# 3. 創建 LND 配置
mkdir -p ~/.lnd
cat > ~/.lnd/lnd.conf << 'EOF'
[Application Options]
debuglevel=info
maxpendingchannels=5
alias=MyLightningNode
color=#68E5FF

[Bitcoin]
bitcoin.active=1
bitcoin.mainnet=1
bitcoin.node=bitcoind

[bitcoind]
bitcoind.rpchost=127.0.0.1
bitcoind.rpcuser=bitcoinuser
bitcoind.rpcpass=CHANGE_THIS_PASSWORD
bitcoind.zmqpubrawblock=tcp://127.0.0.1:18501
bitcoind.zmqpubrawtx=tcp://127.0.0.1:18502

[autopilot]
autopilot.active=1
autopilot.maxchannels=10
autopilot.allocation=0.6
EOF

# 4. 創建錢包
lncli create

# 5. 獲取節點資訊
lncli getinfo

# 6. 生成比特幣接收地址
lncli newaddress p2wkh

# 7. 查看通道列表
lncli listchannels

# 8. 打開通道
lncli openchannel --node_key=<peer_pubkey> --local_amt=500000 --push_amt=250000

# 9. 發送支付
lncli sendpayment --dest=<destination_pubkey> --amt=<amount_satoshis>

# 10. 創建和接收發票
lncli addinvoice --amt=<amount_satoshis>
lncli lookupinvoice <payment_request>

6.3 Regtest 測試環境快速架設

#!/bin/bash
# quick_regtest_setup.sh

# 1. 啟動 Regtest 節點
bitcoind -regtest -daemon \
  -rpcuser=regtest \
  -rpcpassword=regtest123 \
  -rpcport=18443

# 2. 等待節點啟動
sleep 3

# 3. 創建錢包
bitcoin-cli -regtest -rpcuser=regtest -rpcpassword=regtest123 \
  createwallet "testwallet" false false "" false false

# 4. 生成測試地址
ALICE=$(bitcoin-cli -regtest -rpcuser=regtest -rpcpassword=regtest123 \
  -rpcwallet=testwallet getnewaddress "alice")
BOB=$(bitcoin-cli -regtest -rpcuser=regtest -rpcpassword=regtest123 \
  -rpcwallet=testwallet getnewaddress "bob")
MINER=$(bitcoin-cli -regtest -rpcuser=regtest -rpcpassword=regtest123 \
  getnewaddress "miner")

# 5. 挖出初始區塊(100+)
bitcoin-cli -regtest -rpcuser=regtest -rpcpassword=regtest123 \
  generatetoaddress 110 $MINER

# 6. 檢查 Alice 的餘額
bitcoin-cli -regtest -rpcuser=regtest -rpcpassword=regtest123 \
  -rpcwallet=testwallet getbalance

# 7. 發送比特幣給 Alice
TXID=$(bitcoin-cli -regtest -rpcuser=regtest -rpcpassword=regtest123 \
  sendtoaddress $ALICE 10)
echo "Transaction ID: $TXID"

# 8. 挖出一個區塊確認交易
bitcoin-cli -regtest -rpcuser=regtest -rpcpassword=regtest123 \
  generatetoaddress 1 $MINER

# 9. 檢查交易狀態
bitcoin-cli -regtest -rpcuser=regtest -rpcpassword=regtest123 \
  -rpcwallet=testwallet gettransaction $TXID

# 10. 查看 UTXO
bitcoin-cli -regtest -rpcuser=regtest -rpcpassword=regtest123 \
  -rpcwallet=testwallet listunspent

結論

本指南提供了比特幣腳本編程的完整實戰教程,涵蓋從基礎環境搭建到進階應用的各個層面。所有程式碼範例均可在 Regtest 或測試網環境中實際運作,讀者可以通過動手實踐深入理解比特幣腳本的工作原理。

比特幣腳本語言的設計雖然簡單,但功能強大。通過組合基本操作碼,可以實現從簡單支付到複雜合約的各種應用場景。掌握比特幣腳本不僅能幫助開發者創建更安全的比特幣應用,也能讓普通用戶更好地理解比特幣網路的安全機制。


參考文獻

  1. Bitcoin Developer Reference. "Script." https://developer.bitcoin.org/reference/script.html
  1. Bitcoin Optech. "Bitcoin Scripting." https://bitcoinops.org/en/topics/scanning/
  1. Bitcoin Wiki. "Script." https://en.bitcoin.it/wiki/Script
  1. BIP-341: Taproot. https://github.com/bitcoin/bips/blob/master/bip-0341.mediawiki
  1. BIP-342: Taproot Script Validation. https://github.com/bitcoin/bips/blob/master/bip-0342.mediawiki
  1. Miniscript Website. https://bitcoin.sipa.be/miniscript/
  1. Mastering Bitcoin, 2nd Edition. Andreas M. Antonopoulos. O'Reilly Media, 2017.

延伸閱讀與來源

這篇文章對您有幫助嗎?

評論

發表評論

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

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