simplicity/node/
redeem.rs

1// SPDX-License-Identifier: CC0-1.0
2
3use crate::analysis::NodeBounds;
4use crate::bit_machine::{ExecutionError, PruneTracker, SetTracker};
5use crate::dag::{DagLike, InternalSharing, MaxSharing, PostOrderIterItem};
6use crate::jet::Jet;
7use crate::types::{self, arrow::FinalArrow};
8use crate::{encode, BitMachine};
9use crate::{Amr, BitIter, BitWriter, Cmr, DecodeError, Ihr, Imr, Value};
10
11use super::{
12    Commit, CommitData, CommitNode, Construct, ConstructData, ConstructNode, Constructible,
13    Converter, Hide, Inner, Marker, NoDisconnect, NoWitness, Node,
14};
15
16use std::collections::HashSet;
17use std::io;
18use std::marker::PhantomData;
19use std::sync::Arc;
20
21#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Debug, Hash)]
22pub struct Redeem<J> {
23    /// Makes the type non-constructible.
24    never: std::convert::Infallible,
25    /// Required by Rust.
26    phantom: std::marker::PhantomData<J>,
27}
28
29impl<J: Jet> Marker for Redeem<J> {
30    type CachedData = Arc<RedeemData<J>>;
31    type Witness = Value;
32    type Disconnect = Arc<RedeemNode<J>>;
33    type SharingId = Ihr;
34    type Jet = J;
35
36    fn compute_sharing_id(_: Cmr, cached_data: &Arc<RedeemData<J>>) -> Option<Ihr> {
37        Some(cached_data.ihr)
38    }
39}
40
41pub type RedeemNode<J> = Node<Redeem<J>>;
42
43#[derive(Clone, Debug)]
44pub struct RedeemData<J> {
45    amr: Amr,
46    imr: Imr,
47    ihr: Ihr,
48    arrow: FinalArrow,
49    bounds: NodeBounds,
50    /// This isn't really necessary, but it helps type inference if every
51    /// struct has a \<J\> parameter, since it forces the choice of jets to
52    /// be consistent without the user needing to specify it too many times.
53    phantom: PhantomData<J>,
54}
55
56impl<J> PartialEq for RedeemData<J> {
57    fn eq(&self, other: &Self) -> bool {
58        self.ihr == other.ihr
59    }
60}
61impl<J> Eq for RedeemData<J> {}
62
63impl<J> std::hash::Hash for RedeemData<J> {
64    fn hash<H: std::hash::Hasher>(&self, hasher: &mut H) {
65        self.ihr.hash(hasher)
66    }
67}
68
69impl<J: Jet> RedeemData<J> {
70    pub fn new(arrow: FinalArrow, inner: Inner<&Arc<Self>, J, &Arc<Self>, Value>) -> Self {
71        let (amr, imr, bounds) = match inner {
72            Inner::Iden => (
73                Amr::iden(&arrow),
74                Imr::iden(),
75                NodeBounds::iden(arrow.source.bit_width()),
76            ),
77            Inner::Unit => (Amr::unit(&arrow), Imr::unit(), NodeBounds::unit()),
78            Inner::InjL(child) => (
79                Amr::injl(&arrow, child.amr),
80                Imr::injl(child.imr),
81                NodeBounds::injl(child.bounds),
82            ),
83            Inner::InjR(child) => (
84                Amr::injr(&arrow, child.amr),
85                Imr::injr(child.imr),
86                NodeBounds::injr(child.bounds),
87            ),
88            Inner::Take(child) => (
89                Amr::take(&arrow, child.amr),
90                Imr::take(child.imr),
91                NodeBounds::take(child.bounds),
92            ),
93            Inner::Drop(child) => (
94                Amr::drop(&arrow, child.amr),
95                Imr::drop(child.imr),
96                NodeBounds::drop(child.bounds),
97            ),
98            Inner::Comp(left, right) => (
99                Amr::comp(&arrow, &left.arrow, left.amr, right.amr),
100                Imr::comp(left.imr, right.imr),
101                NodeBounds::comp(left.bounds, right.bounds, left.arrow.target.bit_width()),
102            ),
103            Inner::Case(left, right) => (
104                Amr::case(&arrow, left.amr, right.amr),
105                Imr::case(left.imr, right.imr),
106                NodeBounds::case(left.bounds, right.bounds),
107            ),
108            Inner::AssertL(left, r_cmr) => (
109                Amr::assertl(&arrow, left.amr, r_cmr.into()),
110                Imr::case(left.imr, r_cmr.into()),
111                NodeBounds::assertl(left.bounds),
112            ),
113            Inner::AssertR(l_cmr, right) => (
114                Amr::assertr(&arrow, l_cmr.into(), right.amr),
115                Imr::case(l_cmr.into(), right.imr),
116                NodeBounds::assertr(right.bounds),
117            ),
118            Inner::Pair(left, right) => (
119                Amr::pair(&arrow, &left.arrow, &right.arrow, left.amr, right.amr),
120                Imr::pair(left.imr, right.imr),
121                NodeBounds::pair(left.bounds, right.bounds),
122            ),
123            Inner::Disconnect(left, right) => (
124                Amr::disconnect(&arrow, &right.arrow, left.amr, right.amr),
125                Imr::disconnect(left.imr, right.imr),
126                NodeBounds::disconnect(
127                    left.bounds,
128                    right.bounds,
129                    left.arrow.target.bit_width() - right.arrow.source.bit_width(),
130                    left.arrow.source.bit_width(),
131                    left.arrow.target.bit_width(),
132                ),
133            ),
134            Inner::Witness(ref value) => (
135                Amr::witness(&arrow, value),
136                Imr::witness(&arrow, value),
137                NodeBounds::witness(arrow.target.bit_width()),
138            ),
139            Inner::Fail(entropy) => (Amr::fail(entropy), Imr::fail(entropy), NodeBounds::fail()),
140            Inner::Jet(jet) => (Amr::jet(jet), Imr::jet(jet), NodeBounds::jet(jet)),
141            Inner::Word(ref val) => (
142                Amr::const_word(val),
143                Imr::const_word(val),
144                NodeBounds::const_word(val),
145            ),
146        };
147
148        RedeemData {
149            amr,
150            imr,
151            ihr: Ihr::from_imr(imr, &arrow),
152            arrow,
153            bounds,
154            phantom: PhantomData,
155        }
156    }
157}
158
159impl<J: Jet> RedeemNode<J> {
160    /// Accessor for the node's AMR
161    pub fn amr(&self) -> Amr {
162        self.data.amr
163    }
164
165    /// Accessor for the node's IHR
166    pub fn ihr(&self) -> Ihr {
167        self.data.ihr
168    }
169
170    /// Accessor for the node's type arrow
171    pub fn arrow(&self) -> &FinalArrow {
172        &self.data.arrow
173    }
174
175    /// Accessor for the node's bit machine bounds
176    pub fn bounds(&self) -> NodeBounds {
177        self.data.bounds
178    }
179
180    /// Convert a [`RedeemNode`] back to a [`CommitNode`] by forgetting witnesses
181    /// and cached data.
182    pub fn unfinalize(&self) -> Result<Arc<CommitNode<J>>, types::Error> {
183        struct Unfinalizer<J>(PhantomData<J>);
184
185        impl<J: Jet> Converter<Redeem<J>, Commit<J>> for Unfinalizer<J> {
186            type Error = types::Error;
187            fn convert_witness(
188                &mut self,
189                _: &PostOrderIterItem<&RedeemNode<J>>,
190                _: &Value,
191            ) -> Result<NoWitness, Self::Error> {
192                Ok(NoWitness)
193            }
194
195            fn convert_disconnect(
196                &mut self,
197                _: &PostOrderIterItem<&RedeemNode<J>>,
198                _: Option<&Arc<CommitNode<J>>>,
199                _: &Arc<RedeemNode<J>>,
200            ) -> Result<NoDisconnect, Self::Error> {
201                Ok(NoDisconnect)
202            }
203
204            fn convert_data(
205                &mut self,
206                data: &PostOrderIterItem<&RedeemNode<J>>,
207                inner: Inner<&Arc<CommitNode<J>>, J, &NoDisconnect, &NoWitness>,
208            ) -> Result<Arc<CommitData<J>>, Self::Error> {
209                let converted_data = inner.map(|node| node.cached_data());
210                Ok(Arc::new(CommitData::from_final(
211                    data.node.data.arrow.shallow_clone(),
212                    converted_data,
213                )))
214            }
215        }
216
217        self.convert::<MaxSharing<Redeem<J>>, _, _>(&mut Unfinalizer(PhantomData))
218    }
219
220    /// Convert a [`RedeemNode`] back into a [`ConstructNode`]
221    /// by loosening the finalized types, witness data and disconnected branches.
222    pub fn to_construct_node<'brand>(
223        &self,
224        inference_context: &types::Context<'brand>,
225    ) -> Arc<ConstructNode<'brand, J>> {
226        struct ToConstruct<'a, 'brand, J> {
227            inference_context: &'a types::Context<'brand>,
228            phantom: PhantomData<J>,
229        }
230
231        impl<'brand, J: Jet> Converter<Redeem<J>, Construct<'brand, J>> for ToConstruct<'_, 'brand, J> {
232            type Error = ();
233
234            fn convert_witness(
235                &mut self,
236                _: &PostOrderIterItem<&Node<Redeem<J>>>,
237                witness: &Value,
238            ) -> Result<Option<Value>, Self::Error> {
239                Ok(Some(witness.clone()))
240            }
241
242            fn convert_disconnect(
243                &mut self,
244                _: &PostOrderIterItem<&Node<Redeem<J>>>,
245                right: Option<&Arc<Node<Construct<'brand, J>>>>,
246                _: &Arc<RedeemNode<J>>,
247            ) -> Result<Option<Arc<Node<Construct<'brand, J>>>>, Self::Error> {
248                Ok(right.cloned())
249            }
250
251            fn convert_data(
252                &mut self,
253                _: &PostOrderIterItem<&Node<Redeem<J>>>,
254                inner: Inner<
255                    &Arc<Node<Construct<'brand, J>>>,
256                    J,
257                    &Option<Arc<ConstructNode<'brand, J>>>,
258                    &Option<Value>,
259                >,
260            ) -> Result<ConstructData<'brand, J>, Self::Error> {
261                let inner = inner
262                    .map(|node| node.cached_data())
263                    .map_witness(|maybe_value| maybe_value.clone());
264                Ok(ConstructData::from_inner(self.inference_context, inner)
265                    .expect("types were already finalized"))
266            }
267        }
268
269        self.convert::<InternalSharing, _, _>(&mut ToConstruct {
270            inference_context,
271            phantom: PhantomData,
272        })
273        .unwrap()
274    }
275
276    /// Prune the redeem program for the given transaction environment.
277    ///
278    /// Pruning works as follows:
279    /// 1) Run the redeem program on the Bit Machine.
280    /// 2) Mark all (un)used case branches using the IHR of the case node.
281    /// 3) Rebuild the program and omit unused branches.
282    ///
283    /// The pruning result depends on the witness data (which is already part of the redeem program)
284    /// and on the transaction environment. These two inputs determine which case branches are
285    /// used and which are not. Pruning must be done for each transaction environment separately,
286    /// starting from the same original, unpruned program. Pruning is a lossy process, so pruning
287    /// an already pruned program is not sound.
288    ///
289    /// Pruning fails if the original, unpruned program fails to run on the Bit Machine (step 1).
290    /// In this case, the witness data needs to be revised.
291    /// The other pruning steps (2 & 3) never fail.
292    pub fn prune(&self, env: &J::Environment) -> Result<Arc<RedeemNode<J>>, ExecutionError> {
293        self.prune_with_tracker(env, &mut SetTracker::default())
294    }
295
296    /// Prune the redeem program, as in [`Self::prune`], but with a custom tracker which
297    /// can introspect or control pruning.
298    ///
299    /// See [`crate::bit_machine::StderrTracker`] as an example which outputs the IHR of
300    /// each case combinator that we prune a child of.
301    pub fn prune_with_tracker<T: PruneTracker<J>>(
302        &self,
303        env: &J::Environment,
304        tracker: &mut T,
305    ) -> Result<Arc<RedeemNode<J>>, ExecutionError> {
306        struct Pruner<'brand, 't, J, T> {
307            inference_context: types::Context<'brand>,
308            tracker: &'t mut T,
309            phantom: PhantomData<J>,
310        }
311
312        impl<'brand, 't, J, T> Converter<Redeem<J>, Construct<'brand, J>> for Pruner<'brand, 't, J, T>
313        where
314            J: Jet,
315            T: PruneTracker<J>,
316        {
317            type Error = std::convert::Infallible;
318
319            fn convert_witness(
320                &mut self,
321                _: &PostOrderIterItem<&RedeemNode<J>>,
322                witness: &Value,
323            ) -> Result<Option<Value>, Self::Error> {
324                // The pruned type is not finalized at this point,
325                // so we cannot prune the witness value.
326                Ok(Some(witness.shallow_clone()))
327            }
328
329            fn convert_disconnect(
330                &mut self,
331                _: &PostOrderIterItem<&RedeemNode<J>>,
332                right: Option<&Arc<ConstructNode<'brand, J>>>,
333                _: &Arc<RedeemNode<J>>,
334            ) -> Result<Option<Arc<ConstructNode<'brand, J>>>, Self::Error> {
335                debug_assert!(
336                    right.is_some(),
337                    "disconnected branch should exist in unpruned redeem program"
338                );
339                Ok(right.map(Arc::clone))
340            }
341
342            fn prune_case(
343                &mut self,
344                data: &PostOrderIterItem<&RedeemNode<J>>,
345                _left: &Arc<ConstructNode<J>>,
346                _right: &Arc<ConstructNode<J>>,
347            ) -> Result<Hide, Self::Error> {
348                // The IHR of the pruned program may change,
349                // but the Converter trait gives us access to the unpruned node (`data`).
350                // The Bit Machine tracked (un)used case branches based on the unpruned IHR.
351                match (
352                    self.tracker.contains_left(data.node.ihr()),
353                    self.tracker.contains_right(data.node.ihr()),
354                ) {
355                    (true, true) => Ok(Hide::Neither),
356                    (false, true) => Ok(Hide::Left),
357                    (true, false) => Ok(Hide::Right),
358                    (false, false) => Ok(Hide::Neither), // case nodes that were never executed will be pruned out by their ancestors
359                }
360            }
361
362            fn convert_data(
363                &mut self,
364                _: &PostOrderIterItem<&RedeemNode<J>>,
365                inner: Inner<
366                    &Arc<ConstructNode<'brand, J>>,
367                    J,
368                    &Option<Arc<ConstructNode<'brand, J>>>,
369                    &Option<Value>,
370                >,
371            ) -> Result<ConstructData<'brand, J>, Self::Error> {
372                let converted_inner = inner
373                    .map(|node| node.cached_data())
374                    .map_witness(Option::<Value>::clone);
375                let retyped = ConstructData::from_inner(&self.inference_context, converted_inner)
376                    .expect("pruned types should check out if unpruned types check out");
377                Ok(retyped)
378            }
379        }
380
381        struct Finalizer<J>(PhantomData<J>);
382
383        impl<'brand, J: Jet> Converter<Construct<'brand, J>, Redeem<J>> for Finalizer<J> {
384            type Error = std::convert::Infallible;
385
386            fn convert_witness(
387                &mut self,
388                data: &PostOrderIterItem<&ConstructNode<J>>,
389                witness: &Option<Value>,
390            ) -> Result<Value, Self::Error> {
391                let pruned_target_ty = data
392                    .node
393                    .arrow()
394                    .target
395                    .finalize()
396                    .expect("pruned types should check out if unpruned types check out");
397                let pruned_witness = witness
398                    .as_ref()
399                    .expect("witness node that originally stems from redeem program should be populated")
400                    .prune(&pruned_target_ty)
401                    .expect("pruned type should be shrunken version of unpruned type");
402                Ok(pruned_witness)
403            }
404
405            fn convert_disconnect(
406                &mut self,
407                _: &PostOrderIterItem<&ConstructNode<J>>,
408                right: Option<&Arc<RedeemNode<J>>>,
409                _: &Option<Arc<ConstructNode<J>>>,
410            ) -> Result<Arc<RedeemNode<J>>, Self::Error> {
411                Ok(right
412                    .map(Arc::clone)
413                    .expect("disconnect node that originally stems from redeem program should have all branches"))
414            }
415
416            fn convert_data(
417                &mut self,
418                data: &PostOrderIterItem<&ConstructNode<J>>,
419                inner: Inner<&Arc<RedeemNode<J>>, J, &Arc<RedeemNode<J>>, &Value>,
420            ) -> Result<Arc<RedeemData<J>>, Self::Error> {
421                // Finalize target types of witness nodes in advance so we can prune their values.
422                let final_arrow = data
423                    .node
424                    .arrow()
425                    .finalize()
426                    .expect("pruned types should check out if unpruned types check out");
427                let converted_inner = inner
428                    .map(|node| node.cached_data())
429                    .map_disconnect(|node| node.cached_data())
430                    .map_witness(Value::shallow_clone);
431                Ok(Arc::new(RedeemData::new(final_arrow, converted_inner)))
432            }
433        }
434
435        // 1) Run the Bit Machine and mark (un)used branches.
436        // This is the only fallible step in the pruning process.
437        let mut mac = BitMachine::for_program(self)?;
438        mac.exec_with_tracker(self, env, tracker)?;
439
440        // 2) Prune out unused case branches.
441        // Because the types of the pruned program may change,
442        // we construct a temporary witness program with unfinalized types.
443        types::Context::with_context(|inference_context| {
444            let pruned_witness_program = self
445                .convert::<InternalSharing, _, _>(&mut Pruner {
446                    inference_context,
447                    tracker,
448                    phantom: PhantomData,
449                })
450                .expect("pruning unused branches is infallible");
451
452            // 3) Finalize the types of the witness program.
453            // We obtain the pruned redeem program.
454            // Once the pruned type is finalized, we can proceed to prune witness values.
455            Ok(pruned_witness_program
456                .convert::<InternalSharing, _, _>(&mut Finalizer(PhantomData))
457                .expect("finalization is infallible"))
458        })
459    }
460
461    /// Decode a Simplicity program from bits, including the witness data.
462    pub fn decode<I1, I2>(
463        program: BitIter<I1>,
464        mut witness: BitIter<I2>,
465    ) -> Result<Arc<Self>, DecodeError>
466    where
467        I1: Iterator<Item = u8>,
468        I2: Iterator<Item = u8>,
469    {
470        // 0. Set up a type to help with the call to `convert` below
471        struct DecodeFinalizer<'bits, J: Jet, I: Iterator<Item = u8>> {
472            bits: &'bits mut BitIter<I>,
473            phantom: PhantomData<J>,
474        }
475
476        impl<'brand, J: Jet, I: Iterator<Item = u8>> Converter<Construct<'brand, J>, Redeem<J>>
477            for DecodeFinalizer<'_, J, I>
478        {
479            type Error = DecodeError;
480            fn convert_witness(
481                &mut self,
482                data: &PostOrderIterItem<&ConstructNode<J>>,
483                _: &Option<Value>,
484            ) -> Result<Value, Self::Error> {
485                let arrow = data.node.data.arrow();
486                let target_ty = arrow.target.finalize().map_err(DecodeError::Type)?;
487                Value::from_compact_bits(self.bits, &target_ty)
488                    .map_err(crate::decode::Error::from)
489                    .map_err(DecodeError::Decode)
490            }
491
492            fn convert_disconnect(
493                &mut self,
494                _: &PostOrderIterItem<&ConstructNode<J>>,
495                right: Option<&Arc<RedeemNode<J>>>,
496                _: &Option<Arc<ConstructNode<J>>>,
497            ) -> Result<Arc<RedeemNode<J>>, Self::Error> {
498                if let Some(child) = right {
499                    Ok(Arc::clone(child))
500                } else {
501                    Err(DecodeError::DisconnectRedeemTime)
502                }
503            }
504
505            fn convert_data(
506                &mut self,
507                data: &PostOrderIterItem<&ConstructNode<J>>,
508                inner: Inner<&Arc<RedeemNode<J>>, J, &Arc<RedeemNode<J>>, &Value>,
509            ) -> Result<Arc<RedeemData<J>>, Self::Error> {
510                let arrow = data
511                    .node
512                    .data
513                    .arrow()
514                    .finalize()
515                    .map_err(DecodeError::Type)?;
516                let converted_data = inner
517                    .map(|node| node.cached_data())
518                    .map_disconnect(|node| node.cached_data())
519                    .map_witness(Value::shallow_clone);
520                Ok(Arc::new(RedeemData::new(arrow, converted_data)))
521            }
522        }
523
524        // 1. Decode program without witnesses as ConstructNode
525        let program: Arc<Self> = types::Context::with_context(|ctx| {
526            let construct =
527                crate::ConstructNode::decode(&ctx, program).map_err(DecodeError::Decode)?;
528            construct
529                .set_arrow_to_program()
530                .map_err(DecodeError::Type)?;
531
532            // Importantly, we  use `InternalSharing` here to make sure that we respect
533            // the sharing choices that were actually encoded in the bitstream.
534            construct.convert::<InternalSharing, _, _>(&mut DecodeFinalizer {
535                bits: &mut witness,
536                phantom: PhantomData,
537            })
538        })?;
539
540        // 3. Check that we read exactly as much witness data as we expected
541        witness
542            .close()
543            .map_err(crate::decode::Error::BitIter)
544            .map_err(DecodeError::Decode)?;
545
546        // 4. Check sharing
547        // This loop is equivalent to using `program.is_shared_as::<MaxSharing>()`
548        // but is faster since it only runs a single iterator.
549        let mut ihrs: HashSet<Ihr> = HashSet::new();
550        for data in program.as_ref().post_order_iter::<InternalSharing>() {
551            if !ihrs.insert(data.node.ihr()) {
552                return Err(DecodeError::Decode(crate::decode::Error::SharingNotMaximal));
553            }
554        }
555
556        Ok(program)
557    }
558
559    #[cfg(feature = "base64")]
560    #[allow(clippy::should_implement_trait)] // returns Arc<Self>
561    pub fn from_str(prog: &str, wit: &str) -> Result<Arc<Self>, crate::ParseError> {
562        use crate::base64::engine::general_purpose;
563        use crate::base64::Engine as _;
564        use crate::hex::FromHex as _;
565
566        let v = general_purpose::STANDARD
567            .decode(prog)
568            .map_err(crate::ParseError::Base64)?;
569        let prog_iter = crate::BitIter::new(v.into_iter());
570
571        let v = Vec::from_hex(wit).map_err(crate::ParseError::Hex)?;
572        let wit_iter = crate::BitIter::new(v.into_iter());
573        Self::decode(prog_iter, wit_iter).map_err(crate::ParseError::Decode)
574    }
575
576    /// Encode the program to bits.
577    ///
578    /// Includes witness data. Returns the number of written bits.
579    #[deprecated(since = "0.5.0", note = "use Self::encode_with_witness instead")]
580    pub fn encode<W1, W2>(
581        &self,
582        prog: &mut BitWriter<W1>,
583        witness: &mut BitWriter<W2>,
584    ) -> io::Result<usize>
585    where
586        W1: io::Write,
587        W2: io::Write,
588    {
589        let sharing_iter = self.post_order_iter::<MaxSharing<Redeem<J>>>();
590        let program_bits = encode::encode_program(self, prog)?;
591        prog.flush_all()?;
592        let witness_bits = encode::encode_witness(sharing_iter.into_witnesses(), witness)?;
593        witness.flush_all()?;
594        Ok(program_bits + witness_bits)
595    }
596
597    /// Encode the program and witness data to byte vectors.
598    #[deprecated(since = "0.5.0", note = "use Self::to_vec_with_witness instead")]
599    pub fn encode_to_vec(&self) -> (Vec<u8>, Vec<u8>) {
600        let mut ret_1 = vec![];
601        let mut ret_2 = vec![];
602        self.encode_with_witness(&mut ret_1, &mut ret_2).unwrap();
603        (ret_1, ret_2)
604    }
605}
606
607#[cfg(test)]
608mod tests {
609    use super::*;
610    use crate::human_encoding::Forest;
611    use crate::jet::Core;
612    use crate::node::SimpleFinalizer;
613    use crate::types::Final;
614    use hex::DisplayHex;
615    use std::collections::HashMap;
616    use std::fmt;
617
618    #[cfg_attr(not(feature = "base64"), allow(unused_variables))]
619    #[track_caller]
620    fn assert_program_deserializable<J: Jet>(
621        prog_bytes: &[u8],
622        witness_bytes: &[u8],
623        cmr_str: &str,
624        amr_str: &str,
625        ihr_str: &str,
626        b64_str: &str,
627    ) -> Arc<RedeemNode<J>> {
628        let prog_hex = prog_bytes.as_hex();
629        let witness_hex = witness_bytes.as_hex();
630
631        let prog = BitIter::from(prog_bytes);
632        let witness = BitIter::from(witness_bytes);
633        let prog = match RedeemNode::<J>::decode(prog, witness) {
634            Ok(prog) => prog,
635            Err(e) => panic!("program {} failed: {}", prog_hex, e),
636        };
637
638        assert_eq!(
639            prog.cmr().to_string(),
640            cmr_str,
641            "CMR mismatch (got {} expected {}) for program {}",
642            prog.cmr(),
643            cmr_str,
644            prog_hex,
645        );
646
647        assert_eq!(
648            prog.amr().to_string(),
649            amr_str,
650            "AMR mismatch (got {} expected {}) for program {}",
651            prog.amr(),
652            amr_str,
653            prog_hex,
654        );
655        assert_eq!(
656            prog.ihr().to_string(),
657            ihr_str,
658            "IHR mismatch (got {} expected {}) for program {}",
659            prog.ihr(),
660            ihr_str,
661            prog_hex,
662        );
663
664        let (reser_prog, reser_witness) = prog.to_vec_with_witness();
665        assert_eq!(
666            prog_bytes,
667            &reser_prog[..],
668            "program {} reserialized as {}",
669            prog_hex,
670            reser_prog.as_hex(),
671        );
672        assert_eq!(
673            witness_bytes,
674            &reser_witness[..],
675            "witness {} reserialized as {}",
676            witness_hex,
677            reser_witness.as_hex(),
678        );
679
680        #[cfg(feature = "base64")]
681        {
682            let disp = prog.display();
683            assert_eq!(prog.to_string(), b64_str);
684            assert_eq!(disp.program().to_string(), b64_str);
685            assert_eq!(
686                disp.witness().to_string(),
687                witness_bytes.as_hex().to_string()
688            );
689        }
690
691        prog
692    }
693
694    #[track_caller]
695    fn assert_program_not_deserializable<J: Jet>(
696        prog_bytes: &[u8],
697        witness_bytes: &[u8],
698        err: &dyn fmt::Display,
699    ) {
700        let prog_hex = prog_bytes.as_hex();
701        let witness_hex = witness_bytes.as_hex();
702        let err_str = err.to_string();
703
704        let prog = BitIter::from(prog_bytes);
705        let witness = BitIter::from(witness_bytes);
706        match RedeemNode::<J>::decode(prog, witness) {
707            Ok(prog) => panic!(
708                "Program {} wit {} succeded (expected error {}). Program parsed as:\n{:?}",
709                prog_hex, witness_hex, err, prog
710            ),
711            Err(e) if e.to_string() == err_str => {} // ok
712            Err(e) => panic!(
713                "Program {} wit {} failed with error {} (expected error {})",
714                prog_hex, witness_hex, e, err
715            ),
716        };
717    }
718
719    #[test]
720    fn encode_shared_witnesses() {
721        // # Program code:
722        // wit1 = witness :: 1 -> 2^32
723        // wit2 = witness :: 1 -> 2^32
724        //
725        // wits_are_equal = comp (pair wit1 wit2) jet_eq_32 :: 1 -> 2
726        // main = comp wits_are_equal jet_verify            :: 1 -> 1
727        let eqwits = [0xcd, 0xdc, 0x51, 0xb6, 0xe2, 0x08, 0xc0, 0x40];
728        let iter = BitIter::from(&eqwits[..]);
729        let eqwits_prog = CommitNode::<Core>::decode(iter).unwrap();
730
731        let eqwits_final = eqwits_prog
732            .finalize(&mut SimpleFinalizer::new(std::iter::repeat(Value::u32(
733                0xDEADBEEF,
734            ))))
735            .unwrap();
736        let output = eqwits_final.to_vec_with_witness();
737
738        assert_eq!(
739            output,
740            (
741                [0xc9, 0xc4, 0x6d, 0xb8, 0x82, 0x30, 0x10].into(),
742                [0xde, 0xad, 0xbe, 0xef].into(),
743            ),
744            "output {} {}",
745            output.0.as_hex(),
746            output.1.as_hex()
747        );
748    }
749
750    #[test]
751    fn decode_shared_witnesses() {
752        // This program is exactly the output from the `encode_shared_witnesses` test.
753        // The point of this is to make sure that our witness-unsharing logic doesn't
754        // get confused here and try to read two witnesses when there are only one.
755        assert_program_deserializable::<Core>(
756            &[0xc9, 0xc4, 0x6d, 0xb8, 0x82, 0x30, 0x10],
757            &[0xde, 0xad, 0xbe, 0xef],
758            "d7969920eff9a1ed0359aaa8545b239c69969e22c304c645a7b49bcc976a40a8",
759            "f7acbb077e7661a08384818bc8e3a275ed42ad446252575a35a35f71689fef78",
760            "3ce4a6390b4e4bda6330acda4800e66e5d2cae0f5a2888564c706f2b910146b8",
761            "ycRtuIIwEA==",
762        );
763    }
764
765    #[test]
766    fn unshared_child() {
767        // # id1 and id2 should be shared, but are not!
768        // id1 = iden          :: A -> A # cmr dbfefcfc...
769        // id2 = iden          :: A -> A # cmr dbfefcfc...
770        // cp3 = comp id1 id2  :: A -> A # cmr c1ae55b5...
771        // main = comp cp3 cp3 :: A -> A # cmr 314e2879...
772        assert_program_not_deserializable::<Core>(
773            &[0xc1, 0x08, 0x04, 0x00],
774            &[],
775            &DecodeError::Decode(crate::decode::Error::SharingNotMaximal),
776        );
777    }
778
779    #[test]
780    fn witness_consumed() {
781        // "main = unit", but with a witness attached. Found by fuzzer.
782        let prog = BitIter::from(&[0x24][..]);
783        let wit = BitIter::from(&[0x00][..]);
784        match RedeemNode::<Core>::decode(prog, wit) {
785            Err(DecodeError::Decode(crate::decode::Error::BitIter(
786                crate::BitIterCloseError::TrailingBytes { first_byte: 0 },
787            ))) => {} // ok,
788            Err(e) => panic!("got incorrect error {e}"),
789            Ok(_) => panic!("accepted program with bad witness length"),
790        }
791    }
792
793    #[test]
794    fn shared_grandchild() {
795        // # This program repeats the node `cp2` three times; during iteration it will
796        // # be placed on the stack as part of the initial `comp` combinator, but by
797        // # the time we get to it, it will have already been yielded. Makes sure this
798        // # does not confuse the iteration logic and break the decoded program structure.
799        // id1 = iden
800        // cp2 = comp id1 id1
801        // cp3 = comp cp2 cp2
802        // main = comp cp3 cp2
803        assert_program_deserializable::<Core>(
804            &[0xc1, 0x00, 0x00, 0x01, 0x00],
805            &[],
806            "8a54101335ca2cf7e933d74cdb15f99becc4e540799ba5e2d19c00c9d7219e71",
807            "74e868bd640c250bc45522085158a9723fc7e277bb16a8d582c4012ebbb1f6f1",
808            "39b8f72bd1539de87d26673890603d6548cfc8b68571d996bdf9b1d8b557bd35",
809            "wQAAAQA=",
810        );
811    }
812
813    #[test]
814    #[rustfmt::skip]
815    fn assert_lr() {
816        // asst = assertl unit deadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeef
817        // input0 = pair (injl unit) unit
818        // main = comp input0 asst
819        assert_program_deserializable::<Core>(
820            &[
821                0xcd, 0x24, 0x08, 0x4b, 0x6f, 0x56, 0xdf, 0x77,
822                0xef, 0x56, 0xdf, 0x77, 0xef, 0x56, 0xdf, 0x77,
823                0xef, 0x56, 0xdf, 0x77, 0xef, 0x56, 0xdf, 0x77,
824                0xef, 0x56, 0xdf, 0x77, 0xef, 0x56, 0xdf, 0x77,
825                0xef, 0x56, 0xdf, 0x77, 0x86, 0x01, 0x80,
826            ],
827            &[],
828            "abdd773fc7a503908739b4a63198416fdd470948830cb5a6516b98fe0a3bfa85",
829            "1362ee53ae75218ed51dc4bd46cdbfa585f934ac6c6c3ff787e27dce91ccd80b",
830            "251c6778129e0f12da3f2388ab30184e815e9d9456b5931e54802a6715d9ca42",
831            "zSQIS29W33fvVt9371bfd+9W33fvVt9371bfd+9W33fvVt93hgGA",
832        );
833
834
835        // asst = assertr deadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeef unit
836        // input1 = pair (injr unit) unit
837        // main = comp input1 asst
838        assert_program_deserializable::<Core>(
839            &[
840                0xcd, 0x25, 0x08, 0x6d, 0xea, 0xdb, 0xee, 0xfd,
841                0xea, 0xdb, 0xee, 0xfd, 0xea, 0xdb, 0xee, 0xfd,
842                0xea, 0xdb, 0xee, 0xfd, 0xea, 0xdb, 0xee, 0xfd,
843                0xea, 0xdb, 0xee, 0xfd, 0xea, 0xdb, 0xee, 0xfd,
844                0xea, 0xdb, 0xee, 0xf4, 0x86, 0x01, 0x80,
845            ],
846            &[],
847            "f6c678dfb180b94567a9d524e05fbc893f6905e0e3db931ff01dc2701e783d4c",
848            "212d4fa3dbe2b33db1e11bb6f4cc973be5de0896a3775387a06056483b8feb0f",
849            "7a583edcc733b6bba66998110be403ac61fab2d93fc09ba3c84ab2509b538043",
850            "zSUIberb7v3q2+796tvu/erb7v3q2+796tvu/erb7v3q2+70hgGA",
851        );
852    }
853
854    #[test]
855    #[rustfmt::skip]
856    fn disconnect() {
857        // id1 = iden                 :: (2^256 * B) -> (2^256 * B)                       # cmr dbfefcfc...
858        // pr2 = pair id1 id1         :: (2^256 * B) -> ((2^256 * B) * (2^256 * B))       # cmr a62c628c...
859        // disc3 = disconnect pr2 pr2 :: B -> ((2^256 * B) * ((2^256 * B) * (2^256 * B))) # cmr d81d6f28...
860        // ut4 = unit                 :: ((2^256 * B) * ((2^256 * B) * (2^256 * B))) -> 1 # cmr 62274a89...
861        // main = comp disc3 ut4      :: B -> 1                                           # cmr a453360c...
862        assert_program_deserializable::<Core>(
863            &[0xc5, 0x02, 0x06, 0x24, 0x10],
864            &[],
865            "afe8f5f8bd3f64bfa51d2f29ffa22523604d9654c0d9862dbf2dc67ba097cbb2",
866            "15239708cb7b448cedc6a0b6401dce86ed74084056dd95831928860dd0c3ca67",
867            "9cdacb48b16e108ccbd6bcbce459a64056df285c2dc6e02dca6d13c4b1530fb0",
868            "xQIGJBA=",
869        );
870    }
871
872    #[test]
873    #[rustfmt::skip]
874    #[cfg(feature = "elements")]
875    fn disconnect2() {
876        // Program that Russell posted on IRC 2023-06-22 that tickled `Dag::right_child`
877        // bug that had been introduced that day. Probably not the most minimal test
878        // vector but might as well include it as it seems like an interesting program.
879        //
880        // # Witnesses
881        // wit1 = witness :: 1 -> 2^512
882        //
883        // # Constants
884        // const1 = word_jet 0x00000000000000000000003b78ce563f89a0ed9414f5aa28ad0d96d6795f9c63 :: 1 -> 2^256 # cmr a9e3dbca...
885        //
886        // # Program code
887        // id1 = iden                 :: (2^256 * 1) -> (2^256 * 1)     # cmr dbfefcfc...
888        // jt2 = jet_sig_all_hash     :: 1 -> 2^256                     # cmr 9902bc0f...
889        // disc3 = disconnect id1 jt2 :: 1 -> 2^512                     # cmr 6968f10e...
890        // pr4 = pair const1 disc3    :: 1 -> (2^256 * 2^512)           # cmr 378ad609...
891        // pr5 = pair pr4 wit1        :: 1 -> ((2^256 * 2^512) * 2^512) # cmr 0d51ff00...
892        // jt6 = jet_check_sig_verify :: ((2^256 * 2^512) * 2^512) -> 1 # cmr 297459d8...
893        //
894        // main = comp pr5 jt6        :: 1 -> 1                         # cmr 14a5e0cc...
895        assert_program_deserializable::<crate::jet::Elements>(
896            &[
897                0xd3, 0x69, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3b, 0x78, 0xce,
898                0x56, 0x3f, 0x89, 0xa0, 0xed, 0x94, 0x14, 0xf5, 0xaa, 0x28, 0xad, 0x0d, 0x96, 0xd6, 0x79, 0x5f,
899                0x9c, 0x63, 0x47, 0x07, 0x02, 0xc0, 0xe2, 0x8d, 0x88, 0x10, 
900            ],
901            &[
902                0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3b, 0x78, 0xce, 0x56, 0x3f,
903                0x89, 0xa0, 0xed, 0x94, 0x14, 0xf5, 0xaa, 0x28, 0xad, 0x0d, 0x96, 0xd6, 0x79, 0x5f, 0x9c, 0x63,
904                0xdb, 0x86, 0x8d, 0x45, 0xa0, 0xbc, 0x1d, 0x19, 0x01, 0x30, 0x2b, 0xc8, 0x7a, 0x87, 0x1c, 0xf1,
905                0x58, 0xe2, 0xbd, 0xe2, 0xcf, 0xa6, 0x45, 0xa8, 0x95, 0xc1, 0xb4, 0x5d, 0x68, 0xea, 0x24, 0xc0, 
906            ],
907            "f3cd4537d7ebb201732203195b30b549b8dc0c2c6257b3a0d53bedb08ea02874",
908            "107fa80454ed0f2d95d7c18d307912b1497505b98de47198fee23b5018efa544",
909            "d52021c638ba742a90bead9b3055efd66091fb50bb131aa8b10eb7c13ef464d1",
910            "02kAAAAAAAAAAAAAADt4zlY/iaDtlBT1qiitDZbWeV+cY0cHAsDijYgQ",
911        );
912    }
913
914    #[test]
915    #[rustfmt::skip]
916    #[cfg(feature = "elements")]
917    fn disconnect3() {
918        // Yet another disconnect-based program that hit a bug in our AMR computation
919        // (passing left arrow in place of right arrow to the AMR constructor.)
920        // # Program code
921        // id1 = iden                 :: (2^256 * 1) -> (2^256 * 1) # cmr dbfefcfc...
922        // ut2 = unit                 :: 1 -> 1                     # cmr 62274a89...
923        // jl3 = injl ut2             :: 1 -> 2                     # cmr bd0cce93...
924        // disc4 = disconnect id1 jl3 :: 1 -> (2^256 * 2)           # cmr 6968f10e...
925        // ut5 = unit                 :: (2^256 * 2) -> 1           # cmr 62274a89...
926        // main = comp disc4 ut5      :: 1 -> 1                     # cmr a8c9cc7a...
927        assert_program_deserializable::<crate::jet::Elements>(
928            &[0xc9, 0x09, 0x20, 0x74, 0x90, 0x40],
929            &[],
930            "b689bdee289c8dd4e2e283358d187813363d441776cf826dafc27cc8a81ec441",
931            "3c68660a1afde7982ce4aa9d499ad382bc32f5f9ad894a5e915f76e66303a25b",
932            "85313720ee43ae0ee03f88b05e6d9e4494308c6897bdeb3e93b94559c3317484",
933            "yQkgdJBA",
934        );
935    }
936
937    #[test]
938    #[cfg(feature = "elements")]
939    fn decode_schnorr() {
940        #[rustfmt::skip]
941        let schnorr0 = vec![
942            0xc6, 0xd5, 0xf2, 0x61, 0x14, 0x03, 0x24, 0xb1, 0x86, 0x20, 0x92, 0x68, 0x9f, 0x0b, 0xf1, 0x3a,
943            0xa4, 0x53, 0x6a, 0x63, 0x90, 0x8b, 0x06, 0xdf, 0x33, 0x61, 0x0c, 0x03, 0xe2, 0x27, 0x79, 0xc0,
944            0x6d, 0xf2, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
945            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
946            0x00, 0x00, 0xe2, 0x8d, 0x8c, 0x04, 0x00,
947        ];
948        #[rustfmt::skip]
949        let schnorr0_wit = vec![
950            0xe9, 0x07, 0x83, 0x1f, 0x80, 0x84, 0x8d, 0x10, 0x69, 0xa5, 0x37, 0x1b, 0x40, 0x24, 0x10, 0x36,
951            0x4b, 0xdf, 0x1c, 0x5f, 0x83, 0x07, 0xb0, 0x08, 0x4c, 0x55, 0xf1, 0xce, 0x2d, 0xca, 0x82, 0x15,
952            0x25, 0xf6, 0x6a, 0x4a, 0x85, 0xea, 0x8b, 0x71, 0xe4, 0x82, 0xa7, 0x4f, 0x38, 0x2d, 0x2c, 0xe5,
953            0xeb, 0xee, 0xe8, 0xfd, 0xb2, 0x17, 0x2f, 0x47, 0x7d, 0xf4, 0x90, 0x0d, 0x31, 0x05, 0x36, 0xc0,
954        ];
955        assert_program_deserializable::<crate::jet::Elements>(
956            &schnorr0,
957            &schnorr0_wit,
958            "8a9e97676b24be7797d9ee0bf32dd76bcd78028e973025f785eae8dc91c8a0da",
959            "ec97c8774cb6bfb381fdbbcc8d964380fb3a3b45779322624490d6231ae777a4",
960            "ad7c38b16b9129646dc89b52cff144de94a80e383c4983b53de65e3575abcf38",
961            "xtXyYRQDJLGGIJJonwvxOqRTamOQiwbfM2EMA+InecBt8gAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA4o2MBAA=",
962        );
963    }
964
965    #[cfg(feature = "elements")]
966    fn assert_correct_pruning<J: Jet>(
967        unpruned_prog: &str,
968        unpruned_wit: &HashMap<Arc<str>, Value>,
969        expected_pruned_prog: &str,
970        expected_pruned_wit: &HashMap<Arc<str>, Value>,
971        env: &J::Environment,
972    ) {
973        let unpruned_program = types::Context::with_context(|ctx| {
974            Forest::<J>::parse(unpruned_prog)
975                .expect("unpruned program should parse")
976                .to_witness_node(&ctx, unpruned_wit)
977                .expect("unpruned program should have main")
978                .finalize_unpruned()
979                .expect("unpruned program should finalize")
980        });
981        let expected_unpruned_program = types::Context::with_context(|ctx| {
982            Forest::<J>::parse(expected_pruned_prog)
983                .expect("expected pruned program should parse")
984                .to_witness_node(&ctx, expected_pruned_wit)
985                .expect("expected pruned program should have main")
986                .finalize_unpruned()
987                .expect("expected pruned program should finalize")
988        });
989
990        let mut mac = BitMachine::for_program(&unpruned_program)
991            .expect("unpruned program has reasonable bounds");
992        let unpruned_output = mac
993            .exec(&unpruned_program, env)
994            .expect("unpruned program should run without failure");
995
996        let pruned_program = unpruned_program
997            .prune(env)
998            .expect("pruning should not fail if execution succeeded");
999        assert_eq!(
1000            pruned_program.ihr(),
1001            expected_unpruned_program.ihr(),
1002            "pruning result differs from expected result"
1003        );
1004
1005        let mut mac =
1006            BitMachine::for_program(&pruned_program).expect("pruned program has reasonable bounds");
1007        let pruned_output = mac
1008            .exec(&pruned_program, env)
1009            .expect("pruned program should run without failure");
1010        assert_eq!(
1011            unpruned_output, pruned_output,
1012            "pruned program should return same output as unpruned program"
1013        );
1014    }
1015
1016    #[test]
1017    #[cfg(feature = "elements")]
1018    fn prune() {
1019        let env = crate::jet::elements::ElementsEnv::dummy();
1020
1021        /*
1022         * 1) Prune a product type / value
1023         */
1024        let unpruned_prog = r#"wit1 := witness : 1 -> 2
1025wit2 := witness : 1 -> 2^64 * 2^64
1026input := pair wit1 wit2 : 1 -> 2 * (2^64 * 2^64)
1027process := case (drop take jet_is_zero_64) (drop drop jet_is_zero_64) : 2 * (2^64 * 2^64) -> 2
1028main := comp input comp process jet_verify : 1 -> 1"#;
1029
1030        // 1.1) Prune right
1031        let unpruned_wit = HashMap::from([
1032            (Arc::from("wit1"), Value::u1(0)),
1033            (
1034                Arc::from("wit2"),
1035                Value::product(Value::u64(0), Value::u64(0)),
1036            ),
1037        ]);
1038        let pruned_prog = r#"wit1 := witness : 1 -> 2
1039wit2 := witness : 1 -> 2^64 * 1 -- right component was pruned
1040input := pair wit1 wit2 : 1 -> 2 * (2^64 * 1)
1041process := assertl (drop take jet_is_zero_64) #{drop drop jet_is_zero_64} : 2 * (2^64 * 1) -> 2 -- case became assertl
1042main := comp input comp process jet_verify : 1 -> 1"#;
1043        let pruned_wit = HashMap::from([
1044            (Arc::from("wit1"), Value::u1(0)),
1045            (
1046                Arc::from("wit2"),
1047                Value::product(Value::u64(0), Value::unit()),
1048            ),
1049        ]);
1050        assert_correct_pruning::<crate::jet::Elements>(
1051            unpruned_prog,
1052            &unpruned_wit,
1053            pruned_prog,
1054            &pruned_wit,
1055            &env,
1056        );
1057
1058        // 1.2) Prune left
1059        let unpruned_wit = HashMap::from([
1060            (Arc::from("wit1"), Value::u1(1)),
1061            (
1062                Arc::from("wit2"),
1063                Value::product(Value::u64(0), Value::u64(0)),
1064            ),
1065        ]);
1066        let pruned_prog = r#"wit1 := witness : 1 -> 2
1067wit2 := witness : 1 -> 1 * 2^64 -- left component was pruned
1068input := pair wit1 wit2 : 1 -> 2 * (1 * 2^64)
1069process := assertr #{drop take jet_is_zero_64} (drop drop jet_is_zero_64) : 2 * (1 * 2^64) -> 2 -- case became assertr
1070main := comp input comp process jet_verify : 1 -> 1"#;
1071        let pruned_wit = HashMap::from([
1072            (Arc::from("wit1"), Value::u1(1)),
1073            (
1074                Arc::from("wit2"),
1075                Value::product(Value::unit(), Value::u64(0)),
1076            ),
1077        ]);
1078        assert_correct_pruning::<crate::jet::Elements>(
1079            unpruned_prog,
1080            &unpruned_wit,
1081            pruned_prog,
1082            &pruned_wit,
1083            &env,
1084        );
1085
1086        /*
1087         * 1) Prune a sum type / value
1088         */
1089        let prune_sum = r#"wit1 := witness : 1 -> 2^64 + 2^64
1090input := pair wit1 unit : 1 -> (2^64 + 2^64) * 1
1091process := case (take jet_is_zero_64) (take jet_is_zero_64) : (2^64 + 2^64) * 1 -> 2
1092main := comp input comp process jet_verify : 1 -> 1"#;
1093
1094        // 1.1) Prune right
1095        let unpruned_wit =
1096            HashMap::from([(Arc::from("wit1"), Value::left(Value::u64(0), Final::u64()))]);
1097        let pruned_prog = r#"wit1 := witness : 1 -> 2^64 + 1 -- right sub type became unit
1098input := pair wit1 unit : 1 -> (2^64 + 1) * 1
1099process := assertl (take jet_is_zero_64) #{take jet_is_zero_64} : (2^64 + 1) * 1 -> 2 -- case became assertl
1100main := comp input comp process jet_verify : 1 -> 1"#;
1101        let pruned_wit =
1102            HashMap::from([(Arc::from("wit1"), Value::left(Value::u64(0), Final::unit()))]);
1103        assert_correct_pruning::<crate::jet::Elements>(
1104            prune_sum,
1105            &unpruned_wit,
1106            pruned_prog,
1107            &pruned_wit,
1108            &env,
1109        );
1110
1111        // 1.2) Prune left
1112        let unpruned_wit = HashMap::from([(
1113            Arc::from("wit1"),
1114            Value::right(Final::unit(), Value::u64(0)),
1115        )]);
1116        let pruned_prog = r#"wit1 := witness : 1 -> 1 + 2^64 -- left sub type became unit
1117input := pair wit1 unit : 1 -> (1 + 2^64) * 1
1118process := assertr #{take jet_is_zero_64} (take jet_is_zero_64) : (1 + 2^64) * 1 -> 2 -- case became assertr
1119main := comp input comp process jet_verify : 1 -> 1"#;
1120        let pruned_wit = HashMap::from([(
1121            Arc::from("wit1"),
1122            Value::right(Final::unit(), Value::u64(0)),
1123        )]);
1124        assert_correct_pruning::<crate::jet::Elements>(
1125            prune_sum,
1126            &unpruned_wit,
1127            pruned_prog,
1128            &pruned_wit,
1129            &env,
1130        );
1131    }
1132}
1133
1134#[cfg(bench)]
1135#[cfg(feature = "elements")]
1136mod benches {
1137    use super::*;
1138
1139    use crate::bit_encoding::BitIter;
1140    use crate::jet::Elements;
1141
1142    use test::{black_box, Bencher};
1143
1144    #[bench]
1145    fn decode_fixed_program(bh: &mut Bencher) {
1146        let prog = &[
1147            0xd3, 0x69, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3b,
1148            0x78, 0xce, 0x56, 0x3f, 0x89, 0xa0, 0xed, 0x94, 0x14, 0xf5, 0xaa, 0x28, 0xad, 0x0d,
1149            0x96, 0xd6, 0x79, 0x5f, 0x9c, 0x63, 0x47, 0x07, 0x02, 0xc0, 0xe2, 0x8d, 0x88, 0x10,
1150        ][..];
1151        let witness = &[
1152            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3b, 0x78, 0xce,
1153            0x56, 0x3f, 0x89, 0xa0, 0xed, 0x94, 0x14, 0xf5, 0xaa, 0x28, 0xad, 0x0d, 0x96, 0xd6,
1154            0x79, 0x5f, 0x9c, 0x63, 0xdb, 0x86, 0x8d, 0x45, 0xa0, 0xbc, 0x1d, 0x19, 0x01, 0x30,
1155            0x2b, 0xc8, 0x7a, 0x87, 0x1c, 0xf1, 0x58, 0xe2, 0xbd, 0xe2, 0xcf, 0xa6, 0x45, 0xa8,
1156            0x95, 0xc1, 0xb4, 0x5d, 0x68, 0xea, 0x24, 0xc0,
1157        ][..];
1158        bh.iter(|| {
1159            let prog = BitIter::from(prog);
1160            let witness = BitIter::from(witness);
1161            black_box(RedeemNode::<Elements>::decode(
1162                black_box(prog),
1163                black_box(witness),
1164            ))
1165            .unwrap();
1166        });
1167    }
1168}