Miniscript 應用完全指南

理解比特幣腳本的高級表示法 Miniscript,包括語法、類型系統與實際應用場景。

Miniscript 應用完全指南

概述

Miniscript 是一種用於比特幣腳本的高級表示法,由 Bitcoin Core 貢獻者 Pieter Wuille 提出。Miniscript 使得複雜的比特幣腳本可以被人類可讀的方式編寫、驗證和分析,同時保留了比特幣腳本的全部功能。

為什麼需要 Miniscript?

傳統比特幣腳本的問題

傳統的比特幣腳本(Bitcoin Script)使用底層操作碼(OP_CODE)編寫,存在以下問題:

  1. 難以閱讀:原始腳本字串對人類不友好
  2. 難以驗證:難以確認腳本是否安全或正確
  3. 無法靜態分析:無法在簽名前分析腳本行為
  4. 錢包相容性差:不同錢包對複雜腳本的支持程度不同

Miniscript 的優勢

Miniscript 解決了這些問題:

Miniscript 語法基礎

基礎片段(Fragments)

Miniscript 使用「片段」來構建腳本。每個片段都有明確的類型和參數:

片段語法說明
PKpk(key)公鑰簽名驗證
PkHpk_h(key)公鑰雜湊驗證
Multimulti(k, key1, key2, ...)多簽名閾值
Threshthresh(k, expr1, expr2, ...)K-of-N 閾值
Afterafter(n)時間鎖定
Olderolder(n)區塊高度鎖定
Sha256sha256(h)SHA256 雜湊預映證
Hash256hash256(h)Double SHA256
Ripemd160ripemd160(h)RIPEMD160 雜湊預映證
Hash160hash160(h)RIPEMD160+SHA256

示例語法

// 2-of-3 多簽名
multi(2, key1, key2, key3)

// 2-of-3 多簽名加上時間鎖
and_v(v:multi(2, key1, key2, key3), after(1000))

// 任意一個公鑰或時間鎖
or_d(v:pk(key1), v:after(500))

Miniscript 類型系統

Miniscript 為每個片段分配類型,用於靜態分析和安全性驗證:

類型標記

類型標記說明
BBoolean返回布爾值
VValue需要值才能滿足
KKey需要簽名
WWrapped包裝的條件

類型組合

常見類型組合:

實際應用場景

場景一:時間鎖定遺產

// 立即可以使用任何一個私鑰
// 或者 1 年後可以使用任何一個私鑰
or(
    pk(key1),
    and(older(525600), pk(key1))
)

轉換為比特幣腳本後:

OP_IF
    <key1> OP_CHECKSIG
OP_ELSE
    525600 OP_CHECKSEQUENCEVERIFY OP_DROP
    <key1> OP_CHECKSIG
OP_ENDIF

場景二:閾值多簽名

// 3-of-5 多簽名
multi(3, key1, key2, key3, key4, key5)

場景三:需要額外條件的支出

// 需要 key1 簽名加上 1 天後冷卻期
// 或者 2-of-3 多簽名
or(
    and_v(v:pk(key1), older(144)),
    and_v(v:multi(2, key2, key3, key4), after(1000))
)

場景四:硬體錢包 + 手機錢包

// 手機錢包(日常使用)+ 硬體錢包(備份)
or(
    pk(hardware_key),
    and_v(v:pk(backup_key), older(1000))
)

Miniscript 編譯工具

miniscript.fun

線上工具可以將 Miniscript 轉換為比特幣腳本:

  1. 訪問 miniscript.fun
  2. 輸入 Miniscript 表達式
  3. 選擇目標地址類型(P2WSH 或 P2TR)
  4. 獲取編譯後的腳本和地址

Bitcoin Core

Bitcoin Core 24.0+ 內建 Miniscript 支援:

# 解析 Miniscript
getminiscript "pk(key)"

# 從 Miniscript 獲取腳本
toderawminiscript "pk(key)"

程式庫

語言
Rustrust-miniscript
C++Bitcoin Core 內建
Pythonpython-miniscript
JavaScriptminiscript-js

安全性分析

提前終止(Earliest Termination)

Miniscript 可以分析腳本是否會提前終止(提前返回成功/失敗):

滿足性(Satisfiability)

分析腳本是否可以被滿足(即是否存在有效的解鎖方式):

// 總是可以滿足的(總有分支可以達成)
or(pk(key1), pk(key2))

// 可能無法滿足的(兩個條件都無法達成)
and(pk(key1), after(999999999))

完美隱私

某些 Miniscript 結構可以提供「完美隱私」—— 外部觀察者無法從腳本結構推斷出內部邏輯:

// 使用 thresh 提供完美隱私
thresh(1, pk(key1), pk(key2), pk(key3))

