bitcoin-core-miniscript-ffi 0.1.0

Production-grade FFI bindings to Bitcoin Core's miniscript implementation for cross-verification and reference testing
docs.rs failed to build bitcoin-core-miniscript-ffi-0.1.0
Please check the build logs for more information.
See Builds for ideas on how to fix a failed build, or Metadata for how to configure docs.rs builds.
If you believe this is docs.rs' fault, open an issue.
Visit the last successful build: bitcoin-core-miniscript-ffi-0.5.1

bitcoin-core-miniscript-ffi

Crates.io Documentation License: MIT Build Status

Production-grade FFI bindings to Bitcoin Core's miniscript implementation.

This crate provides direct access to Bitcoin Core's battle-tested C++ miniscript parser and analyzer through safe Rust bindings. It enables cross-verification between Bitcoin Core and other miniscript implementations (like rust-miniscript), ensuring consensus-critical code behaves identically across implementations.

Why This Crate?

  • Reference Implementation: Bitcoin Core's miniscript is the canonical implementation used by the Bitcoin network
  • Cross-Verification: Validate that your miniscript implementation matches Bitcoin Core's behavior exactly
  • Production Tested: Built on code that secures billions of dollars in Bitcoin
  • Full Feature Parity: Supports both P2WSH (SegWit v0) and Tapscript (SegWit v1) contexts
  • Type Safety: Safe Rust wrapper with proper memory management and error handling

Features

  • Parse miniscript expressions from strings
  • Validate miniscript type correctness
  • Check sanity constraints (no duplicate keys, no timelock mixing, resource limits)
  • Extract type properties (B, V, K, W modifiers and more)
  • Calculate maximum witness satisfaction size
  • Convert miniscript back to canonical string representation
  • Thread-safe: Send + Sync implementation

Installation

Add to your Cargo.toml:

[dependencies]
bitcoin-core-miniscript-ffi = "0.1"

Build Requirements

This crate requires:

  • Rust 1.70+ (2024 edition)
  • CMake 3.16+
  • C++20 compatible compiler (GCC 10+, Clang 10+, or MSVC 2019+)
  • Boost 1.73+ (headers only)
  • Bitcoin Core source code (automatically included as a git submodule)

Linux (Debian/Ubuntu)

sudo apt-get install cmake build-essential libboost-dev

macOS

brew install cmake boost

Windows

# Using vcpkg
vcpkg install boost:x64-windows

Building from Source

# Clone with submodules (includes Bitcoin Core source)
git clone --recursive https://github.com/portlandhodl/rust-bitcoin-core-miniscript-ffi.git
cd rust-bitcoin-core-miniscript-ffi

# Build
cargo build --release

# Run tests
cargo test -- --include-ignored

If you cloned without --recursive:

git submodule update --init --recursive

Quick Start

use bitcoin_core_miniscript_ffi::{Miniscript, Context};

fn main() -> Result<(), Box<dyn std::error::Error>> {
    // Parse a simple miniscript (2-of-2 multisig)
    let ms = Miniscript::from_str(
        "and_v(v:pk(Alice),pk(Bob))",
        Context::Wsh
    )?;

    // Validate the miniscript
    assert!(ms.is_valid());
    assert!(ms.is_sane());

    // Get type properties
    println!("Type: {}", ms.get_type().unwrap());

    // Get maximum witness size
    if let Some(size) = ms.max_satisfaction_size() {
        println!("Max witness size: {} bytes", size);
    }

    // Convert back to string (canonical form)
    println!("Canonical: {}", ms.to_string().unwrap());

    Ok(())
}

API Reference

Miniscript

The main type representing a parsed miniscript expression.

impl Miniscript {
    /// Parse a miniscript from a string
    pub fn from_str(input: &str, context: Context) -> Result<Self, Error>;

    /// Convert to canonical string representation
    pub fn to_string(&self) -> Option<String>;

    /// Check if the miniscript is valid (type-checks correctly)
    pub fn is_valid(&self) -> bool;

    /// Check if the miniscript is sane (no duplicate keys, no timelock mixing, etc.)
    pub fn is_sane(&self) -> bool;

    /// Get type properties (e.g., "Bdemsu")
    pub fn get_type(&self) -> Option<String>;

    /// Get maximum witness satisfaction size in bytes
    pub fn max_satisfaction_size(&self) -> Option<usize>;

    /// Get the context this miniscript was parsed with
    pub fn context(&self) -> Context;
}

Context

Script context for miniscript parsing.

