simplicity/node/
redeem.rs

1// SPDX-License-Identifier: CC0-1.0
2
3use crate::analysis::NodeBounds;
4use crate::bit_machine::{ExecutionError, 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        struct Pruner<'brand, J> {
294            inference_context: types::Context<'brand>,
295            tracker: SetTracker,
296            phantom: PhantomData<J>,
297        }
298
299        impl<'brand, J: Jet> Converter<Redeem<J>, Construct<'brand, J>> for Pruner<'brand, J> {
300            type Error = std::convert::Infallible;
301
302            fn convert_witness(
303                &mut self,
304                _: &PostOrderIterItem<&RedeemNode<J>>,
305                witness: &Value,
306            ) -> Result<Option<Value>, Self::Error> {
307                // The pruned type is not finalized at this point,
308                // so we cannot prune the witness value.
309                Ok(Some(witness.shallow_clone()))
310            }
311
312            fn convert_disconnect(
313                &mut self,
314                _: &PostOrderIterItem<&RedeemNode<J>>,
315                right: Option<&Arc<ConstructNode<'brand, J>>>,
316                _: &Arc<RedeemNode<J>>,
317            ) -> Result<Option<Arc<ConstructNode<'brand, J>>>, Self::Error> {
318                debug_assert!(
319                    right.is_some(),
320                    "disconnected branch should exist in unpruned redeem program"
321                );
322                Ok(right.map(Arc::clone))
323            }
324
325            fn prune_case(
326                &mut self,
327                data: &PostOrderIterItem<&RedeemNode<J>>,
328                _left: &Arc<ConstructNode<J>>,
329                _right: &Arc<ConstructNode<J>>,
330            ) -> Result<Hide, Self::Error> {
331                // The IHR of the pruned program may change,
332                // but the Converter trait gives us access to the unpruned node (`data`).
333                // The Bit Machine tracked (un)used case branches based on the unpruned IHR.
334                match (
335                    self.tracker.left().contains(&data.node.ihr()),
336                    self.tracker.right().contains(&data.node.ihr()),
337                ) {
338                    (true, true) => Ok(Hide::Neither),
339                    (false, true) => Ok(Hide::Left),
340                    (true, false) => Ok(Hide::Right),
341                    (false, false) => Ok(Hide::Neither), // case nodes that were never executed will be pruned out by their ancestors
342                }
343            }
344
345            fn convert_data(
346                &mut self,
347                _: &PostOrderIterItem<&RedeemNode<J>>,
348                inner: Inner<
349                    &Arc<ConstructNode<'brand, J>>,
350                    J,
351                    &Option<Arc<ConstructNode<'brand, J>>>,
352                    &Option<Value>,
353                >,
354            ) -> Result<ConstructData<'brand, J>, Self::Error> {
355                let converted_inner = inner
356                    .map(|node| node.cached_data())
357                    .map_witness(Option::<Value>::clone);
358                let retyped = ConstructData::from_inner(&self.inference_context, converted_inner)
359                    .expect("pruned types should check out if unpruned types check out");
360                Ok(retyped)
361            }
362        }
363
364        struct Finalizer<J>(PhantomData<J>);
365
366        impl<'brand, J: Jet> Converter<Construct<'brand, J>, Redeem<J>> for Finalizer<J> {
367            type Error = std::convert::Infallible;
368
369            fn convert_witness(
370                &mut self,
371                data: &PostOrderIterItem<&ConstructNode<J>>,
372                witness: &Option<Value>,
373            ) -> Result<Value, Self::Error> {
374                let pruned_target_ty = data
375                    .node
376                    .arrow()
377                    .target
378                    .finalize()
379                    .expect("pruned types should check out if unpruned types check out");
380                let pruned_witness = witness
381                    .as_ref()
382                    .expect("witness node that originally stems from redeem program should be populated")
383                    .prune(&pruned_target_ty)
384                    .expect("pruned type should be shrunken version of unpruned type");
385                Ok(pruned_witness)
386            }
387
388            fn convert_disconnect(
389                &mut self,
390                _: &PostOrderIterItem<&ConstructNode<J>>,
391                right: Option<&Arc<RedeemNode<J>>>,
392                _: &Option<Arc<ConstructNode<J>>>,
393            ) -> Result<Arc<RedeemNode<J>>, Self::Error> {
394                Ok(right
395                    .map(Arc::clone)
396                    .expect("disconnect node that originally stems from redeem program should have all branches"))
397            }
398
399            fn convert_data(
400                &mut self,
401                data: &PostOrderIterItem<&ConstructNode<J>>,
402                inner: Inner<&Arc<RedeemNode<J>>, J, &Arc<RedeemNode<J>>, &Value>,
403            ) -> Result<Arc<RedeemData<J>>, Self::Error> {
404                // Finalize target types of witness nodes in advance so we can prune their values.
405                let final_arrow = data
406                    .node
407                    .arrow()
408                    .finalize()
409                    .expect("pruned types should check out if unpruned types check out");
410                let converted_inner = inner
411                    .map(|node| node.cached_data())
412                    .map_disconnect(|node| node.cached_data())
413                    .map_witness(Value::shallow_clone);
414                Ok(Arc::new(RedeemData::new(final_arrow, converted_inner)))
415            }
416        }
417
418        // 1) Run the Bit Machine and mark (un)used branches.
419        // This is the only fallible step in the pruning process.
420        let mut mac = BitMachine::for_program(self)?;
421        let tracker = mac.exec_prune(self, env)?;
422
423        // 2) Prune out unused case branches.
424        // Because the types of the pruned program may change,
425        // we construct a temporary witness program with unfinalized types.
426        types::Context::with_context(|inference_context| {
427            let pruned_witness_program = self
428                .convert::<InternalSharing, _, _>(&mut Pruner {
429                    inference_context,
430                    tracker,
431                    phantom: PhantomData,
432                })
433                .expect("pruning unused branches is infallible");
434
435            // 3) Finalize the types of the witness program.
436            // We obtain the pruned redeem program.
437            // Once the pruned type is finalized, we can proceed to prune witness values.
438            Ok(pruned_witness_program
439                .convert::<InternalSharing, _, _>(&mut Finalizer(PhantomData))
440                .expect("finalization is infallible"))
441        })
442    }
443
444    /// Decode a Simplicity program from bits, including the witness data.
445    pub fn decode<I1, I2>(
446        program: BitIter<I1>,
447        mut witness: BitIter<I2>,
448    ) -> Result<Arc<Self>, DecodeError>
449    where
450        I1: Iterator<Item = u8>,
451        I2: Iterator<Item = u8>,
452    {
453        // 0. Set up a type to help with the call to `convert` below
454        struct DecodeFinalizer<'bits, J: Jet, I: Iterator<Item = u8>> {
455            bits: &'bits mut BitIter<I>,
456            phantom: PhantomData<J>,
457        }
458
459        impl<'brand, J: Jet, I: Iterator<Item = u8>> Converter<Construct<'brand, J>, Redeem<J>>
460            for DecodeFinalizer<'_, J, I>
461        {
462            type Error = DecodeError;
463            fn convert_witness(
464                &mut self,
465                data: &PostOrderIterItem<&ConstructNode<J>>,
466                _: &Option<Value>,
467            ) -> Result<Value, Self::Error> {
468                let arrow = data.node.data.arrow();
469                let target_ty = arrow.target.finalize().map_err(DecodeError::Type)?;
470                Value::from_compact_bits(self.bits, &target_ty)
471                    .map_err(crate::decode::Error::from)
472                    .map_err(DecodeError::Decode)
473            }
474
475            fn convert_disconnect(
476                &mut self,
477                _: &PostOrderIterItem<&ConstructNode<J>>,
478                right: Option<&Arc<RedeemNode<J>>>,
479                _: &Option<Arc<ConstructNode<J>>>,
480            ) -> Result<Arc<RedeemNode<J>>, Self::Error> {
481                if let Some(child) = right {
482                    Ok(Arc::clone(child))
483                } else {
484                    Err(DecodeError::DisconnectRedeemTime)
485                }
486            }
487
488            fn convert_data(
489                &mut self,
490                data: &PostOrderIterItem<&ConstructNode<J>>,
491                inner: Inner<&Arc<RedeemNode<J>>, J, &Arc<RedeemNode<J>>, &Value>,
492            ) -> Result<Arc<RedeemData<J>>, Self::Error> {
493                let arrow = data
494                    .node
495                    .data
496                    .arrow()
497                    .finalize()
498                    .map_err(DecodeError::Type)?;
499                let converted_data = inner
500                    .map(|node| node.cached_data())
501                    .map_disconnect(|node| node.cached_data())
502                    .map_witness(Value::shallow_clone);
503                Ok(Arc::new(RedeemData::new(arrow, converted_data)))
504            }
505        }
506
507        // 1. Decode program without witnesses as ConstructNode
508        let program: Arc<Self> = types::Context::with_context(|ctx| {
509            let construct =
510                crate::ConstructNode::decode(&ctx, program).map_err(DecodeError::Decode)?;
511            construct
512                .set_arrow_to_program()
513                .map_err(DecodeError::Type)?;
514
515            // Importantly, we  use `InternalSharing` here to make sure that we respect
516            // the sharing choices that were actually encoded in the bitstream.
517            construct.convert::<InternalSharing, _, _>(&mut DecodeFinalizer {
518                bits: &mut witness,
519                phantom: PhantomData,
520            })
521        })?;
522
523        // 3. Check that we read exactly as much witness data as we expected
524        witness
525            .close()
526            .map_err(crate::decode::Error::BitIter)
527            .map_err(DecodeError::Decode)?;
528
529        // 4. Check sharing
530        // This loop is equivalent to using `program.is_shared_as::<MaxSharing>()`
531        // but is faster since it only runs a single iterator.
532        let mut ihrs: HashSet<Ihr> = HashSet::new();
533        for data in program.as_ref().post_order_iter::<InternalSharing>() {
534            if !ihrs.insert(data.node.ihr()) {
535                return Err(DecodeError::Decode(crate::decode::Error::SharingNotMaximal));
536            }
537        }
538
539        Ok(program)
540    }
541
542    #[cfg(feature = "base64")]
543    #[allow(clippy::should_implement_trait)] // returns Arc<Self>
544    pub fn from_str(prog: &str, wit: &str) -> Result<Arc<Self>, crate::ParseError> {
545        use crate::base64::engine::general_purpose;
546        use crate::base64::Engine as _;
547        use crate::hex::FromHex as _;
548
549        let v = general_purpose::STANDARD
550            .decode(prog)
551            .map_err(crate::ParseError::Base64)?;
552        let prog_iter = crate::BitIter::new(v.into_iter());
553
554        let v = Vec::from_hex(wit).map_err(crate::ParseError::Hex)?;
555        let wit_iter = crate::BitIter::new(v.into_iter());
556        Self::decode(prog_iter, wit_iter).map_err(crate::ParseError::Decode)
557    }
558
559    /// Encode the program to bits.
560    ///
561    /// Includes witness data. Returns the number of written bits.
562    #[deprecated(since = "0.5.0", note = "use Self::encode_with_witness instead")]
563    pub fn encode<W1, W2>(
564        &self,
565        prog: &mut BitWriter<W1>,
566        witness: &mut BitWriter<W2>,
567    ) -> io::Result<usize>
568    where
569        W1: io::Write,
570        W2: io::Write,
571    {
572        let sharing_iter = self.post_order_iter::<MaxSharing<Redeem<J>>>();
573        let program_bits = encode::encode_program(self, prog)?;
574        prog.flush_all()?;
575        let witness_bits = encode::encode_witness(sharing_iter.into_witnesses(), witness)?;
576        witness.flush_all()?;
577        Ok(program_bits + witness_bits)
578    }
579
580    /// Encode the program and witness data to byte vectors.
581    #[deprecated(since = "0.5.0", note = "use Self::to_vec_with_witness instead")]
582    pub fn encode_to_vec(&self) -> (Vec<u8>, Vec<u8>) {
583        let mut ret_1 = vec![];
584        let mut ret_2 = vec![];
585        self.encode_with_witness(&mut ret_1, &mut ret_2).unwrap();
586        (ret_1, ret_2)
587    }
588}
589
590#[cfg(test)]
591mod tests {
592    use super::*;
593    use crate::human_encoding::Forest;
594    use crate::jet::Core;
595    use crate::node::SimpleFinalizer;
596    use crate::types::Final;
597    use hex::DisplayHex;
598    use std::collections::HashMap;
599    use std::fmt;
600
601    #[cfg_attr(not(feature = "base64"), allow(unused_variables))]
602    #[track_caller]
603    fn assert_program_deserializable<J: Jet>(
604        prog_bytes: &[u8],
605        witness_bytes: &[u8],
606        cmr_str: &str,
607        amr_str: &str,
608        ihr_str: &str,
609        b64_str: &str,
610    ) -> Arc<RedeemNode<J>> {
611        let prog_hex = prog_bytes.as_hex();
612        let witness_hex = witness_bytes.as_hex();
613
614        let prog = BitIter::from(prog_bytes);
615        let witness = BitIter::from(witness_bytes);
616        let prog = match RedeemNode::<J>::decode(prog, witness) {
617            Ok(prog) => prog,
618            Err(e) => panic!("program {} failed: {}", prog_hex, e),
619        };
620
621        assert_eq!(
622            prog.cmr().to_string(),
623            cmr_str,
624            "CMR mismatch (got {} expected {}) for program {}",
625            prog.cmr(),
626            cmr_str,
627            prog_hex,
628        );
629
630        assert_eq!(
631            prog.amr().to_string(),
632            amr_str,
633            "AMR mismatch (got {} expected {}) for program {}",
634            prog.amr(),
635            amr_str,
636            prog_hex,
637        );
638        assert_eq!(
639            prog.ihr().to_string(),
640            ihr_str,
641            "IHR mismatch (got {} expected {}) for program {}",
642            prog.ihr(),
643            ihr_str,
644            prog_hex,
645        );
646
647        let (reser_prog, reser_witness) = prog.to_vec_with_witness();
648        assert_eq!(
649            prog_bytes,
650            &reser_prog[..],
651            "program {} reserialized as {}",
652            prog_hex,
653            reser_prog.as_hex(),
654        );
655        assert_eq!(
656            witness_bytes,
657            &reser_witness[..],
658            "witness {} reserialized as {}",
659            witness_hex,
660            reser_witness.as_hex(),
661        );
662
663        #[cfg(feature = "base64")]
664        {
665            let disp = prog.display();
666            assert_eq!(prog.to_string(), b64_str);
667            assert_eq!(disp.program().to_string(), b64_str);
668            assert_eq!(
669                disp.witness().to_string(),
670                witness_bytes.as_hex().to_string()
671            );
672        }
673
674        prog
675    }
676
677    #[track_caller]
678    fn assert_program_not_deserializable<J: Jet>(
679        prog_bytes: &[u8],
680        witness_bytes: &[u8],
681        err: &dyn fmt::Display,
682    ) {
683        let prog_hex = prog_bytes.as_hex();
684        let witness_hex = witness_bytes.as_hex();
685        let err_str = err.to_string();
686
687        let prog = BitIter::from(prog_bytes);
688        let witness = BitIter::from(witness_bytes);
689        match RedeemNode::<J>::decode(prog, witness) {
690            Ok(prog) => panic!(
691                "Program {} wit {} succeded (expected error {}). Program parsed as:\n{:?}",
692                prog_hex, witness_hex, err, prog
693            ),
694            Err(e) if e.to_string() == err_str => {} // ok
695            Err(e) => panic!(
696                "Program {} wit {} failed with error {} (expected error {})",
697                prog_hex, witness_hex, e, err
698            ),
699        };
700    }
701
702    #[test]
703    fn encode_shared_witnesses() {
704        // # Program code:
705        // wit1 = witness :: 1 -> 2^32
706        // wit2 = witness :: 1 -> 2^32
707        //
708        // wits_are_equal = comp (pair wit1 wit2) jet_eq_32 :: 1 -> 2
709        // main = comp wits_are_equal jet_verify            :: 1 -> 1
710        let eqwits = [0xcd, 0xdc, 0x51, 0xb6, 0xe2, 0x08, 0xc0, 0x40];
711        let iter = BitIter::from(&eqwits[..]);
712        let eqwits_prog = CommitNode::<Core>::decode(iter).unwrap();
713
714        let eqwits_final = eqwits_prog
715            .finalize(&mut SimpleFinalizer::new(std::iter::repeat(Value::u32(
716                0xDEADBEEF,
717            ))))
718            .unwrap();
719        let output = eqwits_final.to_vec_with_witness();
720
721        assert_eq!(
722            output,
723            (
724                [0xc9, 0xc4, 0x6d, 0xb8, 0x82, 0x30, 0x10].into(),
725                [0xde, 0xad, 0xbe, 0xef].into(),
726            ),
727            "output {} {}",
728            output.0.as_hex(),
729            output.1.as_hex()
730        );
731    }
732
733    #[test]
734    fn decode_shared_witnesses() {
735        // This program is exactly the output from the `encode_shared_witnesses` test.
736        // The point of this is to make sure that our witness-unsharing logic doesn't
737        // get confused here and try to read two witnesses when there are only one.
738        assert_program_deserializable::<Core>(
739            &[0xc9, 0xc4, 0x6d, 0xb8, 0x82, 0x30, 0x10],
740            &[0xde, 0xad, 0xbe, 0xef],
741            "d7969920eff9a1ed0359aaa8545b239c69969e22c304c645a7b49bcc976a40a8",
742            "f7acbb077e7661a08384818bc8e3a275ed42ad446252575a35a35f71689fef78",
743            "3ce4a6390b4e4bda6330acda4800e66e5d2cae0f5a2888564c706f2b910146b8",
744            "ycRtuIIwEA==",
745        );
746    }
747
748    #[test]
749    fn unshared_child() {
750        // # id1 and id2 should be shared, but are not!
751        // id1 = iden          :: A -> A # cmr dbfefcfc...
752        // id2 = iden          :: A -> A # cmr dbfefcfc...
753        // cp3 = comp id1 id2  :: A -> A # cmr c1ae55b5...
754        // main = comp cp3 cp3 :: A -> A # cmr 314e2879...
755        assert_program_not_deserializable::<Core>(
756            &[0xc1, 0x08, 0x04, 0x00],
757            &[],
758            &DecodeError::Decode(crate::decode::Error::SharingNotMaximal),
759        );
760    }
761
762    #[test]
763    fn witness_consumed() {
764        // "main = unit", but with a witness attached. Found by fuzzer.
765        let prog = BitIter::from(&[0x24][..]);
766        let wit = BitIter::from(&[0x00][..]);
767        match RedeemNode::<Core>::decode(prog, wit) {
768            Err(DecodeError::Decode(crate::decode::Error::BitIter(
769                crate::BitIterCloseError::TrailingBytes { first_byte: 0 },
770            ))) => {} // ok,
771            Err(e) => panic!("got incorrect error {e}"),
772            Ok(_) => panic!("accepted program with bad witness length"),
773        }
774    }
775
776    #[test]
777    fn shared_grandchild() {
778        // # This program repeats the node `cp2` three times; during iteration it will
779        // # be placed on the stack as part of the initial `comp` combinator, but by
780        // # the time we get to it, it will have already been yielded. Makes sure this
781        // # does not confuse the iteration logic and break the decoded program structure.
782        // id1 = iden
783        // cp2 = comp id1 id1
784        // cp3 = comp cp2 cp2
785        // main = comp cp3 cp2
786        assert_program_deserializable::<Core>(
787            &[0xc1, 0x00, 0x00, 0x01, 0x00],
788            &[],
789            "8a54101335ca2cf7e933d74cdb15f99becc4e540799ba5e2d19c00c9d7219e71",
790            "74e868bd640c250bc45522085158a9723fc7e277bb16a8d582c4012ebbb1f6f1",
791            "39b8f72bd1539de87d26673890603d6548cfc8b68571d996bdf9b1d8b557bd35",
792            "wQAAAQA=",
793        );
794    }
795
796    #[test]
797    #[rustfmt::skip]
798    fn assert_lr() {
799        // asst = assertl unit deadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeef
800        // input0 = pair (injl unit) unit
801        // main = comp input0 asst
802        assert_program_deserializable::<Core>(
803            &[
804                0xcd, 0x24, 0x08, 0x4b, 0x6f, 0x56, 0xdf, 0x77,
805                0xef, 0x56, 0xdf, 0x77, 0xef, 0x56, 0xdf, 0x77,
806                0xef, 0x56, 0xdf, 0x77, 0xef, 0x56, 0xdf, 0x77,
807                0xef, 0x56, 0xdf, 0x77, 0xef, 0x56, 0xdf, 0x77,
808                0xef, 0x56, 0xdf, 0x77, 0x86, 0x01, 0x80,
809            ],
810            &[],
811            "abdd773fc7a503908739b4a63198416fdd470948830cb5a6516b98fe0a3bfa85",
812            "1362ee53ae75218ed51dc4bd46cdbfa585f934ac6c6c3ff787e27dce91ccd80b",
813            "251c6778129e0f12da3f2388ab30184e815e9d9456b5931e54802a6715d9ca42",
814            "zSQIS29W33fvVt9371bfd+9W33fvVt9371bfd+9W33fvVt93hgGA",
815        );
816
817
818        // asst = assertr deadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeef unit
819        // input1 = pair (injr unit) unit
820        // main = comp input1 asst
821        assert_program_deserializable::<Core>(
822            &[
823                0xcd, 0x25, 0x08, 0x6d, 0xea, 0xdb, 0xee, 0xfd,
824                0xea, 0xdb, 0xee, 0xfd, 0xea, 0xdb, 0xee, 0xfd,
825                0xea, 0xdb, 0xee, 0xfd, 0xea, 0xdb, 0xee, 0xfd,
826                0xea, 0xdb, 0xee, 0xfd, 0xea, 0xdb, 0xee, 0xfd,
827                0xea, 0xdb, 0xee, 0xf4, 0x86, 0x01, 0x80,
828            ],
829            &[],
830            "f6c678dfb180b94567a9d524e05fbc893f6905e0e3db931ff01dc2701e783d4c",
831            "212d4fa3dbe2b33db1e11bb6f4cc973be5de0896a3775387a06056483b8feb0f",
832            "7a583edcc733b6bba66998110be403ac61fab2d93fc09ba3c84ab2509b538043",
833            "zSUIberb7v3q2+796tvu/erb7v3q2+796tvu/erb7v3q2+70hgGA",
834        );
835    }
836
837    #[test]
838    #[rustfmt::skip]
839    fn disconnect() {
840        // id1 = iden                 :: (2^256 * B) -> (2^256 * B)                       # cmr dbfefcfc...
841        // pr2 = pair id1 id1         :: (2^256 * B) -> ((2^256 * B) * (2^256 * B))       # cmr a62c628c...
842        // disc3 = disconnect pr2 pr2 :: B -> ((2^256 * B) * ((2^256 * B) * (2^256 * B))) # cmr d81d6f28...
843        // ut4 = unit                 :: ((2^256 * B) * ((2^256 * B) * (2^256 * B))) -> 1 # cmr 62274a89...
844        // main = comp disc3 ut4      :: B -> 1                                           # cmr a453360c...
845        assert_program_deserializable::<Core>(
846            &[0xc5, 0x02, 0x06, 0x24, 0x10],
847            &[],
848            "afe8f5f8bd3f64bfa51d2f29ffa22523604d9654c0d9862dbf2dc67ba097cbb2",
849            "15239708cb7b448cedc6a0b6401dce86ed74084056dd95831928860dd0c3ca67",
850            "9cdacb48b16e108ccbd6bcbce459a64056df285c2dc6e02dca6d13c4b1530fb0",
851            "xQIGJBA=",
852        );
853    }
854
855    #[test]
856    #[rustfmt::skip]
857    #[cfg(feature = "elements")]
858    fn disconnect2() {
859        // Program that Russell posted on IRC 2023-06-22 that tickled `Dag::right_child`
860        // bug that had been introduced that day. Probably not the most minimal test
861        // vector but might as well include it as it seems like an interesting program.
862        //
863        // # Witnesses
864        // wit1 = witness :: 1 -> 2^512
865        //
866        // # Constants
867        // const1 = word_jet 0x00000000000000000000003b78ce563f89a0ed9414f5aa28ad0d96d6795f9c63 :: 1 -> 2^256 # cmr a9e3dbca...
868        //
869        // # Program code
870        // id1 = iden                 :: (2^256 * 1) -> (2^256 * 1)     # cmr dbfefcfc...
871        // jt2 = jet_sig_all_hash     :: 1 -> 2^256                     # cmr 9902bc0f...
872        // disc3 = disconnect id1 jt2 :: 1 -> 2^512                     # cmr 6968f10e...
873        // pr4 = pair const1 disc3    :: 1 -> (2^256 * 2^512)           # cmr 378ad609...
874        // pr5 = pair pr4 wit1        :: 1 -> ((2^256 * 2^512) * 2^512) # cmr 0d51ff00...
875        // jt6 = jet_check_sig_verify :: ((2^256 * 2^512) * 2^512) -> 1 # cmr 297459d8...
876        //
877        // main = comp pr5 jt6        :: 1 -> 1                         # cmr 14a5e0cc...
878        assert_program_deserializable::<crate::jet::Elements>(
879            &[
880                0xd3, 0x69, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3b, 0x78, 0xce,
881                0x56, 0x3f, 0x89, 0xa0, 0xed, 0x94, 0x14, 0xf5, 0xaa, 0x28, 0xad, 0x0d, 0x96, 0xd6, 0x79, 0x5f,
882                0x9c, 0x63, 0x47, 0x07, 0x02, 0xc0, 0xe2, 0x8d, 0x88, 0x10, 
883            ],
884            &[
885                0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3b, 0x78, 0xce, 0x56, 0x3f,
886                0x89, 0xa0, 0xed, 0x94, 0x14, 0xf5, 0xaa, 0x28, 0xad, 0x0d, 0x96, 0xd6, 0x79, 0x5f, 0x9c, 0x63,
887                0xdb, 0x86, 0x8d, 0x45, 0xa0, 0xbc, 0x1d, 0x19, 0x01, 0x30, 0x2b, 0xc8, 0x7a, 0x87, 0x1c, 0xf1,
888                0x58, 0xe2, 0xbd, 0xe2, 0xcf, 0xa6, 0x45, 0xa8, 0x95, 0xc1, 0xb4, 0x5d, 0x68, 0xea, 0x24, 0xc0, 
889            ],
890            "f3cd4537d7ebb201732203195b30b549b8dc0c2c6257b3a0d53bedb08ea02874",
891            "107fa80454ed0f2d95d7c18d307912b1497505b98de47198fee23b5018efa544",
892            "d52021c638ba742a90bead9b3055efd66091fb50bb131aa8b10eb7c13ef464d1",
893            "02kAAAAAAAAAAAAAADt4zlY/iaDtlBT1qiitDZbWeV+cY0cHAsDijYgQ",
894        );
895    }
896
897    #[test]
898    #[rustfmt::skip]
899    #[cfg(feature = "elements")]
900    fn disconnect3() {
901        // Yet another disconnect-based program that hit a bug in our AMR computation
902        // (passing left arrow in place of right arrow to the AMR constructor.)
903        // # Program code
904        // id1 = iden                 :: (2^256 * 1) -> (2^256 * 1) # cmr dbfefcfc...
905        // ut2 = unit                 :: 1 -> 1                     # cmr 62274a89...
906        // jl3 = injl ut2             :: 1 -> 2                     # cmr bd0cce93...
907        // disc4 = disconnect id1 jl3 :: 1 -> (2^256 * 2)           # cmr 6968f10e...
908        // ut5 = unit                 :: (2^256 * 2) -> 1           # cmr 62274a89...
909        // main = comp disc4 ut5      :: 1 -> 1                     # cmr a8c9cc7a...
910        assert_program_deserializable::<crate::jet::Elements>(
911            &[0xc9, 0x09, 0x20, 0x74, 0x90, 0x40],
912            &[],
913            "b689bdee289c8dd4e2e283358d187813363d441776cf826dafc27cc8a81ec441",
914            "3c68660a1afde7982ce4aa9d499ad382bc32f5f9ad894a5e915f76e66303a25b",
915            "85313720ee43ae0ee03f88b05e6d9e4494308c6897bdeb3e93b94559c3317484",
916            "yQkgdJBA",
917        );
918    }
919
920    #[test]
921    #[cfg(feature = "elements")]
922    fn decode_schnorr() {
923        #[rustfmt::skip]
924        let schnorr0 = vec![
925            0xc6, 0xd5, 0xf2, 0x61, 0x14, 0x03, 0x24, 0xb1, 0x86, 0x20, 0x92, 0x68, 0x9f, 0x0b, 0xf1, 0x3a,
926            0xa4, 0x53, 0x6a, 0x63, 0x90, 0x8b, 0x06, 0xdf, 0x33, 0x61, 0x0c, 0x03, 0xe2, 0x27, 0x79, 0xc0,
927            0x6d, 0xf2, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
928            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
929            0x00, 0x00, 0xe2, 0x8d, 0x8c, 0x04, 0x00,
930        ];
931        #[rustfmt::skip]
932        let schnorr0_wit = vec![
933            0xe9, 0x07, 0x83, 0x1f, 0x80, 0x84, 0x8d, 0x10, 0x69, 0xa5, 0x37, 0x1b, 0x40, 0x24, 0x10, 0x36,
934            0x4b, 0xdf, 0x1c, 0x5f, 0x83, 0x07, 0xb0, 0x08, 0x4c, 0x55, 0xf1, 0xce, 0x2d, 0xca, 0x82, 0x15,
935            0x25, 0xf6, 0x6a, 0x4a, 0x85, 0xea, 0x8b, 0x71, 0xe4, 0x82, 0xa7, 0x4f, 0x38, 0x2d, 0x2c, 0xe5,
936            0xeb, 0xee, 0xe8, 0xfd, 0xb2, 0x17, 0x2f, 0x47, 0x7d, 0xf4, 0x90, 0x0d, 0x31, 0x05, 0x36, 0xc0,
937        ];
938        assert_program_deserializable::<crate::jet::Elements>(
939            &schnorr0,
940            &schnorr0_wit,
941            "8a9e97676b24be7797d9ee0bf32dd76bcd78028e973025f785eae8dc91c8a0da",
942            "ec97c8774cb6bfb381fdbbcc8d964380fb3a3b45779322624490d6231ae777a4",
943            "ad7c38b16b9129646dc89b52cff144de94a80e383c4983b53de65e3575abcf38",
944            "xtXyYRQDJLGGIJJonwvxOqRTamOQiwbfM2EMA+InecBt8gAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA4o2MBAA=",
945        );
946    }
947
948    #[cfg(feature = "elements")]
949    fn assert_correct_pruning<J: Jet>(
950        unpruned_prog: &str,
951        unpruned_wit: &HashMap<Arc<str>, Value>,
952        expected_pruned_prog: &str,
953        expected_pruned_wit: &HashMap<Arc<str>, Value>,
954        env: &J::Environment,
955    ) {
956        let unpruned_program = types::Context::with_context(|ctx| {
957            Forest::<J>::parse(unpruned_prog)
958                .expect("unpruned program should parse")
959                .to_witness_node(&ctx, unpruned_wit)
960                .expect("unpruned program should have main")
961                .finalize_unpruned()
962                .expect("unpruned program should finalize")
963        });
964        let expected_unpruned_program = types::Context::with_context(|ctx| {
965            Forest::<J>::parse(expected_pruned_prog)
966                .expect("expected pruned program should parse")
967                .to_witness_node(&ctx, expected_pruned_wit)
968                .expect("expected pruned program should have main")
969                .finalize_unpruned()
970                .expect("expected pruned program should finalize")
971        });
972
973        let mut mac = BitMachine::for_program(&unpruned_program)
974            .expect("unpruned program has reasonable bounds");
975        let unpruned_output = mac
976            .exec(&unpruned_program, env)
977            .expect("unpruned program should run without failure");
978
979        let pruned_program = unpruned_program
980            .prune(env)
981            .expect("pruning should not fail if execution succeeded");
982        assert_eq!(
983            pruned_program.ihr(),
984            expected_unpruned_program.ihr(),
985            "pruning result differs from expected result"
986        );
987
988        let mut mac =
989            BitMachine::for_program(&pruned_program).expect("pruned program has reasonable bounds");
990        let pruned_output = mac
991            .exec(&pruned_program, env)
992            .expect("pruned program should run without failure");
993        assert_eq!(
994            unpruned_output, pruned_output,
995            "pruned program should return same output as unpruned program"
996        );
997    }
998
999    #[test]
1000    #[cfg(feature = "elements")]
1001    fn prune() {
1002        let env = crate::jet::elements::ElementsEnv::dummy();
1003
1004        /*
1005         * 1) Prune a product type / value
1006         */
1007        let unpruned_prog = r#"wit1 := witness : 1 -> 2
1008wit2 := witness : 1 -> 2^64 * 2^64
1009input := pair wit1 wit2 : 1 -> 2 * (2^64 * 2^64)
1010process := case (drop take jet_is_zero_64) (drop drop jet_is_zero_64) : 2 * (2^64 * 2^64) -> 2
1011main := comp input comp process jet_verify : 1 -> 1"#;
1012
1013        // 1.1) Prune right
1014        let unpruned_wit = HashMap::from([
1015            (Arc::from("wit1"), Value::u1(0)),
1016            (
1017                Arc::from("wit2"),
1018                Value::product(Value::u64(0), Value::u64(0)),
1019            ),
1020        ]);
1021        let pruned_prog = r#"wit1 := witness : 1 -> 2
1022wit2 := witness : 1 -> 2^64 * 1 -- right component was pruned
1023input := pair wit1 wit2 : 1 -> 2 * (2^64 * 1)
1024process := assertl (drop take jet_is_zero_64) #{drop drop jet_is_zero_64} : 2 * (2^64 * 1) -> 2 -- case became assertl
1025main := comp input comp process jet_verify : 1 -> 1"#;
1026        let pruned_wit = HashMap::from([
1027            (Arc::from("wit1"), Value::u1(0)),
1028            (
1029                Arc::from("wit2"),
1030                Value::product(Value::u64(0), Value::unit()),
1031            ),
1032        ]);
1033        assert_correct_pruning::<crate::jet::Elements>(
1034            unpruned_prog,
1035            &unpruned_wit,
1036            pruned_prog,
1037            &pruned_wit,
1038            &env,
1039        );
1040
1041        // 1.2) Prune left
1042        let unpruned_wit = HashMap::from([
1043            (Arc::from("wit1"), Value::u1(1)),
1044            (
1045                Arc::from("wit2"),
1046                Value::product(Value::u64(0), Value::u64(0)),
1047            ),
1048        ]);
1049        let pruned_prog = r#"wit1 := witness : 1 -> 2
1050wit2 := witness : 1 -> 1 * 2^64 -- left component was pruned
1051input := pair wit1 wit2 : 1 -> 2 * (1 * 2^64)
1052process := assertr #{drop take jet_is_zero_64} (drop drop jet_is_zero_64) : 2 * (1 * 2^64) -> 2 -- case became assertr
1053main := comp input comp process jet_verify : 1 -> 1"#;
1054        let pruned_wit = HashMap::from([
1055            (Arc::from("wit1"), Value::u1(1)),
1056            (
1057                Arc::from("wit2"),
1058                Value::product(Value::unit(), Value::u64(0)),
1059            ),
1060        ]);
1061        assert_correct_pruning::<crate::jet::Elements>(
1062            unpruned_prog,
1063            &unpruned_wit,
1064            pruned_prog,
1065            &pruned_wit,
1066            &env,
1067        );
1068
1069        /*
1070         * 1) Prune a sum type / value
1071         */
1072        let prune_sum = r#"wit1 := witness : 1 -> 2^64 + 2^64
1073input := pair wit1 unit : 1 -> (2^64 + 2^64) * 1
1074process := case (take jet_is_zero_64) (take jet_is_zero_64) : (2^64 + 2^64) * 1 -> 2
1075main := comp input comp process jet_verify : 1 -> 1"#;
1076
1077        // 1.1) Prune right
1078        let unpruned_wit =
1079            HashMap::from([(Arc::from("wit1"), Value::left(Value::u64(0), Final::u64()))]);
1080        let pruned_prog = r#"wit1 := witness : 1 -> 2^64 + 1 -- right sub type became unit
1081input := pair wit1 unit : 1 -> (2^64 + 1) * 1
1082process := assertl (take jet_is_zero_64) #{take jet_is_zero_64} : (2^64 + 1) * 1 -> 2 -- case became assertl
1083main := comp input comp process jet_verify : 1 -> 1"#;
1084        let pruned_wit =
1085            HashMap::from([(Arc::from("wit1"), Value::left(Value::u64(0), Final::unit()))]);
1086        assert_correct_pruning::<crate::jet::Elements>(
1087            prune_sum,
1088            &unpruned_wit,
1089            pruned_prog,
1090            &pruned_wit,
1091            &env,
1092        );
1093
1094        // 1.2) Prune left
1095        let unpruned_wit = HashMap::from([(
1096            Arc::from("wit1"),
1097            Value::right(Final::unit(), Value::u64(0)),
1098        )]);
1099        let pruned_prog = r#"wit1 := witness : 1 -> 1 + 2^64 -- left sub type became unit
1100input := pair wit1 unit : 1 -> (1 + 2^64) * 1
1101process := assertr #{take jet_is_zero_64} (take jet_is_zero_64) : (1 + 2^64) * 1 -> 2 -- case became assertr
1102main := comp input comp process jet_verify : 1 -> 1"#;
1103        let pruned_wit = HashMap::from([(
1104            Arc::from("wit1"),
1105            Value::right(Final::unit(), Value::u64(0)),
1106        )]);
1107        assert_correct_pruning::<crate::jet::Elements>(
1108            prune_sum,
1109            &unpruned_wit,
1110            pruned_prog,
1111            &pruned_wit,
1112            &env,
1113        );
1114    }
1115}
1116
1117#[cfg(bench)]
1118#[cfg(feature = "elements")]
1119mod benches {
1120    use super::*;
1121
1122    use crate::bit_encoding::BitIter;
1123    use crate::jet::Elements;
1124
1125    use test::{black_box, Bencher};
1126
1127    #[bench]
1128    fn decode_fixed_program(bh: &mut Bencher) {
1129        let prog = &[
1130            0xd3, 0x69, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3b,
1131            0x78, 0xce, 0x56, 0x3f, 0x89, 0xa0, 0xed, 0x94, 0x14, 0xf5, 0xaa, 0x28, 0xad, 0x0d,
1132            0x96, 0xd6, 0x79, 0x5f, 0x9c, 0x63, 0x47, 0x07, 0x02, 0xc0, 0xe2, 0x8d, 0x88, 0x10,
1133        ][..];
1134        let witness = &[
1135            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3b, 0x78, 0xce,
1136            0x56, 0x3f, 0x89, 0xa0, 0xed, 0x94, 0x14, 0xf5, 0xaa, 0x28, 0xad, 0x0d, 0x96, 0xd6,
1137            0x79, 0x5f, 0x9c, 0x63, 0xdb, 0x86, 0x8d, 0x45, 0xa0, 0xbc, 0x1d, 0x19, 0x01, 0x30,
1138            0x2b, 0xc8, 0x7a, 0x87, 0x1c, 0xf1, 0x58, 0xe2, 0xbd, 0xe2, 0xcf, 0xa6, 0x45, 0xa8,
1139            0x95, 0xc1, 0xb4, 0x5d, 0x68, 0xea, 0x24, 0xc0,
1140        ][..];
1141        bh.iter(|| {
1142            let prog = BitIter::from(prog);
1143            let witness = BitIter::from(witness);
1144            black_box(RedeemNode::<Elements>::decode(
1145                black_box(prog),
1146                black_box(witness),
1147            ))
1148            .unwrap();
1149        });
1150    }
1151}