這與 multi(1, ...) 行為相同,但結構更複雜。

Miniscript 與 Taproot

Taproot 地址

Miniscript 可以編譯為 P2TR(Taproot)地址:

# 編譯為 Taproot
toderawminiscript "pk(key)" - tapped

腳本樹

Taproot 允許腳本樹結構,Miniscript 可以表示:

//  Taproot 腳本樹
or(
    pk(key_main),
    and(older(1000), pk(key_backup))
)

這會創建一個展示為單一公鑰的 Taproot 地址,但包含備份支出路徑。

進階 Miniscript 程式設計

使用 Rust 庫實現

use rust_miniscript::miniscript::Miniscript;
use rust_miniscript::bitcoin::PublicKey;
use rust_miniscript::bitcoin::secp256k1::Secp256k1;

fn main() -> Result<(), Box<dyn std::error::Error>> {
    let secp = Secp256k1::new();

    // 生成三個測試公鑰
    let (_, pk1) = secp.generate_keypair(&mut rand::thread_rng());
    let (_, pk2) = secp.generate_keypair(&mut rand::thread_rng());
    let (_, pk3) = secp.generate_keypair(&mut rand::thread_rng());

    // 創建 2-of-3 多簽名 Miniscript
    let ms = Miniscript::<PublicKey,>::from_str(&format!(
        "multi(2,{},{},{})",
        pk1, pk2, pk3
    ))?;

    // 獲取腳本
    let script = ms.encode();
    println!("P2WSH 腳本: {}", script);

    // 獲取地址
    let address = ms.address(Network::Bitcoin);
    println!("比特幣地址: {}", address);

    Ok(())
}

使用 Python 實現

from miniscript import Miniscript, Key

def create_timelock_escrow(
    escrow_key: Key,
    buyer_key: Key,
    seller_key: Key,
    timelock_blocks: int
) -> Miniscript:
    """
    創建一個帶時間鎖的托管合約

    邏輯:
    - 買家和賣家共同簽名可以立即轉帳
    - 或者一段時間後只有賣家可以轉帳(退款)
    """

    # 2-of-2 多簽名
    multi_2of2 = Miniscript.multi(
        k=2,
        keys=[buyer_key, seller_key]
    )

    # 時間鎖路徑:賣家單獨可以在 timelock 後提款
    timelock_path = Miniscript.and_(
        Miniscript.older(timelock_blocks),
        Miniscript.pk(seller_key)
    )

    # 組合邏輯:多簽 OR 時間鎖
    escrow = Miniscript.or_(multi_2of2, timelock_path)

    return escrow

def create_inheritance_contract(
    heir_keys: list[Key],
    timelock_months: int
) -> Miniscript:
    """
    創建遺產合約

    邏輯:
    - 主要繼承人需要 2-of-3 簽名
    - 或者 1 年後任何一個備用繼承人可以獲得資金
    """

    # 2-of-3 主要路徑
    primary = Miniscript.multi(k=2, keys=heir_keys)

    # 備份路徑:1年後任何備用金鑰
    backup = Miniscript.and_(
        Miniscript.older(timelock_months * 4320),  # 假設每月約 4320 區塊
        Miniscript.multi(k=1, keys=heir_keys)
    )

    return Miniscript.or_(primary, backup)

完整錢包整合示例

// TypeScript 錢包整合示例

interface WalletConfig {
    mainKey: PublicKey;
    backupKeys: PublicKey[];
    timelockBlocks: number;
    network: 'mainnet' | 'testnet';
}

class MiniscriptWallet {
    private config: WalletConfig;

    constructor(config: WalletConfig) {
        this.config = config;
    }

    /**
     * 創建日常使用的錢包(主要密鑰 + 備份)
     *
     * 邏輯:
     * - 主密鑰可以隨時使用
     * - 備份密鑰需要 30 天冷卻期
     */
    createDailyWallet(): Miniscript {
        const mainPath = Miniscript.pk(this.config.mainKey);

        const backupPath = Miniscript.and_(
            Miniscript.older(this.config.timelockBlocks),
            Miniscript.pk(this.config.backupKeys[0])
        );

        // 任一路徑都可以提款
        return Miniscript.or_(mainPath, backupPath);
    }

    /**
     * 創建冷存儲錢包(高安全性)
     *
     * 邏輯:
     * - 3-of-5 多簽名
     * - 需要大多數持有人同意
     */
    createColdStorage(): Miniscript {
        return Miniscript.multi(
            k=3,
            keys=[
                this.config.mainKey,
                ...this.config.backupKeys
            ]
        );
    }

