simplicity/
lib.rs

1// SPDX-License-Identifier: CC0-1.0
2
3//! # rust-simplicity
4//!
5//! This is the official Rust library of the [Simplicity Language](https://simplicity-lang.org/).
6//!
7//! Simplicity is a low-level, typed functional language designed to be a drop-in alternative
8//! for Bitcoin's [Tapscript](https://github.com/bitcoin/bips/blob/master/bip-0342.mediawiki).
9//! It offers static resource bounds and has a formal specification (in Rocq) which allows the
10//! creation of machine-checkable proofs of program behavior.
11//!
12//! It is currently deployed on Blockstream's Liquid, which is a sidechain resembling Bitcoin
13//! in many ways; but which differs in many Script-relevant ways (e.g. supporting multiple assets and using Confidential Transactions). We expect by the end of 2025 to directly
14//! support Bitcoin, so that Simplicity can be used on a custom regtest chain.
15//!
16//! Simplicity is a very low-level language. If you are simply looking to develop with the
17//! language, you may wish to use [SimplicityHL](https://github.com/BlockstreamResearch/simplicityhl) instead.
18//!
19//! # Nodes
20//!
21//! When creating and manipulating Simplicity programs, there are three node types that you
22//! may use when creating programs.
23//!
24//! * [`ConstructNode`] represents a program under construction. Type inference is done on
25//!   the program as it is being built, which must be done within a scope containing a
26//!   [`types::Context`].
27//! * [`CommitNode`] represents a complete program for which witness and disconnect nodes
28//!   are unpopulated. This can be used to produce a CMR, which in turn is used to generate
29//!   an address for use on-chain.
30//! * [`RedeemNode`] represents a program as it is embedded on-chain. Unused nodes must be
31//!   pruned, and any disconnect or witness nodes which remain must appear on-chain. This
32//!   is the only node type which has a canonical encoding.
33//!
34//! # Quick Start
35//!
36//! ```rust
37//! use simplicity::node::CoreConstructible;
38//! use simplicity::types::Context;
39//! use simplicity::{ConstructNode, jet::Core};
40//! use std::sync::Arc;
41//!
42//! // Create a trivial Simplicity program
43//! let program = Context::with_context(|ctx| {
44//!     let construct = Arc::<ConstructNode<Core>>::unit(&ctx);
45//!     construct.finalize_types().unwrap()
46//! });
47//!
48//! // Encode the program to bytes
49//! let encoded: Vec<u8> = simplicity::write_to_vec(|w| {
50//!     program.encode_without_witness(w)
51//! });
52//! ```
53
54#![cfg_attr(bench, feature(test))]
55#![allow(
56    // we use `bool` to represent bits and frequentely assert_eq against them
57    clippy::bool_assert_comparison,
58    // we use () as the environment for Core (FIXME we should probabl use a newtype)
59    clippy::let_unit_value,
60    // We write map(Arc::clone) to signify that a cheap Arc is being cloned
61    clippy::map_clone
62)]
63
64#[cfg(feature = "bitcoin")]
65pub extern crate bitcoin;
66#[cfg(feature = "elements")]
67pub extern crate elements;
68#[cfg(feature = "serde")]
69pub extern crate serde;
70
71/// Re-export of base64 crate
72#[cfg(feature = "base64")]
73pub use bitcoin::base64;
74/// Re-export of byteorder crate
75pub extern crate byteorder;
76/// Re-export of ghost_cell crate
77pub extern crate ghost_cell;
78/// Re-export of hashes crate
79pub extern crate hashes;
80/// Re-export of hex crate
81pub extern crate hex;
82
83#[cfg(bench)]
84extern crate test;
85
86#[macro_use]
87mod macros;
88
89mod analysis;
90mod bit_encoding;
91pub mod bit_machine;
92pub mod dag;
93pub mod human_encoding;
94pub mod jet;
95mod merkle;
96pub mod node;
97#[cfg(feature = "elements")]
98pub mod policy;
99pub mod types;
100mod value;
101
102pub use bit_encoding::decode;
103pub use bit_encoding::encode;
104pub use bit_encoding::{
105    u2, BitCollector, BitIter, CloseError as BitIterCloseError, EarlyEndOfStreamError,
106};
107pub use bit_encoding::{write_to_vec, BitWriter};
108
109#[cfg(feature = "elements")]
110pub use crate::policy::{
111    sighash, Policy, Preimage32, Satisfier, SimplicityKey, ToXOnlyPubkey, Translator,
112};
113
114pub use crate::analysis::{Cost, NodeBounds};
115pub use crate::bit_machine::BitMachine;
116pub use crate::encode::{encode_natural, encode_value, encode_witness};
117pub use crate::merkle::{
118    amr::Amr,
119    cmr::Cmr,
120    ihr::{Ihr, Imr},
121    tmr::Tmr,
122    FailEntropy, HasCmr,
123};
124pub use crate::node::{CommitNode, ConstructNode, Hiding, RedeemNode};
125pub use crate::value::{Value, ValueRef, Word};
126pub use simplicity_sys as ffi;
127use std::fmt;
128
129/// Return the version of Simplicity leaves inside a tap tree.
130#[cfg(feature = "elements")]
131pub fn leaf_version() -> elements::taproot::LeafVersion {
132    elements::taproot::LeafVersion::from_u8(0xbe).expect("constant leaf version")
133}
134
135/// Error decoding a program from the bit encoding.
136#[non_exhaustive]
137#[derive(Debug)]
138pub enum DecodeError {
139    /// Decoder error
140    Decode(decode::Error),
141    /// A disconnect node was *not* populated at redeem time
142    DisconnectRedeemTime,
143    /// Type-checking error
144    Type(types::Error),
145}
146
147impl fmt::Display for DecodeError {
148    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
149        match self {
150            Self::Decode(ref e) => fmt::Display::fmt(e, f),
151            Self::DisconnectRedeemTime => {
152                f.write_str("disconnect node had one child (redeem time); must have two")
153            }
154            Self::Type(ref e) => fmt::Display::fmt(e, f),
155        }
156    }
157}
158
159impl std::error::Error for DecodeError {
160    fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
161        match self {
162            Self::Decode(ref e) => Some(e),
163            Self::DisconnectRedeemTime => None,
164            Self::Type(ref e) => Some(e),
165        }
166    }
167}
168
169/// Error parsing a program or witness (decoding it from a string encoding).
170#[non_exhaustive]
171#[derive(Debug)]
172pub enum ParseError {
173    /// Bit-encoding error.
174    Decode(DecodeError),
175    /// Base64 decoding error
176    #[cfg(feature = "base64")]
177    Base64(base64::DecodeError),
178    /// Hex decoding error
179    Hex(hex::error::HexToBytesError),
180}
181
182impl fmt::Display for ParseError {
183    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
184        match self {
185            Self::Decode(ref e) => e.fmt(f),
186            #[cfg(feature = "base64")]
187            Self::Base64(ref e) => e.fmt(f),
188            Self::Hex(ref e) => e.fmt(f),
189        }
190    }
191}
192
193impl std::error::Error for ParseError {
194    fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
195        match self {
196            Self::Decode(ref e) => Some(e),
197            #[cfg(feature = "base64")]
198            Self::Base64(ref e) => Some(e),
199            Self::Hex(ref e) => Some(e),
200        }
201    }
202}
203
204/// Error finalizing a program (i.e. typechecking and pruning).
205#[non_exhaustive]
206#[derive(Debug)]
207pub enum FinalizeError {
208    /// A disconnect node was *not* populated at redeem time
209    DisconnectRedeemTime,
210    // Execution error
211    Execution(bit_machine::ExecutionError),
212    /// Type-checking error
213    Type(types::Error),
214}
215
216impl fmt::Display for FinalizeError {
217    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
218        match self {
219            Self::DisconnectRedeemTime => {
220                f.write_str("disconnect node had one child (redeem time); must have two")
221            }
222            Self::Execution(ref e) => fmt::Display::fmt(e, f),
223            Self::Type(ref e) => fmt::Display::fmt(e, f),
224        }
225    }
226}
227
228impl std::error::Error for FinalizeError {
229    fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
230        match self {
231            Self::DisconnectRedeemTime => None,
232            Self::Execution(ref e) => Some(e),
233            Self::Type(ref e) => Some(e),
234        }
235    }
236}
237
238/// Error type for simplicity
239// FIXME we should collapse this error to its single variant; but need to update
240// autogenerated code to do so, so we leave it be for now.
241#[non_exhaustive]
242#[derive(Debug)]
243pub enum Error {
244    /// Tried to parse a jet but the name wasn't recognized
245    InvalidJetName(String),
246}
247
248impl fmt::Display for Error {
249    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
250        match self {
251            Error::InvalidJetName(s) => write!(f, "unknown jet `{}`", s),
252        }
253    }
254}
255
256impl std::error::Error for Error {
257    fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
258        match *self {
259            Error::InvalidJetName(..) => None,
260        }
261    }
262}