miniscript_core_ffi/lib.rs
1//! # bitcoin-core-miniscript-ffi
2//!
3//! **FFI bindings to Bitcoin Core's miniscript implementation.**
4//!
5//! ## Safety
6//!
7//! This crate provides safe Rust wrappers around unsafe FFI calls to Bitcoin Core's C++
8//! miniscript implementation. The unsafe code is necessary for FFI interop and cannot be
9//! eliminated, but it is carefully encapsulated to provide a safe public API.
10//!
11//! ### Why Unsafe Code is Required
12//!
13//! 1. **FFI Boundary**: All calls to the C++ library require `unsafe` blocks because Rust
14//! cannot verify the safety of foreign code.
15//!
16//! 2. **Raw Pointers**: The C API uses raw pointers for:
17//! - Opaque handles to C++ objects (`MiniscriptNode*`)
18//! - String data (`char*`)
19//! - Binary data (`uint8_t*`)
20//! - Callback contexts (`void*`)
21//!
22//! 3. **Callback Trampolines**: The satisfier callbacks must be `extern "C"` functions that
23//! receive raw pointers and convert them back to Rust types.
24//!
25//! ### Safety Guarantees
26//!
27//! Despite the unsafe internals, this crate provides the following safety guarantees:
28//!
29//! - **Memory Safety**: All C-allocated memory is properly freed via RAII (`Drop` impl)
30//! - **Null Safety**: All pointer dereferences are guarded by null checks
31//! - **Lifetime Safety**: The `Miniscript` struct owns its C++ object and ensures it
32//! outlives all references
33//! - **Thread Safety**: `Miniscript` implements `Send` and `Sync` because the underlying
34//! C++ object is immutable after creation
35//! - **No Undefined Behavior**: All unsafe blocks have documented invariants that are
36//! upheld by the implementation
37//!
38//! This crate provides direct access to Bitcoin Core's C++ miniscript parser
39//! and analyzer through safe Rust bindings. It enables cross-verification between Bitcoin Core
40//! and other miniscript implementations (like [rust-miniscript](https://github.com/rust-bitcoin/rust-miniscript)),
41//! ensuring consensus-critical code behaves identically across implementations.
42//!
43//! ## Why This Crate?
44//!
45//! - **Reference Implementation**: Bitcoin Core's miniscript is the canonical implementation
46//! - **Cross-Verification**: Validate that your miniscript implementation matches Bitcoin Core's behavior exactly
47//! - **Production Tested**: Code matches that of Bitcoin Core the majority consensus client
48//! - **Full Feature Parity**: Supports both P2WSH (`SegWit` v0) and Tapscript (`SegWit` v1) contexts
49//! - **Type Safety**: Safe Rust wrapper with proper memory management and error handling
50//!
51//! ## Features
52//!
53//! - Parse miniscript expressions from strings
54//! - Validate miniscript type correctness
55//! - Check sanity constraints (no duplicate keys, no timelock mixing, resource limits)
56//! - Extract type properties (B, V, K, W modifiers and more)
57//! - Calculate maximum witness satisfaction size
58//! - Convert miniscript back to canonical string representation
59//! - Satisfy miniscripts with custom satisfiers
60//! - Thread-safe: `Send + Sync` implementation
61//!
62//! ## Quick Start
63//!
64//! ```rust,no_run
65//! use miniscript_core_ffi::{Miniscript, Context};
66//!
67//! // Parse a simple miniscript (2-of-2 multisig)
68//! let ms = Miniscript::from_str("and_v(v:pk(Alice),pk(Bob))", Context::Wsh)
69//! .expect("valid miniscript");
70//!
71//! // Validate the miniscript
72//! assert!(ms.is_valid());
73//! assert!(ms.is_sane());
74//!
75//! // Get type properties
76//! println!("Type: {}", ms.get_type().unwrap());
77//!
78//! // Get maximum witness size
79//! if let Some(size) = ms.max_satisfaction_size() {
80//! println!("Max witness size: {} bytes", size);
81//! }
82//!
83//! // Convert back to string (canonical form)
84//! println!("Canonical: {}", ms.to_string().unwrap());
85//! ```
86//!
87//! ## Cross-Verification Example
88//!
89//! ```rust,no_run
90//! use miniscript_core_ffi::{Miniscript, Context};
91//!
92//! fn verify_against_core(miniscript_str: &str) -> bool {
93//! // Parse with Bitcoin Core's implementation
94//! let core_result = Miniscript::from_str(miniscript_str, Context::Wsh);
95//!
96//! match core_result {
97//! Ok(ms) => {
98//! // Verify type properties match your implementation
99//! let core_type = ms.get_type().unwrap();
100//! println!("Core type: {}", core_type);
101//! true
102//! }
103//! Err(e) => {
104//! // Bitcoin Core rejected it - your implementation should too
105//! println!("Core rejected: {}", e);
106//! false
107//! }
108//! }
109//! }
110//! ```
111//!
112//! ## Taproot Support
113//!
114//! ```rust,no_run
115//! use miniscript_core_ffi::{Miniscript, Context};
116//!
117//! // Parse a Tapscript miniscript
118//! let ms = Miniscript::from_str("pk(A)", Context::Tapscript)
119//! .expect("valid tapscript");
120//!
121//! println!("Valid: {}", ms.is_valid());
122//! println!("Type: {}", ms.get_type().unwrap_or_default());
123//! ```
124//!
125//! ## Satisfying Miniscripts
126//!
127//! ```rust,no_run
128//! use miniscript_core_ffi::{Miniscript, Context, SimpleSatisfier};
129//!
130//! let ms = Miniscript::from_str("pk(A)", Context::Wsh)
131//! .expect("valid miniscript");
132//!
133//! let mut satisfier = SimpleSatisfier::new();
134//! // Add signature for key A
135//! satisfier.signatures.insert(b"A".to_vec(), vec![0x30, 0x44, /* ... */]);
136//!
137//! let result = ms.satisfy(satisfier, true).expect("satisfaction");
138//! println!("Witness stack has {} elements", result.stack.len());
139//! ```
140//!
141//! ## Type Properties
142//!
143//! The type string returned by [`Miniscript::get_type()`] contains single-character flags:
144//!
145//! | Flag | Meaning |
146//! |------|---------|
147//! | `B` | Base expression (consumes nothing, produces nonzero) |
148//! | `V` | Verify expression (consumes nothing, produces nothing, fails if unsatisfied) |
149//! | `K` | Key expression (consumes nothing, produces a public key) |
150//! | `W` | Wrapped expression (consumes one stack element) |
151//! | `z` | Zero-arg property (consumes no stack elements) |
152//! | `o` | One-arg property (consumes exactly one stack element) |
153//! | `n` | Nonzero property (never produces zero) |
154//! | `d` | Dissatisfiable property (has a dissatisfaction) |
155//! | `u` | Unit property (on satisfaction, puts exactly 1 on stack) |
156//! | `e` | Expression property (can be used as an expression) |
157//! | `f` | Forced property (always requires a signature) |
158//! | `s` | Safe property (cannot be malleated) |
159//! | `m` | Nonmalleable property (satisfaction is unique) |
160//! | `x` | Expensive verify property |
161//! | `k` | Timelock property (contains a timelock) |
162//!
163//! ## Thread Safety
164//!
165//! [`Miniscript`] implements `Send` and `Sync`, making it safe to use across threads:
166//!
167//! ```rust,no_run
168//! use miniscript_core_ffi::{Miniscript, Context};
169//! use std::sync::Arc;
170//! use std::thread;
171//!
172//! let ms = Arc::new(
173//! Miniscript::from_str("pk(A)", Context::Wsh).unwrap()
174//! );
175//!
176//! let handles: Vec<_> = (0..4).map(|_| {
177//! let ms = Arc::clone(&ms);
178//! thread::spawn(move || {
179//! assert!(ms.is_valid());
180//! })
181//! }).collect();
182//!
183//! for h in handles {
184//! h.join().unwrap();
185//! }
186//! ```
187//!
188//! ## Comparison with rust-miniscript
189//!
190//! | Feature | bitcoin-core-miniscript-ffi | rust-miniscript |
191//! |---------|----------------------------|-----------------|
192//! | Implementation | Bitcoin Core C++ | Pure Rust |
193//! | Consensus compatibility | Reference | Aims to match |
194//! | Dependencies | Bitcoin Core, Boost | Pure Rust |
195//! | Build complexity | Higher | Lower |
196//! | Use case | Cross-verification, reference | Production wallets |
197//!
198//! **Recommendation**: Use this crate for testing and verification. Use rust-miniscript for
199//! production applications, but verify critical paths against this crate.
200
201#![allow(non_upper_case_globals)]
202#![allow(non_camel_case_types)]
203#![allow(non_snake_case)]
204
205// FFI bindings generated by bindgen
206mod ffi {
207 #![allow(dead_code)]
208 #![allow(non_upper_case_globals)]
209 #![allow(non_camel_case_types)]
210 #![allow(non_snake_case)]
211 include!(concat!(env!("OUT_DIR"), "/bindings.rs"));
212}
213
214// Re-export FFI types that are used in the public API
215pub use ffi::{MiniscriptAvailability, MiniscriptContext, MiniscriptNode, MiniscriptResult};
216pub use ffi::{SatisfactionResult as FfiSatisfactionResult, SatisfierCallbacks};
217
218// Import FFI functions for internal use
219use ffi::{
220 miniscript_check_duplicate_key, miniscript_check_ops_limit, miniscript_check_stack_size,
221 miniscript_free_bytes, miniscript_free_string, miniscript_from_script,
222 miniscript_get_exec_stack_size, miniscript_get_ops, miniscript_get_script_size,
223 miniscript_get_stack_size, miniscript_get_static_ops, miniscript_get_type,
224 miniscript_has_timelock_mix, miniscript_is_non_malleable, miniscript_is_sane,
225 miniscript_is_valid, miniscript_is_valid_top_level, miniscript_max_satisfaction_size,
226 miniscript_needs_signature, miniscript_node_free, miniscript_satisfaction_result_free,
227 miniscript_satisfy, miniscript_to_script, miniscript_to_string, miniscript_valid_satisfactions,
228 miniscript_version,
229};
230
231// Descriptor module
232pub mod descriptor;
233pub use descriptor::{
234 Descriptor, DescriptorBuilder, Network as DescriptorNetwork, descriptor_version,
235 get_descriptor_checksum,
236};
237
238use std::collections::HashMap;
239use std::ffi::{CStr, CString};
240use std::fmt;
241use std::ptr;
242
243// Re-export bitcoin types for convenience
244pub use bitcoin::Witness;
245pub use bitcoin::hashes::hash160::Hash as Hash160;
246pub use bitcoin::hashes::ripemd160::Hash as Ripemd160;
247pub use bitcoin::hashes::sha256::Hash as Sha256;
248pub use bitcoin::hashes::sha256d::Hash as Hash256;
249pub use bitcoin::locktime::absolute::LockTime;
250pub use bitcoin::locktime::relative::LockTime as RelativeLockTime;
251pub use bitcoin::script::ScriptBuf;
252pub use bitcoin::secp256k1::ecdsa::Signature as EcdsaSignature;
253pub use bitcoin::taproot::Signature as SchnorrSignature;
254
255/// Script context for miniscript parsing.
256///
257/// Miniscript expressions are context-dependent - the same expression may be
258/// valid in one context but not another due to different script size limits,
259/// opcode availability, and signature requirements.
260///
261/// # Example
262///
263/// ```rust,no_run
264/// use miniscript_core_ffi::{Miniscript, Context};
265///
266/// // Parse for SegWit v0 (P2WSH)
267/// let wsh = Miniscript::from_str("pk(A)", Context::Wsh);
268///
269/// // Parse for SegWit v1 (Tapscript)
270/// let tap = Miniscript::from_str("pk(A)", Context::Tapscript);
271/// ```
272#[derive(Debug, Clone, Copy, PartialEq, Eq)]
273pub enum Context {
274 /// P2WSH context (`SegWit` v0)
275 ///
276 /// Used for Pay-to-Witness-Script-Hash outputs. Has a 10,000 byte script
277 /// size limit and uses ECDSA signatures.
278 Wsh,
279 /// Tapscript context (`SegWit` v1)
280 ///
281 /// Used for Taproot script paths. Has a larger script size limit and
282 /// uses Schnorr signatures. Some opcodes like `OP_CHECKMULTISIG` are
283 /// disabled in favor of `OP_CHECKSIGADD`.
284 Tapscript,
285}
286
287impl From<Context> for MiniscriptContext {
288 fn from(ctx: Context) -> Self {
289 match ctx {
290 Context::Wsh => Self::MINISCRIPT_CONTEXT_WSH,
291 Context::Tapscript => Self::MINISCRIPT_CONTEXT_TAPSCRIPT,
292 }
293 }
294}
295
296/// Availability of a satisfaction.
297///
298/// Indicates whether a miniscript can be satisfied with the provided data.
299/// This is used both for actual satisfaction attempts and for size estimation.
300///
301/// # Example
302///
303/// ```rust,no_run
304/// use miniscript_core_ffi::Availability;
305///
306/// fn check_availability(avail: Availability) {
307/// match avail {
308/// Availability::Yes => println!("Can satisfy"),
309/// Availability::No => println!("Cannot satisfy"),
310/// Availability::Maybe => println!("Might be able to satisfy"),
311/// }
312/// }
313/// ```
314#[derive(Debug, Clone, Copy, PartialEq, Eq)]
315pub enum Availability {
316 /// Satisfaction is not available.
317 ///
318 /// The required data (signature, preimage, etc.) is not present.
319 No,
320 /// Satisfaction is available.
321 ///
322 /// All required data is present and the satisfaction can be produced.
323 Yes,
324 /// Satisfaction may be available (for size estimation).
325 ///
326 /// Used when estimating witness sizes without actually having the data.
327 Maybe,
328}
329
330impl From<MiniscriptAvailability> for Availability {
331 fn from(avail: MiniscriptAvailability) -> Self {
332 match avail {
333 MiniscriptAvailability::MINISCRIPT_AVAILABILITY_NO => Self::No,
334 MiniscriptAvailability::MINISCRIPT_AVAILABILITY_YES => Self::Yes,
335 MiniscriptAvailability::MINISCRIPT_AVAILABILITY_MAYBE => Self::Maybe,
336 }
337 }
338}
339
340impl From<Availability> for MiniscriptAvailability {
341 fn from(avail: Availability) -> Self {
342 match avail {
343 Availability::No => Self::MINISCRIPT_AVAILABILITY_NO,
344 Availability::Yes => Self::MINISCRIPT_AVAILABILITY_YES,
345 Availability::Maybe => Self::MINISCRIPT_AVAILABILITY_MAYBE,
346 }
347 }
348}
349
350/// Error type for miniscript operations.
351///
352/// Contains a human-readable error message describing what went wrong.
353/// This error type is returned by parsing and satisfaction operations.
354///
355/// # Example
356///
357/// ```rust,no_run
358/// use miniscript_core_ffi::{Miniscript, Context};
359///
360/// let result = Miniscript::from_str("invalid_miniscript", Context::Wsh);
361/// if let Err(e) = result {
362/// println!("Parse error: {}", e);
363/// }
364/// ```
365#[derive(Debug)]
366pub struct Error {
367 /// The error message describing what went wrong.
368 message: String,
369}
370
371impl fmt::Display for Error {
372 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
373 write!(f, "{}", self.message)
374 }
375}
376
377impl std::error::Error for Error {}
378
379/// Trait for providing satisfaction data to miniscript.
380///
381/// Implement this trait to provide signatures, hash preimages, and timelock
382/// information needed to satisfy a miniscript. The satisfier is called during
383/// the satisfaction process to provide the necessary data.
384///
385/// # Example
386///
387/// ```rust,no_run
388/// use miniscript_core_ffi::{Satisfier, Availability};
389///
390/// struct MySatisfier {
391/// // Your signing keys and preimages
392/// }
393///
394/// impl Satisfier for MySatisfier {
395/// fn sign(&self, key: &[u8]) -> (Availability, Option<Vec<u8>>) {
396/// // Return signature for the key if available
397/// (Availability::No, None)
398/// }
399///
400/// fn check_after(&self, value: u32) -> bool {
401/// // Check if absolute timelock is satisfied
402/// false
403/// }
404///
405/// fn check_older(&self, value: u32) -> bool {
406/// // Check if relative timelock is satisfied
407/// false
408/// }
409///
410/// fn sat_sha256(&self, hash: &[u8]) -> (Availability, Option<Vec<u8>>) {
411/// (Availability::No, None)
412/// }
413///
414/// fn sat_ripemd160(&self, hash: &[u8]) -> (Availability, Option<Vec<u8>>) {
415/// (Availability::No, None)
416/// }
417///
418/// fn sat_hash256(&self, hash: &[u8]) -> (Availability, Option<Vec<u8>>) {
419/// (Availability::No, None)
420/// }
421///
422/// fn sat_hash160(&self, hash: &[u8]) -> (Availability, Option<Vec<u8>>) {
423/// (Availability::No, None)
424/// }
425/// }
426/// ```
427pub trait Satisfier: Send {
428 /// Sign with the given key, returning the signature bytes.
429 ///
430 /// # Arguments
431 ///
432 /// * `key` - The key identifier bytes (as used in the miniscript)
433 ///
434 /// # Returns
435 ///
436 /// A tuple of (availability, optional signature bytes). Return `Availability::Yes`
437 /// with the signature if signing succeeds, or `Availability::No` with `None` if
438 /// the key is not available.
439 fn sign(&self, key: &[u8]) -> (Availability, Option<Vec<u8>>);
440
441 /// Check if the absolute timelock is satisfied.
442 ///
443 /// # Arguments
444 ///
445 /// * `value` - The timelock value (block height or Unix timestamp)
446 ///
447 /// # Returns
448 ///
449 /// `true` if the current time/height satisfies the timelock.
450 fn check_after(&self, value: u32) -> bool;
451
452 /// Check if the relative timelock is satisfied.
453 ///
454 /// # Arguments
455 ///
456 /// * `value` - The relative timelock value (blocks or time units)
457 ///
458 /// # Returns
459 ///
460 /// `true` if the relative timelock is satisfied.
461 fn check_older(&self, value: u32) -> bool;
462
463 /// Get the preimage for a SHA256 hash.
464 ///
465 /// # Arguments
466 ///
467 /// * `hash` - The 32-byte SHA256 hash
468 ///
469 /// # Returns
470 ///
471 /// A tuple of (availability, optional preimage bytes).
472 fn sat_sha256(&self, hash: &[u8]) -> (Availability, Option<Vec<u8>>);
473
474 /// Get the preimage for a RIPEMD160 hash.
475 ///
476 /// # Arguments
477 ///
478 /// * `hash` - The 20-byte RIPEMD160 hash
479 ///
480 /// # Returns
481 ///
482 /// A tuple of (availability, optional preimage bytes).
483 fn sat_ripemd160(&self, hash: &[u8]) -> (Availability, Option<Vec<u8>>);
484
485 /// Get the preimage for a HASH256 (double SHA256) hash.
486 ///
487 /// # Arguments
488 ///
489 /// * `hash` - The 32-byte HASH256 hash
490 ///
491 /// # Returns
492 ///
493 /// A tuple of (availability, optional preimage bytes).
494 fn sat_hash256(&self, hash: &[u8]) -> (Availability, Option<Vec<u8>>);
495
496 /// Get the preimage for a HASH160 hash.
497 ///
498 /// # Arguments
499 ///
500 /// * `hash` - The 20-byte HASH160 hash (RIPEMD160 of SHA256)
501 ///
502 /// # Returns
503 ///
504 /// A tuple of (availability, optional preimage bytes).
505 fn sat_hash160(&self, hash: &[u8]) -> (Availability, Option<Vec<u8>>);
506}
507
508/// A simple satisfier that uses pre-populated data.
509///
510/// This is a convenience implementation of [`Satisfier`] that stores signatures,
511/// hash preimages, and timelock information in hash maps and sets. Populate the
512/// fields before passing to [`Miniscript::satisfy()`].
513///
514/// # Example
515///
516/// ```rust,no_run
517/// use miniscript_core_ffi::SimpleSatisfier;
518///
519/// let mut satisfier = SimpleSatisfier::new();
520///
521/// // Add a signature for key "A"
522/// satisfier.signatures.insert(b"A".to_vec(), vec![0x30, 0x44, /* ... */]);
523///
524/// // Mark absolute timelock 500000 as satisfied
525/// satisfier.after_satisfied.insert(500000);
526///
527/// // Add a SHA256 preimage
528/// let hash = vec![/* 32-byte hash */];
529/// let preimage = vec![/* preimage bytes */];
530/// satisfier.sha256_preimages.insert(hash, preimage);
531/// ```
532pub struct SimpleSatisfier {
533 /// Map from key bytes to signature bytes
534 pub signatures: HashMap<Vec<u8>, Vec<u8>>,
535 /// Set of satisfied absolute timelocks
536 pub after_satisfied: std::collections::HashSet<u32>,
537 /// Set of satisfied relative timelocks
538 pub older_satisfied: std::collections::HashSet<u32>,
539 /// Map from SHA256 hash to preimage
540 pub sha256_preimages: HashMap<Vec<u8>, Vec<u8>>,
541 /// Map from RIPEMD160 hash to preimage
542 pub ripemd160_preimages: HashMap<Vec<u8>, Vec<u8>>,
543 /// Map from HASH256 hash to preimage
544 pub hash256_preimages: HashMap<Vec<u8>, Vec<u8>>,
545 /// Map from HASH160 hash to preimage
546 pub hash160_preimages: HashMap<Vec<u8>, Vec<u8>>,
547}
548
549impl SimpleSatisfier {
550 /// Create a new empty satisfier.
551 #[must_use]
552 pub fn new() -> Self {
553 Self {
554 signatures: HashMap::new(),
555 after_satisfied: std::collections::HashSet::new(),
556 older_satisfied: std::collections::HashSet::new(),
557 sha256_preimages: HashMap::new(),
558 ripemd160_preimages: HashMap::new(),
559 hash256_preimages: HashMap::new(),
560 hash160_preimages: HashMap::new(),
561 }
562 }
563}
564
565impl Default for SimpleSatisfier {
566 fn default() -> Self {
567 Self::new()
568 }
569}
570
571impl Satisfier for SimpleSatisfier {
572 fn sign(&self, key: &[u8]) -> (Availability, Option<Vec<u8>>) {
573 self.signatures
574 .get(key)
575 .map_or((Availability::No, None), |sig| {
576 (Availability::Yes, Some(sig.clone()))
577 })
578 }
579
580 fn check_after(&self, value: u32) -> bool {
581 self.after_satisfied.contains(&value)
582 }
583
584 fn check_older(&self, value: u32) -> bool {
585 self.older_satisfied.contains(&value)
586 }
587
588 fn sat_sha256(&self, hash: &[u8]) -> (Availability, Option<Vec<u8>>) {
589 self.sha256_preimages
590 .get(hash)
591 .map_or((Availability::No, None), |preimage| {
592 (Availability::Yes, Some(preimage.clone()))
593 })
594 }
595
596 fn sat_ripemd160(&self, hash: &[u8]) -> (Availability, Option<Vec<u8>>) {
597 self.ripemd160_preimages
598 .get(hash)
599 .map_or((Availability::No, None), |preimage| {
600 (Availability::Yes, Some(preimage.clone()))
601 })
602 }
603
604 fn sat_hash256(&self, hash: &[u8]) -> (Availability, Option<Vec<u8>>) {
605 self.hash256_preimages
606 .get(hash)
607 .map_or((Availability::No, None), |preimage| {
608 (Availability::Yes, Some(preimage.clone()))
609 })
610 }
611
612 fn sat_hash160(&self, hash: &[u8]) -> (Availability, Option<Vec<u8>>) {
613 self.hash160_preimages
614 .get(hash)
615 .map_or((Availability::No, None), |preimage| {
616 (Availability::Yes, Some(preimage.clone()))
617 })
618 }
619}
620
621/// Result of a satisfaction attempt.
622///
623/// Contains the availability status and the witness stack that can be used
624/// to satisfy the miniscript in a transaction.
625///
626/// # Example
627///
628/// ```rust,no_run
629/// use miniscript_core_ffi::{Miniscript, Context, SimpleSatisfier, Availability};
630///
631/// let ms = Miniscript::from_str("pk(A)", Context::Wsh).unwrap();
632/// let satisfier = SimpleSatisfier::new();
633///
634/// let result = ms.satisfy(satisfier, true).unwrap();
635/// match result.availability {
636/// Availability::Yes => {
637/// let witness = result.to_witness();
638/// println!("Got witness with {} elements", witness.len());
639/// }
640/// _ => println!("Could not satisfy"),
641/// }
642/// ```
643pub struct SatisfyResult {
644 /// Whether the satisfaction was successful.
645 ///
646 /// - `Availability::Yes` - Satisfaction succeeded, `stack` contains valid witness data
647 /// - `Availability::No` - Satisfaction failed, required data not available
648 /// - `Availability::Maybe` - Partial satisfaction (for size estimation)
649 pub availability: Availability,
650 /// The witness stack (if successful).
651 ///
652 /// Each element is a byte vector representing one witness stack item.
653 /// Use [`to_witness()`](Self::to_witness) to convert to a [`bitcoin::Witness`].
654 pub stack: Vec<Vec<u8>>,
655}
656
657impl SatisfyResult {
658 /// Convert the witness stack to a [`bitcoin::Witness`].
659 ///
660 /// This is useful for constructing transactions with the satisfaction.
661 #[must_use]
662 pub fn to_witness(&self) -> Witness {
663 Witness::from_slice(&self.stack)
664 }
665}
666
667impl std::fmt::Debug for SatisfyResult {
668 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
669 f.debug_struct("SatisfyResult")
670 .field("availability", &self.availability)
671 .field("stack_len", &self.stack.len())
672 .finish()
673 }
674}
675
676// FFI callback trampolines
677
678/// FFI callback function for signing operations.
679///
680/// This function is called by the C++ miniscript implementation when it needs
681/// a signature for a given key during satisfaction. It acts as a trampoline
682/// between the C++ code and the Rust `Satisfier` trait implementation.
683///
684/// # Safety
685///
686/// This function is marked as safe but contains an unsafe block because:
687/// - It is only called from C++ code via the FFI boundary
688/// - The caller (C++ code) guarantees that:
689/// - `context` is a valid pointer created by `Box::into_raw(Box::new(Box<dyn Satisfier>))`
690/// - `key_bytes` is a valid pointer to `key_len` bytes
691/// - `sig_out` and `sig_len_out` are valid, non-null pointers
692/// - Memory allocated with `libc::malloc` is freed by the C++ caller
693///
694/// # Invariants
695///
696/// - The `context` pointer must remain valid for the duration of the callback
697/// - The callback must not panic (panics across FFI boundaries are UB)
698///
699/// # Parameters
700///
701/// * `context` - Raw pointer to a boxed `Satisfier` trait object
702/// * `key_bytes` - Pointer to the key bytes to sign with
703/// * `key_len` - Length of the key bytes
704/// * `sig_out` - Output pointer for the signature bytes (allocated with malloc)
705/// * `sig_len_out` - Output pointer for the signature length
706///
707/// # Returns
708///
709/// Returns a `MiniscriptAvailability` indicating whether the signature is available.
710extern "C" fn sign_callback(
711 context: *mut std::ffi::c_void,
712 key_bytes: *const u8,
713 key_len: usize,
714 sig_out: *mut *mut u8,
715 sig_len_out: *mut usize,
716) -> MiniscriptAvailability {
717 // SAFETY: This callback is only invoked by the C++ miniscript library during
718 // the `satisfy` call. The invariants are:
719 // 1. `context` was created by `Box::into_raw(Box::new(boxed_satisfier))` in `satisfy()`
720 // 2. `key_bytes` points to valid memory of `key_len` bytes (from C++ std::vector)
721 // 3. `sig_out` and `sig_len_out` are valid output pointers (stack-allocated in C++)
722 // 4. The satisfier outlives this callback (it's freed after `miniscript_satisfy` returns)
723 unsafe {
724 let satisfier = &*(context as *const Box<dyn Satisfier>);
725 let key = std::slice::from_raw_parts(key_bytes, key_len);
726
727 let (avail, sig) = satisfier.sign(key);
728
729 if let Some(sig_data) = sig {
730 let len = sig_data.len();
731 let ptr = libc::malloc(len).cast::<u8>();
732 if !ptr.is_null() {
733 std::ptr::copy_nonoverlapping(sig_data.as_ptr(), ptr, len);
734 *sig_out = ptr;
735 *sig_len_out = len;
736 }
737 }
738
739 avail.into()
740 }
741}
742
743/// FFI callback function for checking absolute timelock satisfaction.
744///
745/// This function is called by the C++ miniscript implementation when it needs
746/// to check if an absolute timelock (`OP_CHECKLOCKTIMEVERIFY`) is satisfied.
747/// It acts as a trampoline between the C++ code and the Rust `Satisfier` trait.
748///
749/// # Safety
750///
751/// This function contains an unsafe block. The caller (C++ code) guarantees:
752/// - `context` is a valid pointer created by `Box::into_raw(Box::new(Box<dyn Satisfier>))`
753/// - The satisfier remains valid for the duration of the callback
754///
755/// # Parameters
756///
757/// * `context` - Raw pointer to a boxed `Satisfier` trait object
758/// * `value` - The timelock value to check (block height or Unix timestamp)
759///
760/// # Returns
761///
762/// Returns `true` if the timelock is satisfied, `false` otherwise.
763extern "C" fn check_after_callback(context: *mut std::ffi::c_void, value: u32) -> bool {
764 // SAFETY: `context` was created by `Box::into_raw` in `satisfy()` and remains
765 // valid until after `miniscript_satisfy` returns.
766 unsafe {
767 let satisfier = &*(context as *const Box<dyn Satisfier>);
768 satisfier.check_after(value)
769 }
770}
771
772/// FFI callback function for checking relative timelock satisfaction.
773///
774/// This function is called by the C++ miniscript implementation when it needs
775/// to check if a relative timelock (`OP_CHECKSEQUENCEVERIFY`) is satisfied.
776/// It acts as a trampoline between the C++ code and the Rust `Satisfier` trait.
777///
778/// # Safety
779///
780/// This function contains an unsafe block. The caller (C++ code) guarantees:
781/// - `context` is a valid pointer created by `Box::into_raw(Box::new(Box<dyn Satisfier>))`
782/// - The satisfier remains valid for the duration of the callback
783///
784/// # Parameters
785///
786/// * `context` - Raw pointer to a boxed `Satisfier` trait object
787/// * `value` - The relative timelock value to check (block count or time units)
788///
789/// # Returns
790///
791/// Returns `true` if the relative timelock is satisfied, `false` otherwise.
792extern "C" fn check_older_callback(context: *mut std::ffi::c_void, value: u32) -> bool {
793 // SAFETY: `context` was created by `Box::into_raw` in `satisfy()` and remains
794 // valid until after `miniscript_satisfy` returns.
795 unsafe {
796 let satisfier = &*(context as *const Box<dyn Satisfier>);
797 satisfier.check_older(value)
798 }
799}
800
801/// FFI callback function for SHA256 hash preimage satisfaction.
802///
803/// This function is called by the C++ miniscript implementation when it needs
804/// a preimage for a SHA256 hash during satisfaction. It acts as a trampoline
805/// between the C++ code and the Rust `Satisfier` trait implementation.
806///
807/// # Safety
808///
809/// This function contains an unsafe block. The caller (C++ code) guarantees:
810/// - `context` is a valid pointer created by `Box::into_raw(Box::new(Box<dyn Satisfier>))`
811/// - `hash` is a valid pointer to `hash_len` bytes
812/// - `preimage_out` and `preimage_len_out` are valid, non-null pointers
813/// - Memory allocated with `libc::malloc` is freed by the C++ caller
814///
815/// # Parameters
816///
817/// * `context` - Raw pointer to a boxed `Satisfier` trait object
818/// * `hash` - Pointer to the SHA256 hash bytes (32 bytes)
819/// * `hash_len` - Length of the hash bytes (should be 32)
820/// * `preimage_out` - Output pointer for the preimage bytes (allocated with malloc)
821/// * `preimage_len_out` - Output pointer for the preimage length
822///
823/// # Returns
824///
825/// Returns a `MiniscriptAvailability` indicating whether the preimage is available.
826extern "C" fn sat_sha256_callback(
827 context: *mut std::ffi::c_void,
828 hash: *const u8,
829 hash_len: usize,
830 preimage_out: *mut *mut u8,
831 preimage_len_out: *mut usize,
832) -> MiniscriptAvailability {
833 // SAFETY: See function-level safety documentation. All pointers are valid
834 // for the duration of the callback as guaranteed by the C++ caller.
835 unsafe {
836 let satisfier = &*(context as *const Box<dyn Satisfier>);
837 let hash_slice = std::slice::from_raw_parts(hash, hash_len);
838
839 let (avail, preimage) = satisfier.sat_sha256(hash_slice);
840
841 if let Some(preimage_data) = preimage {
842 let len = preimage_data.len();
843 let ptr = libc::malloc(len).cast::<u8>();
844 if !ptr.is_null() {
845 std::ptr::copy_nonoverlapping(preimage_data.as_ptr(), ptr, len);
846 *preimage_out = ptr;
847 *preimage_len_out = len;
848 }
849 }
850
851 avail.into()
852 }
853}
854
855/// FFI callback function for RIPEMD160 hash preimage satisfaction.
856///
857/// This function is called by the C++ miniscript implementation when it needs
858/// a preimage for a RIPEMD160 hash during satisfaction. It acts as a trampoline
859/// between the C++ code and the Rust `Satisfier` trait implementation.
860///
861/// # Safety
862///
863/// This function contains an unsafe block. The caller (C++ code) guarantees:
864/// - `context` is a valid pointer created by `Box::into_raw(Box::new(Box<dyn Satisfier>))`
865/// - `hash` is a valid pointer to `hash_len` bytes
866/// - `preimage_out` and `preimage_len_out` are valid, non-null pointers
867/// - Memory allocated with `libc::malloc` is freed by the C++ caller
868///
869/// # Parameters
870///
871/// * `context` - Raw pointer to a boxed `Satisfier` trait object
872/// * `hash` - Pointer to the RIPEMD160 hash bytes (20 bytes)
873/// * `hash_len` - Length of the hash bytes (should be 20)
874/// * `preimage_out` - Output pointer for the preimage bytes (allocated with malloc)
875/// * `preimage_len_out` - Output pointer for the preimage length
876///
877/// # Returns
878///
879/// Returns a `MiniscriptAvailability` indicating whether the preimage is available.
880extern "C" fn sat_ripemd160_callback(
881 context: *mut std::ffi::c_void,
882 hash: *const u8,
883 hash_len: usize,
884 preimage_out: *mut *mut u8,
885 preimage_len_out: *mut usize,
886) -> MiniscriptAvailability {
887 // SAFETY: See function-level safety documentation. All pointers are valid
888 // for the duration of the callback as guaranteed by the C++ caller.
889 unsafe {
890 let satisfier = &*(context as *const Box<dyn Satisfier>);
891 let hash_slice = std::slice::from_raw_parts(hash, hash_len);
892
893 let (avail, preimage) = satisfier.sat_ripemd160(hash_slice);
894
895 if let Some(preimage_data) = preimage {
896 let len = preimage_data.len();
897 let ptr = libc::malloc(len).cast::<u8>();
898 if !ptr.is_null() {
899 std::ptr::copy_nonoverlapping(preimage_data.as_ptr(), ptr, len);
900 *preimage_out = ptr;
901 *preimage_len_out = len;
902 }
903 }
904
905 avail.into()
906 }
907}
908
909/// FFI callback function for HASH256 (double SHA256) hash preimage satisfaction.
910///
911/// This function is called by the C++ miniscript implementation when it needs
912/// a preimage for a HASH256 hash during satisfaction. HASH256 is double SHA256,
913/// commonly used in Bitcoin. It acts as a trampoline between the C++ code and
914/// the Rust `Satisfier` trait implementation.
915///
916/// # Safety
917///
918/// This function contains an unsafe block. The caller (C++ code) guarantees:
919/// - `context` is a valid pointer created by `Box::into_raw(Box::new(Box<dyn Satisfier>))`
920/// - `hash` is a valid pointer to `hash_len` bytes
921/// - `preimage_out` and `preimage_len_out` are valid, non-null pointers
922/// - Memory allocated with `libc::malloc` is freed by the C++ caller
923///
924/// # Parameters
925///
926/// * `context` - Raw pointer to a boxed `Satisfier` trait object
927/// * `hash` - Pointer to the HASH256 hash bytes (32 bytes)
928/// * `hash_len` - Length of the hash bytes (should be 32)
929/// * `preimage_out` - Output pointer for the preimage bytes (allocated with malloc)
930/// * `preimage_len_out` - Output pointer for the preimage length
931///
932/// # Returns
933///
934/// Returns a `MiniscriptAvailability` indicating whether the preimage is available.
935extern "C" fn sat_hash256_callback(
936 context: *mut std::ffi::c_void,
937 hash: *const u8,
938 hash_len: usize,
939 preimage_out: *mut *mut u8,
940 preimage_len_out: *mut usize,
941) -> MiniscriptAvailability {
942 // SAFETY: See function-level safety documentation. All pointers are valid
943 // for the duration of the callback as guaranteed by the C++ caller.
944 unsafe {
945 let satisfier = &*(context as *const Box<dyn Satisfier>);
946 let hash_slice = std::slice::from_raw_parts(hash, hash_len);
947
948 let (avail, preimage) = satisfier.sat_hash256(hash_slice);
949
950 if let Some(preimage_data) = preimage {
951 let len = preimage_data.len();
952 let ptr = libc::malloc(len).cast::<u8>();
953 if !ptr.is_null() {
954 std::ptr::copy_nonoverlapping(preimage_data.as_ptr(), ptr, len);
955 *preimage_out = ptr;
956 *preimage_len_out = len;
957 }
958 }
959
960 avail.into()
961 }
962}
963
964/// FFI callback function for HASH160 (RIPEMD160 of SHA256) hash preimage satisfaction.
965///
966/// This function is called by the C++ miniscript implementation when it needs
967/// a preimage for a HASH160 hash during satisfaction. HASH160 is RIPEMD160(SHA256(x)),
968/// commonly used in Bitcoin for address generation. It acts as a trampoline between
969/// the C++ code and the Rust `Satisfier` trait implementation.
970///
971/// # Safety
972///
973/// This function contains an unsafe block. The caller (C++ code) guarantees:
974/// - `context` is a valid pointer created by `Box::into_raw(Box::new(Box<dyn Satisfier>))`
975/// - `hash` is a valid pointer to `hash_len` bytes
976/// - `preimage_out` and `preimage_len_out` are valid, non-null pointers
977/// - Memory allocated with `libc::malloc` is freed by the C++ caller
978///
979/// # Parameters
980///
981/// * `context` - Raw pointer to a boxed `Satisfier` trait object
982/// * `hash` - Pointer to the HASH160 hash bytes (20 bytes)
983/// * `hash_len` - Length of the hash bytes (should be 20)
984/// * `preimage_out` - Output pointer for the preimage bytes (allocated with malloc)
985/// * `preimage_len_out` - Output pointer for the preimage length
986///
987/// # Returns
988///
989/// Returns a `MiniscriptAvailability` indicating whether the preimage is available.
990extern "C" fn sat_hash160_callback(
991 context: *mut std::ffi::c_void,
992 hash: *const u8,
993 hash_len: usize,
994 preimage_out: *mut *mut u8,
995 preimage_len_out: *mut usize,
996) -> MiniscriptAvailability {
997 // SAFETY: See function-level safety documentation. All pointers are valid
998 // for the duration of the callback as guaranteed by the C++ caller.
999 unsafe {
1000 let satisfier = &*(context as *const Box<dyn Satisfier>);
1001 let hash_slice = std::slice::from_raw_parts(hash, hash_len);
1002
1003 let (avail, preimage) = satisfier.sat_hash160(hash_slice);
1004
1005 if let Some(preimage_data) = preimage {
1006 let len = preimage_data.len();
1007 let ptr = libc::malloc(len).cast::<u8>();
1008 if !ptr.is_null() {
1009 std::ptr::copy_nonoverlapping(preimage_data.as_ptr(), ptr, len);
1010 *preimage_out = ptr;
1011 *preimage_len_out = len;
1012 }
1013 }
1014
1015 avail.into()
1016 }
1017}
1018
1019/// A parsed miniscript node.
1020///
1021/// This is a safe wrapper around Bitcoin Core's C++ miniscript implementation.
1022/// It provides methods for parsing, validating, analyzing, and satisfying
1023/// miniscript expressions.
1024///
1025/// # Thread Safety
1026///
1027/// `Miniscript` implements `Send` and `Sync`, making it safe to share across
1028/// threads. The underlying C++ object is immutable after creation.
1029///
1030/// # Memory Management
1031///
1032/// The struct owns the underlying C++ object and will free it when dropped.
1033/// Do not attempt to use the raw pointer after the `Miniscript` is dropped.
1034///
1035/// # Example
1036///
1037/// ```rust,no_run
1038/// use miniscript_core_ffi::{Miniscript, Context};
1039///
1040/// // Parse a miniscript
1041/// let ms = Miniscript::from_str("and_v(v:pk(A),pk(B))", Context::Wsh)
1042/// .expect("valid miniscript");
1043///
1044/// // Check properties
1045/// assert!(ms.is_valid());
1046/// assert!(ms.is_sane());
1047/// println!("Type: {}", ms.get_type().unwrap());
1048/// println!("Max witness size: {:?}", ms.max_satisfaction_size());
1049/// ```
1050pub struct Miniscript {
1051 /// Raw pointer to the C++ `MiniscriptNode` object.
1052 ptr: *mut MiniscriptNode,
1053 /// The context this miniscript was parsed with.
1054 context: Context,
1055}
1056
1057// SAFETY: The underlying C++ object is self-contained and doesn't use thread-local storage.
1058// The node is immutable after creation, so it's safe to send between threads.
1059unsafe impl Send for Miniscript {}
1060
1061// SAFETY: All methods on Miniscript take &self and the underlying object is immutable.
1062unsafe impl Sync for Miniscript {}
1063
1064impl Miniscript {
1065 /// Parse a miniscript from a string.
1066 ///
1067 /// # Arguments
1068 ///
1069 /// * `input` - The miniscript string (e.g., "`and_v(v:pk(A),pk(B))`")
1070 /// * `context` - The script context (WSH or Tapscript)
1071 ///
1072 /// # Errors
1073 ///
1074 /// Returns an error if parsing fails.
1075 pub fn from_str(input: &str, context: Context) -> Result<Self, Error> {
1076 let c_input = CString::new(input).map_err(|_| Error {
1077 message: "input contains null byte".to_string(),
1078 })?;
1079
1080 let mut node_ptr: *mut MiniscriptNode = ptr::null_mut();
1081
1082 // SAFETY: We're passing valid pointers and the C code handles null checks.
1083 let result = unsafe {
1084 ffi::miniscript_from_string(c_input.as_ptr(), context.into(), &raw mut node_ptr)
1085 };
1086
1087 if result.success {
1088 Ok(Self {
1089 ptr: node_ptr,
1090 context,
1091 })
1092 } else {
1093 let message = if result.error_message.is_null() {
1094 "unknown error".to_string()
1095 } else {
1096 // SAFETY: error_message is a valid C string if not null
1097 let msg = unsafe { CStr::from_ptr(result.error_message) }
1098 .to_string_lossy()
1099 .into_owned();
1100 unsafe { miniscript_free_string(result.error_message) };
1101 msg
1102 };
1103 Err(Error { message })
1104 }
1105 }
1106
1107 /// Convert the miniscript back to a string.
1108 #[must_use]
1109 pub fn to_string(&self) -> Option<String> {
1110 // SAFETY: self.ptr is valid while self exists
1111 let c_str = unsafe { miniscript_to_string(self.ptr) };
1112 if c_str.is_null() {
1113 return None;
1114 }
1115
1116 // SAFETY: c_str is a valid C string
1117 let result = unsafe { CStr::from_ptr(c_str) }
1118 .to_string_lossy()
1119 .into_owned();
1120 unsafe { miniscript_free_string(c_str) };
1121
1122 Some(result)
1123 }
1124
1125 /// Check if the miniscript is valid (type-checks correctly).
1126 #[must_use]
1127 pub fn is_valid(&self) -> bool {
1128 // SAFETY: self.ptr is valid while self exists
1129 unsafe { miniscript_is_valid(self.ptr) }
1130 }
1131
1132 /// Check if the miniscript is sane.
1133 ///
1134 /// This includes checks for:
1135 /// - No duplicate keys
1136 /// - No timelock mixing
1137 /// - Within resource limits
1138 #[must_use]
1139 pub fn is_sane(&self) -> bool {
1140 // SAFETY: self.ptr is valid while self exists
1141 unsafe { miniscript_is_sane(self.ptr) }
1142 }
1143
1144 /// Get the type properties of the miniscript.
1145 ///
1146 /// Returns a string like "Bdems" where each letter indicates a property.
1147 #[must_use]
1148 pub fn get_type(&self) -> Option<String> {
1149 // SAFETY: self.ptr is valid while self exists
1150 let c_str = unsafe { miniscript_get_type(self.ptr) };
1151 if c_str.is_null() {
1152 return None;
1153 }
1154
1155 // SAFETY: c_str is a valid C string
1156 let result = unsafe { CStr::from_ptr(c_str) }
1157 .to_string_lossy()
1158 .into_owned();
1159 unsafe { miniscript_free_string(c_str) };
1160
1161 Some(result)
1162 }
1163
1164 /// Get the maximum witness size for satisfying this miniscript.
1165 #[must_use]
1166 pub fn max_satisfaction_size(&self) -> Option<usize> {
1167 let mut size: usize = 0;
1168 // SAFETY: self.ptr is valid while self exists
1169 if unsafe { miniscript_max_satisfaction_size(self.ptr, &raw mut size) } {
1170 Some(size)
1171 } else {
1172 None
1173 }
1174 }
1175
1176 /// Get the context this miniscript was parsed with.
1177 #[must_use]
1178 pub const fn context(&self) -> Context {
1179 self.context
1180 }
1181
1182 /// Check if the miniscript is non-malleable.
1183 #[must_use]
1184 pub fn is_non_malleable(&self) -> bool {
1185 // SAFETY: self.ptr is valid while self exists
1186 unsafe { miniscript_is_non_malleable(self.ptr) }
1187 }
1188
1189 /// Check if the miniscript requires a signature to satisfy.
1190 #[must_use]
1191 pub fn needs_signature(&self) -> bool {
1192 // SAFETY: self.ptr is valid while self exists
1193 unsafe { miniscript_needs_signature(self.ptr) }
1194 }
1195
1196 /// Check if the miniscript has a timelock mix (mixing height and time locks).
1197 #[must_use]
1198 pub fn has_timelock_mix(&self) -> bool {
1199 // SAFETY: self.ptr is valid while self exists
1200 unsafe { miniscript_has_timelock_mix(self.ptr) }
1201 }
1202
1203 /// Check if the miniscript is valid at the top level.
1204 #[must_use]
1205 pub fn is_valid_top_level(&self) -> bool {
1206 // SAFETY: self.ptr is valid while self exists
1207 unsafe { miniscript_is_valid_top_level(self.ptr) }
1208 }
1209
1210 /// Check if the miniscript is within the ops limit.
1211 #[must_use]
1212 pub fn check_ops_limit(&self) -> bool {
1213 // SAFETY: self.ptr is valid while self exists
1214 unsafe { miniscript_check_ops_limit(self.ptr) }
1215 }
1216
1217 /// Check if the miniscript is within the stack size limit.
1218 #[must_use]
1219 pub fn check_stack_size(&self) -> bool {
1220 // SAFETY: self.ptr is valid while self exists
1221 unsafe { miniscript_check_stack_size(self.ptr) }
1222 }
1223
1224 /// Check if the miniscript has no duplicate keys.
1225 #[must_use]
1226 pub fn check_duplicate_key(&self) -> bool {
1227 // SAFETY: self.ptr is valid while self exists
1228 unsafe { miniscript_check_duplicate_key(self.ptr) }
1229 }
1230
1231 /// Get the number of ops in the miniscript.
1232 #[must_use]
1233 pub fn get_ops(&self) -> Option<u32> {
1234 let mut ops: u32 = 0;
1235 // SAFETY: self.ptr is valid while self exists
1236 if unsafe { miniscript_get_ops(self.ptr, &raw mut ops) } {
1237 Some(ops)
1238 } else {
1239 None
1240 }
1241 }
1242
1243 /// Get the maximum stack size needed to satisfy this miniscript.
1244 #[must_use]
1245 pub fn get_stack_size(&self) -> Option<u32> {
1246 let mut size: u32 = 0;
1247 // SAFETY: self.ptr is valid while self exists
1248 if unsafe { miniscript_get_stack_size(self.ptr, &raw mut size) } {
1249 Some(size)
1250 } else {
1251 None
1252 }
1253 }
1254
1255 /// Get the maximum execution stack size.
1256 #[must_use]
1257 pub fn get_exec_stack_size(&self) -> Option<u32> {
1258 let mut size: u32 = 0;
1259 // SAFETY: self.ptr is valid while self exists
1260 if unsafe { miniscript_get_exec_stack_size(self.ptr, &raw mut size) } {
1261 Some(size)
1262 } else {
1263 None
1264 }
1265 }
1266
1267 /// Get the script size.
1268 #[must_use]
1269 pub fn get_script_size(&self) -> Option<usize> {
1270 let mut size: usize = 0;
1271 // SAFETY: self.ptr is valid while self exists
1272 if unsafe { miniscript_get_script_size(self.ptr, &raw mut size) } {
1273 Some(size)
1274 } else {
1275 None
1276 }
1277 }
1278
1279 /// Check if the miniscript has valid satisfactions.
1280 #[must_use]
1281 pub fn valid_satisfactions(&self) -> bool {
1282 // SAFETY: self.ptr is valid while self exists
1283 unsafe { miniscript_valid_satisfactions(self.ptr) }
1284 }
1285
1286 /// Get the static ops count (for Tapscript).
1287 #[must_use]
1288 pub fn get_static_ops(&self) -> Option<u32> {
1289 let mut ops: u32 = 0;
1290 // SAFETY: self.ptr is valid while self exists
1291 if unsafe { miniscript_get_static_ops(self.ptr, &raw mut ops) } {
1292 Some(ops)
1293 } else {
1294 None
1295 }
1296 }
1297
1298 /// Convert the miniscript to raw script bytes.
1299 #[must_use]
1300 pub fn to_script_bytes(&self) -> Option<Vec<u8>> {
1301 let mut script_ptr: *mut u8 = ptr::null_mut();
1302 let mut script_len: usize = 0;
1303
1304 // SAFETY: self.ptr is valid while self exists
1305 if unsafe { miniscript_to_script(self.ptr, &raw mut script_ptr, &raw mut script_len) } {
1306 if script_ptr.is_null() {
1307 return None;
1308 }
1309 // SAFETY: script_ptr is valid and contains script_len bytes
1310 let script = unsafe { std::slice::from_raw_parts(script_ptr, script_len) }.to_vec();
1311 unsafe { miniscript_free_bytes(script_ptr) };
1312 Some(script)
1313 } else {
1314 None
1315 }
1316 }
1317
1318 /// Convert the miniscript to a [`bitcoin::ScriptBuf`].
1319 ///
1320 /// This returns the script as a proper Bitcoin script type from the `bitcoin` crate.
1321 #[must_use]
1322 pub fn to_script(&self) -> Option<ScriptBuf> {
1323 self.to_script_bytes().map(ScriptBuf::from_bytes)
1324 }
1325
1326 /// Parse a miniscript from raw script bytes.
1327 ///
1328 /// # Errors
1329 ///
1330 /// Returns an error if parsing fails.
1331 pub fn from_script_bytes(script: &[u8], context: Context) -> Result<Self, Error> {
1332 let mut node_ptr: *mut MiniscriptNode = ptr::null_mut();
1333
1334 // SAFETY: We're passing valid pointers and the C code handles null checks.
1335 let result = unsafe {
1336 miniscript_from_script(
1337 script.as_ptr(),
1338 script.len(),
1339 context.into(),
1340 &raw mut node_ptr,
1341 )
1342 };
1343
1344 if result.success {
1345 Ok(Self {
1346 ptr: node_ptr,
1347 context,
1348 })
1349 } else {
1350 let message = if result.error_message.is_null() {
1351 "unknown error".to_string()
1352 } else {
1353 // SAFETY: error_message is a valid C string if not null
1354 let msg = unsafe { CStr::from_ptr(result.error_message) }
1355 .to_string_lossy()
1356 .into_owned();
1357 unsafe { miniscript_free_string(result.error_message) };
1358 msg
1359 };
1360 Err(Error { message })
1361 }
1362 }
1363
1364 /// Produce a witness that satisfies this miniscript.
1365 ///
1366 /// # Arguments
1367 ///
1368 /// * `satisfier` - An implementation of the Satisfier trait that provides
1369 /// signatures, hash preimages, and timelock information.
1370 /// * `nonmalleable` - If true, only produce non-malleable satisfactions.
1371 ///
1372 /// # Returns
1373 ///
1374 /// A `SatisfyResult` containing the availability and witness stack.
1375 ///
1376 /// # Errors
1377 ///
1378 /// Returns an error if satisfaction fails.
1379 pub fn satisfy<S: Satisfier + 'static>(
1380 &self,
1381 satisfier: S,
1382 nonmalleable: bool,
1383 ) -> Result<SatisfyResult, Error> {
1384 // Box the satisfier so we can pass it through FFI
1385 let boxed: Box<dyn Satisfier> = Box::new(satisfier);
1386 let boxed_ptr = Box::into_raw(Box::new(boxed));
1387
1388 let callbacks = SatisfierCallbacks {
1389 rust_context: boxed_ptr.cast::<std::ffi::c_void>(),
1390 sign_callback: Some(sign_callback),
1391 check_after_callback: Some(check_after_callback),
1392 check_older_callback: Some(check_older_callback),
1393 sat_sha256_callback: Some(sat_sha256_callback),
1394 sat_ripemd160_callback: Some(sat_ripemd160_callback),
1395 sat_hash256_callback: Some(sat_hash256_callback),
1396 sat_hash160_callback: Some(sat_hash160_callback),
1397 };
1398
1399 // SAFETY: self.ptr is valid, callbacks is properly initialized
1400 let mut result =
1401 unsafe { miniscript_satisfy(self.ptr, &raw const callbacks, nonmalleable) };
1402
1403 // Clean up the boxed satisfier
1404 unsafe {
1405 let _ = Box::from_raw(boxed_ptr);
1406 }
1407
1408 // Check for errors
1409 if !result.error_message.is_null() {
1410 let msg = unsafe { CStr::from_ptr(result.error_message) }
1411 .to_string_lossy()
1412 .into_owned();
1413 unsafe { miniscript_satisfaction_result_free(&raw mut result) };
1414 return Err(Error { message: msg });
1415 }
1416
1417 // Convert the stack
1418 let mut stack = Vec::new();
1419 if !result.stack.is_null() && result.stack_count > 0 {
1420 for i in 0..result.stack_count {
1421 let elem_ptr = unsafe { *result.stack.add(i) };
1422 let elem_len = unsafe { *result.stack_sizes.add(i) };
1423
1424 if elem_ptr.is_null() || elem_len == 0 {
1425 stack.push(Vec::new());
1426 } else {
1427 let elem = unsafe { std::slice::from_raw_parts(elem_ptr, elem_len) }.to_vec();
1428 stack.push(elem);
1429 }
1430 }
1431 }
1432
1433 let availability = result.availability.into();
1434
1435 // Free the C result
1436 unsafe { miniscript_satisfaction_result_free(&raw mut result) };
1437
1438 Ok(SatisfyResult {
1439 availability,
1440 stack,
1441 })
1442 }
1443}
1444
1445impl Drop for Miniscript {
1446 fn drop(&mut self) {
1447 if !self.ptr.is_null() {
1448 // SAFETY: ptr was allocated by miniscript_from_string
1449 unsafe { miniscript_node_free(self.ptr) };
1450 }
1451 }
1452}
1453
1454impl fmt::Debug for Miniscript {
1455 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1456 f.debug_struct("Miniscript")
1457 .field("context", &self.context)
1458 .field("string", &self.to_string())
1459 .field("type", &self.get_type())
1460 .finish_non_exhaustive()
1461 }
1462}
1463
1464/// Get the library version string.
1465///
1466/// Returns the version of the underlying Bitcoin Core miniscript FFI wrapper.
1467///
1468/// # Example
1469///
1470/// ```rust,no_run
1471/// use miniscript_core_ffi::version;
1472///
1473/// println!("Library version: {}", version());
1474/// ```
1475#[must_use]
1476pub fn version() -> &'static str {
1477 // SAFETY: miniscript_version returns a static string
1478 unsafe {
1479 CStr::from_ptr(miniscript_version())
1480 .to_str()
1481 .unwrap_or("unknown")
1482 }
1483}
1484
1485#[cfg(test)]
1486mod tests {
1487 use super::*;
1488
1489 #[test]
1490 fn test_version() {
1491 let v = version();
1492 assert!(!v.is_empty());
1493 }
1494
1495 #[test]
1496 fn test_parse_simple() {
1497 let ms = Miniscript::from_str("pk(A)", Context::Wsh).expect("should parse");
1498 assert!(ms.is_valid());
1499 assert_eq!(ms.to_string(), Some("pk(A)".to_string()));
1500 }
1501
1502 #[test]
1503 fn test_parse_and_v() {
1504 let ms = Miniscript::from_str("and_v(v:pk(A),pk(B))", Context::Wsh).expect("should parse");
1505 assert!(ms.is_valid());
1506 }
1507
1508 #[test]
1509 fn test_invalid_miniscript() {
1510 let result = Miniscript::from_str("invalid", Context::Wsh);
1511 assert!(result.is_err());
1512 }
1513
1514 #[test]
1515 fn test_type_properties() {
1516 let ms = Miniscript::from_str("pk(A)", Context::Wsh).expect("should parse");
1517 let type_str = ms.get_type().expect("should have type");
1518 assert!(type_str.contains('B'));
1519 }
1520
1521 #[test]
1522 fn test_simple_satisfier() {
1523 let satisfier = SimpleSatisfier::new();
1524 assert!(satisfier.signatures.is_empty());
1525 assert!(satisfier.sha256_preimages.is_empty());
1526 }
1527}