Skip to main content

simplicity/jet/
mod.rs

1// SPDX-License-Identifier: CC0-1.0
2
3//! # Simplicity jets
4//!
5//! Jets are special nodes that read a value,
6//! process it internally, and write an output value.
7//! This evaluation happens in a black-box manner:
8//! In terms of the Bit Machine, it is a one-step process.
9//!
10//! In practice, jets call foreign C code that is equivalent to some Simplicity DAG.
11//! This speeds up evaluation tremendously.
12//! Equivalence of C and Simplicity is proved using the _Verified Software Toolchain_.
13//! Programs are also smaller in size because jets replace large, equivalent Simplicity DAGs.
14
15#[cfg(feature = "bitcoin")]
16pub mod bitcoin;
17pub mod core;
18#[cfg(feature = "elements")]
19pub mod elements;
20mod init;
21pub mod type_name;
22
23pub use self::core::CoreEnv;
24#[cfg(feature = "bitcoin")]
25pub use crate::jet::bitcoin::BitcoinEnv;
26#[cfg(feature = "elements")]
27pub use elements::ElementsTxEnv;
28
29#[cfg(feature = "bitcoin")]
30pub use init::bitcoin::Bitcoin;
31pub use init::core::Core;
32#[cfg(feature = "elements")]
33pub use init::elements::Elements;
34use simplicity_sys::c_jets::frame_ffi::CFrameItem;
35
36use crate::analysis::Cost;
37use crate::decode;
38use crate::jet::type_name::TypeName;
39use crate::merkle::cmr::Cmr;
40use crate::{BitIter, BitWriter};
41use std::any::{Any, TypeId};
42use std::cmp::Ordering;
43use std::hash::{Hash, Hasher};
44use std::io::Write;
45
46/// Generic error that a jet failed during its execution.
47///
48/// Failure could be due to a failed assertion, an illegal input, etc.
49#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Debug, Hash)]
50pub struct JetFailed;
51
52impl std::fmt::Display for JetFailed {
53    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
54        f.write_str("Jet failed during execution")
55    }
56}
57
58impl std::error::Error for JetFailed {}
59
60/// An environment for jets to read.
61pub trait JetEnvironment {
62    /// The type of jet that this environment supports.
63    type Jet: Jet;
64
65    /// CJetEnvironment to interact with C FFI.
66    type CJetEnvironment;
67
68    /// Obtains a C FFI compatible environment for the jet.
69    fn c_jet_env(&self) -> &Self::CJetEnvironment;
70
71    /// Obtain the FFI C pointer for the jet.
72    fn c_jet_ptr(
73        jet: &Self::Jet,
74    ) -> fn(&mut CFrameItem, CFrameItem, &Self::CJetEnvironment) -> bool;
75}
76
77/// Family of jets that share an encoding scheme and execution environment.
78///
79/// Jets are single nodes that read an input,
80/// process it internally using foreign C code _(black box)_,
81/// and produce an output.
82/// Jets may read values from their _environment_.
83///
84/// Jets are **always** leaves in a Simplicity DAG.
85pub trait Jet: DynJet + std::fmt::Debug + std::fmt::Display + Send + Sync + 'static {
86    /// Return the CMR of the jet.
87    fn cmr(&self) -> Cmr;
88
89    /// Return the source type of the jet.
90    fn source_ty(&self) -> TypeName;
91
92    /// Return the target type of the jet.
93    fn target_ty(&self) -> TypeName;
94
95    /// Encode the jet to bits.
96    fn encode(&self, w: &mut BitWriter<&mut dyn Write>) -> std::io::Result<usize>;
97
98    /// Decode a jet from bits.
99    fn decode<I: Iterator<Item = u8>>(bits: &mut BitIter<I>) -> Result<Self, decode::Error>
100    where
101        Self: Sized;
102
103    /// Return the cost of the jet.
104    fn cost(&self) -> Cost;
105
106    /// Parse a jet from a string.
107    fn parse(s: &str) -> Result<Self, crate::Error>
108    where
109        Self: Sized;
110}
111
112/// Lets `Box<dyn Jet>` work with `Clone`, `Eq`, `Ord`, and `Hash`.
113pub trait DynJet {
114    fn as_any(&self) -> &dyn Any;
115    fn dyn_clone(&self) -> Box<dyn Jet>;
116    fn dyn_eq(&self, other: &dyn Jet) -> bool;
117    fn dyn_cmp(&self, other: &dyn Jet) -> Ordering;
118    fn dyn_hash(&self, state: &mut dyn Hasher);
119}
120
121impl<T: Jet + Clone + Ord + Hash> DynJet for T {
122    fn as_any(&self) -> &dyn Any {
123        self
124    }
125
126    fn dyn_clone(&self) -> Box<dyn Jet> {
127        Box::new(self.clone())
128    }
129
130    fn dyn_eq(&self, other: &dyn Jet) -> bool {
131        other
132            .as_any()
133            .downcast_ref::<Self>()
134            .is_some_and(|other| self == other)
135    }
136
137    fn dyn_cmp(&self, other: &dyn Jet) -> Ordering {
138        match other.as_any().downcast_ref::<Self>() {
139            Some(other) => self.cmp(other),
140            None => TypeId::of::<Self>().cmp(&other.as_any().type_id()),
141        }
142    }
143
144    fn dyn_hash(&self, mut state: &mut dyn Hasher) {
145        self.hash(&mut state)
146    }
147}
148
149impl Clone for Box<dyn Jet> {
150    fn clone(&self) -> Self {
151        (**self).dyn_clone()
152    }
153}
154
155impl PartialEq for Box<dyn Jet> {
156    fn eq(&self, other: &Self) -> bool {
157        (**self).dyn_eq(&**other)
158    }
159}
160
161impl Eq for Box<dyn Jet> {}
162
163impl PartialOrd for Box<dyn Jet> {
164    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
165        Some(self.cmp(other))
166    }
167}
168
169impl Ord for Box<dyn Jet> {
170    fn cmp(&self, other: &Self) -> Ordering {
171        (**self).dyn_cmp(&**other)
172    }
173}
174
175impl Hash for Box<dyn Jet> {
176    fn hash<H: Hasher>(&self, state: &mut H) {
177        (**self).dyn_hash(state)
178    }
179}
180
181#[cfg(test)]
182mod tests {
183    use crate::jet::{Core, CoreEnv};
184    use crate::node::{ConstructNode, CoreConstructible, JetConstructible};
185    use crate::types;
186    use crate::value::Word;
187    use crate::{BitMachine, Value};
188    use std::sync::Arc;
189
190    #[test]
191    fn test_ffi_jet() {
192        types::Context::with_context(|ctx| {
193            let two_words = Arc::<ConstructNode>::comp(
194                &Arc::<ConstructNode>::pair(
195                    &Arc::<ConstructNode>::const_word(&ctx, Word::u32(2)),
196                    &Arc::<ConstructNode>::const_word(&ctx, Word::u32(16)),
197                )
198                .unwrap(),
199                &Arc::<ConstructNode>::jet(&ctx, &Core::Add32),
200            )
201            .unwrap();
202            assert_eq!(
203                BitMachine::test_exec(two_words, &CoreEnv::new()).expect("executing"),
204                Value::product(
205                    Value::u1(0),       // carry bit
206                    Value::u32(2 + 16), // result
207                ),
208            );
209        });
210    }
211
212    #[test]
213    fn test_simple() {
214        types::Context::with_context(|ctx| {
215            let two_words = Arc::<ConstructNode>::pair(
216                &Arc::<ConstructNode>::const_word(&ctx, Word::u32(2)),
217                &Arc::<ConstructNode>::const_word(&ctx, Word::u16(16)),
218            )
219            .unwrap();
220            assert_eq!(
221                BitMachine::test_exec(two_words, &CoreEnv::new()).expect("executing"),
222                Value::product(Value::u32(2), Value::u16(16)),
223            );
224        });
225    }
226}