1use 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 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 pub fn amr(&self) -> Amr {
158 self.data.amr
159 }
160
161 pub fn ihr(&self) -> Ihr {
163 self.data.ihr
164 }
165
166 pub fn arrow(&self) -> &FinalArrow {
168 &self.data.arrow
169 }
170
171 pub fn bounds(&self) -> NodeBounds {
173 self.data.bounds
174 }
175
176 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 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 pub fn prune<JE: JetEnvironment>(&self, env: &JE) -> Result<Arc<RedeemNode>, ExecutionError> {
284 self.prune_with_tracker(env, &mut SetTracker::default())
285 }
286
287 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 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 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), }
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 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 let mut mac = BitMachine::for_program(self)?;
426 mac.exec_with_tracker(self, env, tracker)?;
427
428 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 Ok(pruned_witness_program
446 .convert::<InternalSharing, _, _>(&mut Finalizer)
447 .expect("finalization is infallible"))
448 })
449 }
450
451 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 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 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 construct.convert::<InternalSharing, _, _>(&mut DecodeFinalizer { bits: &mut witness })
524 })?;
525
526 witness
528 .close()
529 .map_err(crate::decode::Error::BitIter)
530 .map_err(DecodeError::Decode)?;
531
532 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)] 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 #[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 #[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 => {} 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 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 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 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 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 ))) => {} 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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}