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