Skip to main content

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