    /**
     * 創建商業帳戶(需要審批)
     *
     * 邏輯:
     * - 經理 A + 經理 B 同時簽名
     * - 或者 3-of-5 董事簽名
     * - 或者 1 年後資金自動釋出
     */
    createBusinessAccount(
        managerKeys: PublicKey[],
        directorKeys: PublicKey[]
    ): Miniscript {
        // 雙經理路徑
        const managerPath = Miniscript.multi(k=2, keys=managerKeys);

        // 董事路徑
        const directorPath = Miniscript.multi(k=3, keys=directorKeys);

        // 時間解鎖路徑(自動釋出)
        const timelockPath = Miniscript.and_(
            Miniscript.older(525600),  // 約 1 年
            Miniscript.pk(this.config.mainKey)
        );

        // 組合:經理 OR 董事 OR 時間鎖
        return Miniscript.or_(
            managerPath,
            Miniscript.or_(directorPath, timelockPath)
        );
    }

    /**
     * 生成比特幣地址
     */
    generateAddress(miniscript: Miniscript): string {
        const script = miniscript.encode();
        const hash = sha256(script);
        return bech32.encode('bc', hash, this.config.network);
    }
}

比特幣核心 RPC 整合

# 使用 Bitcoin Core 創建和兌現 Miniscript

# 1. 解析 Miniscript
bitcoin-cli getminiscript "multi(2,03a0434e9e2cfaea60e8a2a6a00d5d7a8d6b9e2cfaea60e8a2a6a00d5d7a8d6b,03b0434e9e2cfaea60e8a2a6a00d5d7a8d6b9e2cfaea60e8a2a6a00d5d7a8d6)"

# 2. 獲取腳本
bitcoin-cli decodepsbt "psbt_base64"

# 3. 創建 P2TR 地址
bitcoin-cli getdescriptorinfo "tr(/0'/0'/0')"

# 4. 創建 Miniscript 錢包
bitcoin-cli createwallet "miniscript_wallet" true true

# 5. 導入 Miniscript 描述符
importdescriptors '[
  {
    "desc": "wsh(multi(2,03a0434e9e2cfaea60e8a2a6a00d5d7a8d6b9e2cfaea60e8a2a6a00d5d7a8d6b,03b0434e9e2cfaea60e8a2a6a00d5d7a8d6b9e2cfaea60e8a2a6a00d5d7a8d6))",
    "timestamp": "now",
    "watchonly": true,
    "range": [0, 100],
    "next_index": 1
  }
]'

常見問題

Miniscript 與普通腳本有什麼區別?

Miniscript 是比特幣腳本的可讀表示,兩者在區塊鏈上的表現完全相同。Miniscript 只是讓編寫和驗證比特幣腳本變得更容易。

Miniscript 是否需要新的比特幣升級?

不需要。Miniscript 完全相容現有的比特幣共識層,可以直接在任何現代比特幣腳本中使用。

使用 Miniscript 是否有額外費用?

沒有。Miniscript 編譯後產生的腳本與手寫腳本的大小相同,不會產生額外的區塊空間費用。

Miniscript 是否支援智能合約?

是的。Miniscript 可以實現各種複雜的支出條件,包括時間鎖、多簽名、雜湊預映證等,這些是比特幣智能合約的基礎。

Miniscript 安全性分析深入

提前終止安全(Earliest-Termination)

Miniscript 的類型系統允許靜態分析腳本的提前終止屬性:

// 提前終止安全的腳本範例
and_b(pk(A), pk(B))
// 兩個條件都必須滿足,無法提前終止

// 可能提前終止的腳本
or_b(pk(A), pk(B))
// 如果 A 簽名成功,腳本提前終止

提前終止分析的重要性:

安全性類型詳解

Miniscript 使用四個基礎類型標記:

類型組合矩陣
════════════════════════════════════════════════════

基礎類型:
┌─────────────────────────────────────────────────┐
│ B (Boolean)   - 返回 TRUE/FALSE                │
│ V (Value)    - 需要提供數值才能滿足            │
│ K (Key)      - 需要提供密鑰/簽名               │
│ W (Wrapped)  - 包裝類型,自動解包              │
└─────────────────────────────────────────────────┘

組合類型:
┌─────────────────────────────────────────────────┐
│ Bk - Boolean + Key                             │
│ Kv - Key + Value                               │
│ Bv - Boolean + Value                          │
│ Kw - Key + Wrapped                            │
│ ...                                           │
└─────────────────────────────────────────────────┘

安全性驗證流程

def verify_miniscript_safety(miniscript: str) -> dict:
    """
    驗證 Miniscript 安全性
    """

    # 1. 解析 Miniscript
    ms = parse_miniscript(miniscript)

    # 2. 類型檢查
    type_check(ms)

    # 3. 提前終止分析
    is_earliest_termination = analyze_earliest_termination(ms)

    # 4. 滿足性檢查
    is_satisfiable = check_satisfiability(ms)

    # 5. 安全 性檢查
    is_safe = check_malleability(ms)

    return {
        "type": ms.type,
        "earliest_termination": is_earliest_termination,
        "satisfiable": is_satisfiable,
        "safe": is_safe
    }