pub enum Context {
    /// P2WSH context (SegWit v0) - 520 byte script limit
    Wsh,
    /// Tapscript context (SegWit v1) - no script size limit, x-only pubkeys
    Tapscript,
}

Type Properties

The type string returned by get_type() contains single-character flags:

Flag Meaning
B Base expression (consumes nothing, produces nonzero)
V Verify expression (consumes nothing, produces nothing, fails if unsatisfied)
K Key expression (consumes nothing, produces a public key)
W Wrapped expression (consumes one stack element)
z Zero-arg property (consumes no stack elements)
o One-arg property (consumes exactly one stack element)
n Nonzero property (never produces zero)
d Dissatisfiable property (has a dissatisfaction)
u Unit property (on satisfaction, puts exactly 1 on stack)
e Expression property (can be used as an expression)
f Forced property (always requires a signature)
s Safe property (cannot be malleated)
m Nonmalleable property (satisfaction is unique)
x Expensive verify property
k Timelock property (contains a timelock)

Use Cases

Cross-Verification Testing

use bitcoin_core_miniscript_ffi::{Miniscript, Context};

fn verify_against_core(miniscript_str: &str) -> bool {
    // Parse with Bitcoin Core's implementation
    let core_result = Miniscript::from_str(miniscript_str, Context::Wsh);

    // Compare with your implementation
    match core_result {
        Ok(ms) => {
            // Verify type properties match
            let core_type = ms.get_type().unwrap();
            // ... compare with your implementation's type
            true
        }
        Err(e) => {
            // Bitcoin Core rejected it - your implementation should too
            println!("Core rejected: {}", e);
            false
        }
    }
}

Wallet Development

use bitcoin_core_miniscript_ffi::{Miniscript, Context};

fn validate_spending_policy(policy: &str) -> Result<(), String> {
    let ms = Miniscript::from_str(policy, Context::Wsh)
        .map_err(|e| format!("Invalid policy: {}", e))?;

    if !ms.is_sane() {
        return Err("Policy fails sanity checks".to_string());
    }

    if let Some(size) = ms.max_satisfaction_size() {
        if size > 10000 {
            return Err(format!("Witness too large: {} bytes", size));
        }
    }

    Ok(())
}

Taproot Script Analysis

use bitcoin_core_miniscript_ffi::{Miniscript, Context};

fn analyze_tapscript(script: &str) {
    let ms = Miniscript::from_str(script, Context::Tapscript)
        .expect("valid tapscript");

    println!("Valid: {}", ms.is_valid());
    println!("Sane: {}", ms.is_sane());
    println!("Type: {}", ms.get_type().unwrap_or_default());

    if let Some(size) = ms.max_satisfaction_size() {
        println!("Max witness: {} vbytes", size);
    }
}

Thread Safety

Miniscript implements Send and Sync, making it safe to use across threads:

use bitcoin_core_miniscript_ffi::{Miniscript, Context};
use std::sync::Arc;
use std::thread;

let ms = Arc::new(
    Miniscript::from_str("pk(A)", Context::Wsh).unwrap()
);

let handles: Vec<_> = (0..4).map(|_| {
    let ms = Arc::clone(&ms);
    thread::spawn(move || {
        assert!(ms.is_valid());
    })
}).collect();

for h in handles {
    h.join().unwrap();
}

Performance

The library is optimized for production use:

  • Zero-copy string handling where possible
  • Minimal allocations in hot paths
  • Static linking eliminates runtime overhead
  • Release builds use -O3 optimization

Comparison with rust-miniscript

Feature bitcoin-core-miniscript-ffi rust-miniscript
Implementation Bitcoin Core C++ Pure Rust
Consensus compatibility Reference Aims to match
Dependencies Bitcoin Core, Boost Pure Rust
Build complexity Higher Lower
Use case Cross-verification, reference Production wallets

Recommendation: Use this crate for testing and verification. Use rust-miniscript for production applications, but verify critical paths against this crate.

Contributing

Contributions are welcome! Please feel free to submit a Pull Request.

Development Setup

# Clone with submodules
git clone --recursive https://github.com/portlandhodl/rust-bitcoin-core-miniscript-ffi.git
cd rust-bitcoin-core-miniscript-ffi

# Build in debug mode
cargo build

# Run all tests (including integration tests)
cargo test -- --include-ignored

# Build documentation
cargo doc --open

Running Benchmarks

cargo bench

License

This project is licensed under the MIT License - see the LICENSE file for details.

Bitcoin Core's miniscript implementation is also MIT licensed.

Acknowledgments

Related Projects