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}