常見安全陷阱

安全陷阱清單
═════════════════════════════════════════════════════

1. 不可滿足的腳本
   ┌───────────────────────────────────────────────┐
   │ and(pk(A), after(999999999))                │
   │                                             │
   │ 問題:時間永遠不會到來,永遠無法花費         │
   │ 解決:使用 or 確保總是可以滿足              │
   └───────────────────────────────────────────────┘

2. 隱藏的提前終止
   ┌───────────────────────────────────────────────┐
   │ or(pk(A), and(pk(B), after(100)))           │
   │                                             │
   │ 問題:A 簽名後腳本提前終止,B 的資金被鎖住  │
   │ 解決:仔細分析所有分支的行為                │
   └───────────────────────────────────────────────┘

3. 時間鎖陷阱
   ┌───────────────────────────────────────────────┐
   │ and(older(525600), pk(A))                    │
   │                                             │
   │ 問題:1年後才能提款,且需要 A 簽名          │
   │ 解決:添加備份路徑                          │
   └───────────────────────────────────────────────┘

4. 多簽名閾值錯誤
   ┌───────────────────────────────────────────────┐
   │ multi(3, A, B, C, D, E)                    │
   │                                             │
   │ 問題:需要 5 人中的 3 人,不是 3-of-5       │
   │ 解決:確保 k <= n                            │
   └───────────────────────────────────────────────┘

Miniscript 實際部署範例

企業比特幣托管方案

企業級 Miniscript 架構
═════════════════════════════════════════════════════

層級 1:日常運營帳戶
───────────────────────────────────────────────────
Miniscript: multi(2, CEO_key, CFO_key)

特點:
- 需要 CEO 和 CFO 共同簽名
- 適合日常支出
- 金額限制:$100,000/日

層級 2:儲備帳戶
───────────────────────────────────────────────────
Miniscript: or(
    multi(3, CEO_key, CFO_key, CTO_key),
    and(older(1000), multi(2, CEO_key, Chairman_key))
)

特點:
- 需要 3 位高管簽名
- 或 2 位高管 + 1 年等待期
- 適合大額支出

層級 3:離線冷存儲
───────────────────────────────────────────────────
Miniscript: multi(5,
    CEO_key,
    CFO_key,
    Chairman_key,
    External_Auditor_key,
    Time_Lock_Recovery_key
)

特點:
- 需要全部 5 把密鑰
- 完全離線存儲
- 適合長期儲備

去中心化儲蓄合約

// 自動儲蓄計劃合約

or(
    // 路徑 1:每月自動存款
    and(
        older(4320),  // 約 1 個月
        pk(savings_contract)
    ),

    // 路徑 2:緊急提款(需要 3-of-5 同意)
    multi(3,
        user_key,
        spouse_key,
        lawyer_key,
        accountant_key,
        bank_key
    )
)

// 邏輯:
// - 合約地址每月自動存入固定金額
// - 用戶在任何時候可以通過多簽提款
// - 實現自動化儲蓄目標

聯合創投基金

// 創投基金合約

or(
    // 路徑 1:投資委員會批準
    and(
        multi(3, GP_key1, GP_key2, GP_key3),
        after(5000)  // 鎖定期
    ),

    // 路徑 2:有限合夥人投票
    and(
        multi(4, LP_key1, LP_key2, LP_key3, LP_key4),
        older(10000)  // 更長鎖定期
    ),

    // 路徑 3:到期還本
    and(
        older(20000),  // 約 4 年
        pk(fund_address)
    )
)

// 邏輯:
// - GP 批準 + 鎖定期後可以投資
// - LP 多數同意可以還款
// - 4 年後自動還本

總結

Miniscript 是比特幣腳本開發的重要工具,它使得複雜的比特幣腳本變得可讀、可分析和可驗證。通過 Miniscript,開發者可以安全地構建多簽名、時間鎖和其他高級腳本,同時錢包和工具可以更好地理解和處理這些腳本。隨著 Taproot 的採用,Miniscript 的重要性將進一步提升。

主要優點:

  1. 可讀性:人類可理解的腳本表示
  2. 可分析性:靜態分析腳本行為
  3. 可驗證性:確保腳本安全
  4. 可組合性:靈活構建複雜邏輯
  5. 兼容性:支持 Taproot 和各種地址類型

應用場景:

延伸閱讀與來源

這篇文章對您有幫助嗎?

評論

發表評論

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

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