Skip to main content

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;
40//! use std::sync::Arc;
41//!
42//! // Create a trivial Simplicity program
43//! let program = Context::with_context(|ctx| {
44//!     let construct = Arc::<ConstructNode>::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;
93#[cfg(feature = "human_encoding")]
94pub mod human_encoding;
95pub mod jet;
96mod merkle;
97pub mod node;
98#[cfg(feature = "elements")]
99pub mod policy;
100#[cfg(feature = "serde")]
101pub mod serializers;
102pub mod types;
103mod value;
104
105pub use bit_encoding::decode;
106pub use bit_encoding::encode;
107pub use bit_encoding::{
108    u2, BitCollector, BitIter, CloseError as BitIterCloseError, EarlyEndOfStreamError,
109};
110pub use bit_encoding::{write_to_vec, BitWriter};
111
112#[cfg(feature = "elements")]
113pub use crate::policy::{
114    sighash, Policy, Preimage32, Satisfier, SimplicityKey, ToXOnlyPubkey, Translator,
115};
116
117pub use crate::analysis::{Cost, NodeBounds};
118pub use crate::bit_machine::BitMachine;
119pub use crate::encode::{encode_natural, encode_value, encode_witness};
120pub use crate::merkle::{
121    amr::Amr,
122    cmr::Cmr,
123    ihr::{Ihr, Imr},
124    tmr::Tmr,
125    FailEntropy, HasCmr,
126};
127pub use crate::node::{CommitNode, ConstructNode, Hiding, RedeemNode};
128pub use crate::value::{Value, ValueRef, Word};
129pub use simplicity_sys as ffi;
130use std::fmt;
131
132/// Return the version of Simplicity leaves inside a tap tree.
133#[cfg(feature = "elements")]
134pub fn leaf_version() -> elements::taproot::LeafVersion {
135    elements::taproot::LeafVersion::from_u8(0xbe).expect("constant leaf version")
136}
137
138/// Error decoding a program from the bit encoding.
139#[non_exhaustive]
140#[derive(Debug)]
141pub enum DecodeError {
142    /// Decoder error
143    Decode(decode::Error),
144    /// A disconnect node was *not* populated at redeem time
145    DisconnectRedeemTime,
146    /// Type-checking error
147    Type(types::Error),
148}
149
150impl fmt::Display for DecodeError {
151    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
152        match self {
153            Self::Decode(ref e) => fmt::Display::fmt(e, f),
154            Self::DisconnectRedeemTime => {
155                f.write_str("disconnect node had one child (redeem time); must have two")
156            }
157            Self::Type(ref e) => fmt::Display::fmt(e, f),
158        }
159    }
160}
161
162impl std::error::Error for DecodeError {
163    fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
164        match self {
165            Self::Decode(ref e) => Some(e),
166            Self::DisconnectRedeemTime => None,
167            Self::Type(ref e) => Some(e),
168        }
169    }
170}
171
172/// Error parsing a program or witness (decoding it from a string encoding).
173#[non_exhaustive]
174#[derive(Debug)]
175pub enum ParseError {
176    /// Bit-encoding error.
177    Decode(DecodeError),
178    /// Base64 decoding error
179    #[cfg(feature = "base64")]
180    Base64(base64::DecodeError),
181    /// Hex decoding error
182    Hex(hex::error::HexToBytesError),
183}
184
185impl fmt::Display for ParseError {
186    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
187        match self {
188            Self::Decode(ref e) => e.fmt(f),
189            #[cfg(feature = "base64")]
190            Self::Base64(ref e) => e.fmt(f),
191            Self::Hex(ref e) => e.fmt(f),
192        }
193    }
194}
195
196impl std::error::Error for ParseError {
197    fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
198        match self {
199            Self::Decode(ref e) => Some(e),
200            #[cfg(feature = "base64")]
201            Self::Base64(ref e) => Some(e),
202            Self::Hex(ref e) => Some(e),
203        }
204    }
205}
206
207/// Error finalizing a program (i.e. typechecking and pruning).
208#[non_exhaustive]
209#[derive(Debug)]
210pub enum FinalizeError {
211    /// A disconnect node was *not* populated at redeem time
212    DisconnectRedeemTime,
213    // Execution error
214    Execution(bit_machine::ExecutionError),
215    /// Type-checking error
216    Type(types::Error),
217}
218
219impl fmt::Display for FinalizeError {
220    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
221        match self {
222            Self::DisconnectRedeemTime => {
223                f.write_str("disconnect node had one child (redeem time); must have two")
224            }
225            Self::Execution(ref e) => fmt::Display::fmt(e, f),
226            Self::Type(ref e) => fmt::Display::fmt(e, f),
227        }
228    }
229}
230
231impl std::error::Error for FinalizeError {
232    fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
233        match self {
234            Self::DisconnectRedeemTime => None,
235            Self::Execution(ref e) => Some(e),
236            Self::Type(ref e) => Some(e),
237        }
238    }
239}
240
241/// Error type for simplicity
242// FIXME we should collapse this error to its single variant; but need to update
243// autogenerated code to do so, so we leave it be for now.
244#[non_exhaustive]
245#[derive(Debug)]
246pub enum Error {
247    /// Tried to parse a jet but the name wasn't recognized
248    InvalidJetName(String),
249}
250
251impl fmt::Display for Error {
252    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
253        match self {
254            Error::InvalidJetName(s) => write!(f, "unknown jet `{}`", s),
255        }
256    }
257}
258
259impl std::error::Error for Error {
260    fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
261        match *self {
262            Error::InvalidJetName(..) => None,
263        }
264    }
265}