simplicity/node/
construct.rs

1// SPDX-License-Identifier: CC0-1.0
2
3use crate::dag::{InternalSharing, PostOrderIterItem};
4use crate::jet::Jet;
5use crate::types::{self, arrow::Arrow};
6use crate::{encode, BitIter, BitWriter, Cmr, FailEntropy, RedeemNode, Value, Word};
7
8use std::io;
9use std::marker::PhantomData;
10use std::sync::Arc;
11
12use super::{
13    Commit, CommitData, CommitNode, Converter, Inner, Marker, NoDisconnect, NoWitness, Node,
14    Redeem, RedeemData,
15};
16use super::{CoreConstructible, DisconnectConstructible, JetConstructible, WitnessConstructible};
17
18/// ID used to share [`ConstructNode`]s.
19///
20/// This is impossible to construct, which is a promise that it is impossible
21/// to share [`ConstructNode`]s.
22#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Debug, Hash)]
23pub enum ConstructId {}
24
25#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Debug, Hash)]
26pub struct Construct<J> {
27    /// Makes the type non-constructible.
28    never: std::convert::Infallible,
29    /// Required by Rust.
30    phantom: std::marker::PhantomData<J>,
31}
32
33impl<J: Jet> Marker for Construct<J> {
34    type CachedData = ConstructData<J>;
35    type Witness = Option<Value>;
36    type Disconnect = Option<Arc<ConstructNode<J>>>;
37    type SharingId = ConstructId;
38    type Jet = J;
39
40    fn compute_sharing_id(_: Cmr, _: &ConstructData<J>) -> Option<ConstructId> {
41        None
42    }
43}
44
45pub type ConstructNode<J> = Node<Construct<J>>;
46
47impl<J: Jet> ConstructNode<J> {
48    /// Accessor for the node's arrow
49    pub fn arrow(&self) -> &Arrow {
50        self.data.arrow()
51    }
52
53    /// Sets the source and target type of the node to unit
54    pub fn set_arrow_to_program(&self) -> Result<(), types::Error> {
55        let ctx = self.data.inference_context();
56        let unit_ty = types::Type::unit(ctx);
57        ctx.unify(
58            &self.arrow().source,
59            &unit_ty,
60            "setting root source to unit",
61        )?;
62        ctx.unify(
63            &self.arrow().target,
64            &unit_ty,
65            "setting root target to unit",
66        )?;
67        Ok(())
68    }
69
70    /// Convert a [`ConstructNode`] to a [`CommitNode`] by finalizing all of the types.
71    ///
72    /// Also sets the source and target type of this node to unit. This is almost
73    /// certainly what you want, since the resulting `CommitNode` cannot be further
74    /// composed, and needs to be 1->1 to go on-chain. But if you don't, call
75    /// [`Self::finalize_types_non_program`] instead.
76    pub fn finalize_types(&self) -> Result<Arc<CommitNode<J>>, crate::Error> {
77        self.set_arrow_to_program()?;
78        self.finalize_types_non_program()
79    }
80
81    /// Convert a [`ConstructNode`] to a [`CommitNode`] by finalizing all of the types.
82    ///
83    /// Does *not* sets the source and target type of this node to unit.
84    pub fn finalize_types_non_program(&self) -> Result<Arc<CommitNode<J>>, crate::Error> {
85        struct FinalizeTypes<J: Jet>(PhantomData<J>);
86
87        impl<J: Jet> Converter<Construct<J>, Commit<J>> for FinalizeTypes<J> {
88            type Error = crate::Error;
89            fn convert_witness(
90                &mut self,
91                _: &PostOrderIterItem<&ConstructNode<J>>,
92                _: &Option<Value>,
93            ) -> Result<NoWitness, Self::Error> {
94                Ok(NoWitness)
95            }
96
97            fn convert_disconnect(
98                &mut self,
99                _: &PostOrderIterItem<&ConstructNode<J>>,
100                _: Option<&Arc<CommitNode<J>>>,
101                _: &Option<Arc<ConstructNode<J>>>,
102            ) -> Result<NoDisconnect, Self::Error> {
103                Ok(NoDisconnect)
104            }
105
106            fn convert_data(
107                &mut self,
108                data: &PostOrderIterItem<&ConstructNode<J>>,
109                inner: Inner<&Arc<CommitNode<J>>, J, &NoDisconnect, &NoWitness>,
110            ) -> Result<Arc<CommitData<J>>, Self::Error> {
111                let converted_data = inner.map(|node| node.cached_data());
112                CommitData::new(&data.node.data.arrow, converted_data)
113                    .map(Arc::new)
114                    .map_err(crate::Error::from)
115            }
116        }
117
118        self.convert::<InternalSharing, _, _>(&mut FinalizeTypes(PhantomData))
119    }
120
121    /// Finalize the witness program as an unpruned redeem program.
122    ///
123    /// Witness nodes must be populated with sufficient data,
124    /// to ensure that the resulting redeem program successfully runs on the Bit Machine.
125    /// Furthermore, **all** disconnected branches must be populated,
126    /// even those that are not executed.
127    ///
128    /// The resulting redeem program is **not pruned**.
129    ///
130    /// ## See
131    ///
132    /// [`RedeemNode::prune`]
133    pub fn finalize_unpruned(&self) -> Result<Arc<RedeemNode<J>>, crate::Error> {
134        struct Finalizer<J>(PhantomData<J>);
135
136        impl<J: Jet> Converter<Construct<J>, Redeem<J>> for Finalizer<J> {
137            type Error = crate::Error;
138
139            fn convert_witness(
140                &mut self,
141                data: &PostOrderIterItem<&ConstructNode<J>>,
142                wit: &Option<Value>,
143            ) -> Result<Value, Self::Error> {
144                if let Some(ref wit) = wit {
145                    Ok(wit.shallow_clone())
146                } else {
147                    // We insert a zero value into unpopulated witness nodes,
148                    // assuming that this node will later be pruned out of the program.
149                    //
150                    // Pruning requires running a program on the Bit Machine,
151                    // which in turn requires a program with fully populated witness nodes.
152                    // It would be horrible UX to force the caller to provide witness data
153                    // even for unexecuted branches, so we insert zero values here.
154                    //
155                    // If this node is executed after all, then the caller can fix the witness
156                    // data based on the returned execution error.
157                    //
158                    // Zero values may "accidentally" satisfy a program even if the caller
159                    // didn't provide any witness data. However, this is only the case for the
160                    // most trivial programs. The only place where we must be careful is our
161                    // unit tests, which tend to include these kinds of trivial programs.
162                    let ty = data.node.arrow().target.finalize()?;
163                    Ok(Value::zero(&ty))
164                }
165            }
166
167            fn convert_disconnect(
168                &mut self,
169                _: &PostOrderIterItem<&ConstructNode<J>>,
170                maybe_converted: Option<&Arc<RedeemNode<J>>>,
171                _: &Option<Arc<ConstructNode<J>>>,
172            ) -> Result<Arc<RedeemNode<J>>, Self::Error> {
173                if let Some(child) = maybe_converted {
174                    Ok(Arc::clone(child))
175                } else {
176                    Err(crate::Error::DisconnectRedeemTime)
177                }
178            }
179
180            fn convert_data(
181                &mut self,
182                data: &PostOrderIterItem<&ConstructNode<J>>,
183                inner: Inner<&Arc<RedeemNode<J>>, J, &Arc<RedeemNode<J>>, &Value>,
184            ) -> Result<Arc<RedeemData<J>>, Self::Error> {
185                let converted_data = inner
186                    .map(|node| node.cached_data())
187                    .map_disconnect(|node| node.cached_data())
188                    .map_witness(Value::shallow_clone);
189                Ok(Arc::new(RedeemData::new(
190                    data.node.arrow().finalize()?,
191                    converted_data,
192                )))
193            }
194        }
195
196        self.convert::<InternalSharing, _, _>(&mut Finalizer(PhantomData))
197    }
198
199    /// Finalize the witness program as a pruned redeem program.
200    ///
201    /// Witness nodes must be populated with sufficient data,
202    /// to ensure that the resulting redeem program successfully runs on the Bit Machine.
203    /// Furthermore, **all** disconnected branches must be populated,
204    /// even those that are not executed.
205    ///
206    /// The resulting redeem program is **pruned** based on the given transaction environment.
207    ///
208    /// ## See
209    ///
210    /// [`RedeemNode::prune`]
211    pub fn finalize_pruned(
212        &self,
213        env: &J::Environment,
214    ) -> Result<Arc<RedeemNode<J>>, crate::Error> {
215        let unpruned = self.finalize_unpruned()?;
216        unpruned.prune(env).map_err(crate::Error::Execution)
217    }
218
219    /// Decode a Simplicity expression from bits, without witness data.
220    ///
221    /// # Usage
222    ///
223    /// Use this method only if the serialization **does not** include the witness data.
224    /// This means, the program simply has no witness during commitment,
225    /// or the witness is provided by other means.
226    ///
227    /// If the serialization contains the witness data, then use [`crate::RedeemNode::decode()`].
228    pub fn decode<I: Iterator<Item = u8>>(
229        mut bits: BitIter<I>,
230    ) -> Result<Arc<Self>, crate::decode::Error> {
231        let res = crate::decode::decode_expression(&mut bits)?;
232        bits.close()?;
233        Ok(res)
234    }
235
236    /// Encode a Simplicity expression to bits, with no witness data
237    pub fn encode<W: io::Write>(&self, w: &mut BitWriter<W>) -> io::Result<usize> {
238        let program_bits = encode::encode_program(self, w)?;
239        w.flush_all()?;
240        Ok(program_bits)
241    }
242}
243
244#[derive(Clone, Debug)]
245pub struct ConstructData<J> {
246    arrow: Arrow,
247    /// This isn't really necessary, but it helps type inference if every
248    /// struct has a \<J\> parameter, since it forces the choice of jets to
249    /// be consistent without the user needing to specify it too many times.
250    phantom: PhantomData<J>,
251}
252
253impl<J: Jet> ConstructData<J> {
254    /// Constructs a new [`ConstructData`] from an (unfinalized) type arrow
255    pub fn new(arrow: Arrow) -> Self {
256        ConstructData {
257            arrow,
258            phantom: PhantomData,
259        }
260    }
261
262    /// Accessor for the node's arrow
263    pub fn arrow(&self) -> &Arrow {
264        &self.arrow
265    }
266}
267
268impl<J> CoreConstructible for ConstructData<J> {
269    fn iden(inference_context: &types::Context) -> Self {
270        ConstructData {
271            arrow: Arrow::iden(inference_context),
272            phantom: PhantomData,
273        }
274    }
275
276    fn unit(inference_context: &types::Context) -> Self {
277        ConstructData {
278            arrow: Arrow::unit(inference_context),
279            phantom: PhantomData,
280        }
281    }
282
283    fn injl(child: &Self) -> Self {
284        ConstructData {
285            arrow: Arrow::injl(&child.arrow),
286            phantom: PhantomData,
287        }
288    }
289
290    fn injr(child: &Self) -> Self {
291        ConstructData {
292            arrow: Arrow::injr(&child.arrow),
293            phantom: PhantomData,
294        }
295    }
296
297    fn take(child: &Self) -> Self {
298        ConstructData {
299            arrow: Arrow::take(&child.arrow),
300            phantom: PhantomData,
301        }
302    }
303
304    fn drop_(child: &Self) -> Self {
305        ConstructData {
306            arrow: Arrow::drop_(&child.arrow),
307            phantom: PhantomData,
308        }
309    }
310
311    fn comp(left: &Self, right: &Self) -> Result<Self, types::Error> {
312        Ok(ConstructData {
313            arrow: Arrow::comp(&left.arrow, &right.arrow)?,
314            phantom: PhantomData,
315        })
316    }
317
318    fn case(left: &Self, right: &Self) -> Result<Self, types::Error> {
319        Ok(ConstructData {
320            arrow: Arrow::case(&left.arrow, &right.arrow)?,
321            phantom: PhantomData,
322        })
323    }
324
325    fn assertl(left: &Self, right: Cmr) -> Result<Self, types::Error> {
326        Ok(ConstructData {
327            arrow: Arrow::assertl(&left.arrow, right)?,
328            phantom: PhantomData,
329        })
330    }
331
332    fn assertr(left: Cmr, right: &Self) -> Result<Self, types::Error> {
333        Ok(ConstructData {
334            arrow: Arrow::assertr(left, &right.arrow)?,
335            phantom: PhantomData,
336        })
337    }
338
339    fn pair(left: &Self, right: &Self) -> Result<Self, types::Error> {
340        Ok(ConstructData {
341            arrow: Arrow::pair(&left.arrow, &right.arrow)?,
342            phantom: PhantomData,
343        })
344    }
345
346    fn fail(inference_context: &types::Context, entropy: FailEntropy) -> Self {
347        ConstructData {
348            arrow: Arrow::fail(inference_context, entropy),
349            phantom: PhantomData,
350        }
351    }
352
353    fn const_word(inference_context: &types::Context, word: Word) -> Self {
354        ConstructData {
355            arrow: Arrow::const_word(inference_context, word),
356            phantom: PhantomData,
357        }
358    }
359
360    fn inference_context(&self) -> &types::Context {
361        self.arrow.inference_context()
362    }
363}
364
365impl<J: Jet> DisconnectConstructible<Option<Arc<ConstructNode<J>>>> for ConstructData<J> {
366    fn disconnect(
367        left: &Self,
368        right: &Option<Arc<ConstructNode<J>>>,
369    ) -> Result<Self, types::Error> {
370        let right = right.as_ref();
371        Ok(ConstructData {
372            arrow: Arrow::disconnect(&left.arrow, &right.map(|n| n.arrow()))?,
373            phantom: PhantomData,
374        })
375    }
376}
377
378impl<J> WitnessConstructible<Option<Value>> for ConstructData<J> {
379    fn witness(inference_context: &types::Context, _witness: Option<Value>) -> Self {
380        ConstructData {
381            arrow: Arrow::witness(inference_context, NoWitness),
382            phantom: PhantomData,
383        }
384    }
385}
386
387impl<J: Jet> JetConstructible<J> for ConstructData<J> {
388    fn jet(inference_context: &types::Context, jet: J) -> Self {
389        ConstructData {
390            arrow: Arrow::jet(inference_context, jet),
391            phantom: PhantomData,
392        }
393    }
394}
395
396#[cfg(test)]
397mod tests {
398    use super::*;
399    use crate::jet::Core;
400    use crate::types::Final;
401    use crate::Value;
402
403    #[test]
404    fn occurs_check_error() {
405        let ctx = types::Context::new();
406        let iden = Arc::<ConstructNode<Core>>::iden(&ctx);
407        let node = Arc::<ConstructNode<Core>>::disconnect(&iden, &Some(Arc::clone(&iden))).unwrap();
408
409        assert!(matches!(
410            node.finalize_types_non_program(),
411            Err(crate::Error::Type(types::Error::OccursCheck { .. })),
412        ));
413    }
414
415    #[test]
416    fn occurs_check_2() {
417        let ctx = types::Context::new();
418        // A more complicated occurs-check test that caused a deadlock in the past.
419        let iden = Arc::<ConstructNode<Core>>::iden(&ctx);
420        let injr = Arc::<ConstructNode<Core>>::injr(&iden);
421        let pair = Arc::<ConstructNode<Core>>::pair(&injr, &iden).unwrap();
422        let drop = Arc::<ConstructNode<Core>>::drop_(&pair);
423
424        let case1 = Arc::<ConstructNode<Core>>::case(&drop, &drop).unwrap();
425        let case2 = Arc::<ConstructNode<Core>>::case(&case1, &case1).unwrap();
426
427        let comp1 = Arc::<ConstructNode<Core>>::comp(&case2, &case2).unwrap();
428        let comp2 = Arc::<ConstructNode<Core>>::comp(&comp1, &case1).unwrap();
429
430        assert!(matches!(
431            comp2.finalize_types_non_program(),
432            Err(crate::Error::Type(types::Error::OccursCheck { .. })),
433        ));
434    }
435
436    #[test]
437    fn occurs_check_3() {
438        let ctx = types::Context::new();
439        // A similar example that caused a slightly different deadlock in the past.
440        let wit = Arc::<ConstructNode<Core>>::witness(&ctx, None);
441        let drop = Arc::<ConstructNode<Core>>::drop_(&wit);
442
443        let comp1 = Arc::<ConstructNode<Core>>::comp(&drop, &drop).unwrap();
444        let comp2 = Arc::<ConstructNode<Core>>::comp(&comp1, &comp1).unwrap();
445        let comp3 = Arc::<ConstructNode<Core>>::comp(&comp2, &comp2).unwrap();
446        let comp4 = Arc::<ConstructNode<Core>>::comp(&comp3, &comp3).unwrap();
447        let comp5 = Arc::<ConstructNode<Core>>::comp(&comp4, &comp4).unwrap();
448
449        let case = Arc::<ConstructNode<Core>>::case(&comp5, &comp4).unwrap();
450        let drop2 = Arc::<ConstructNode<Core>>::drop_(&case);
451        let case2 = Arc::<ConstructNode<Core>>::case(&drop2, &case).unwrap();
452        let comp6 = Arc::<ConstructNode<Core>>::comp(&case2, &case2).unwrap();
453        let case3 = Arc::<ConstructNode<Core>>::case(&comp6, &comp6).unwrap();
454
455        let comp7 = Arc::<ConstructNode<Core>>::comp(&case3, &case3).unwrap();
456        let comp8 = Arc::<ConstructNode<Core>>::comp(&comp7, &comp7).unwrap();
457
458        assert!(matches!(
459            comp8.finalize_types_non_program(),
460            Err(crate::Error::Type(types::Error::OccursCheck { .. })),
461        ));
462    }
463
464    #[test]
465    fn type_check_error() {
466        let ctx = types::Context::new();
467        let unit = Arc::<ConstructNode<Core>>::unit(&ctx);
468        let case = Arc::<ConstructNode<Core>>::case(&unit, &unit).unwrap();
469
470        assert!(matches!(
471            Arc::<ConstructNode<Core>>::disconnect(&case, &Some(unit)),
472            Err(types::Error::Bind { .. }),
473        ));
474    }
475
476    #[test]
477    fn scribe() {
478        // Ok to use same type inference context for all the below tests,
479        // since everything has concrete types and anyway we only care
480        // about CMRs, for which type inference is irrelevant.
481        let ctx = types::Context::new();
482        let unit = Arc::<ConstructNode<Core>>::unit(&ctx);
483        let bit0 = Arc::<ConstructNode<Core>>::const_word(&ctx, Word::u1(0));
484        let bit1 = Arc::<ConstructNode<Core>>::const_word(&ctx, Word::u1(1));
485
486        assert_eq!(
487            unit.cmr(),
488            Arc::<ConstructNode<Core>>::scribe(&ctx, &Value::unit()).cmr()
489        );
490        assert_eq!(
491            bit0.cmr(),
492            Arc::<ConstructNode<Core>>::scribe(&ctx, &Value::u1(0)).cmr()
493        );
494        assert_eq!(
495            bit1.cmr(),
496            Arc::<ConstructNode<Core>>::scribe(&ctx, &Value::u1(1)).cmr()
497        );
498        assert_eq!(
499            Arc::<ConstructNode<Core>>::const_word(&ctx, Word::u2(1)).cmr(),
500            Arc::<ConstructNode<Core>>::scribe(&ctx, &Value::u2(1)).cmr()
501        );
502        assert_eq!(
503            Arc::<ConstructNode<Core>>::injl(&bit0).cmr(),
504            Arc::<ConstructNode<Core>>::scribe(&ctx, &Value::left(Value::u1(0), Final::unit()))
505                .cmr()
506        );
507        assert_eq!(
508            Arc::<ConstructNode<Core>>::injr(&bit1).cmr(),
509            Arc::<ConstructNode<Core>>::scribe(&ctx, &Value::right(Final::unit(), Value::u1(1)))
510                .cmr()
511        );
512        assert_eq!(
513            Arc::<ConstructNode<Core>>::pair(&unit, &unit)
514                .unwrap()
515                .cmr(),
516            Arc::<ConstructNode<Core>>::scribe(&ctx, &Value::product(Value::unit(), Value::unit()))
517                .cmr()
518        );
519    }
520
521    #[test]
522    fn regression_286_1() {
523        // This is the smallest pure Simplicity program I was able to find that exhibits the bad
524        // behavior seen in https://github.com/BlockstreamResearch/rust-simplicity/issues/286
525        let ctx = types::Context::new();
526        let cmr = Cmr::from_byte_array([0xde; 32]);
527
528        let u0 = Arc::<ConstructNode<Core>>::unit(&ctx);
529        let i1 = Arc::<ConstructNode<Core>>::injl(&u0);
530        let i2 = Arc::<ConstructNode<Core>>::injr(&i1);
531        let i3 = Arc::<ConstructNode<Core>>::injr(&i2);
532        let i4 = Arc::<ConstructNode<Core>>::injl(&i3);
533        let u5 = Arc::<ConstructNode<Core>>::unit(&ctx);
534        let i6 = Arc::<ConstructNode<Core>>::injl(&u5);
535        let i7 = Arc::<ConstructNode<Core>>::injr(&i6);
536        let p8 = Arc::<ConstructNode<Core>>::pair(&i4, &i7).unwrap();
537        let u9 = Arc::<ConstructNode<Core>>::unit(&ctx);
538        let a10 = Arc::<ConstructNode<Core>>::assertr(cmr, &u9).unwrap();
539        let u11 = Arc::<ConstructNode<Core>>::unit(&ctx);
540        let a12 = Arc::<ConstructNode<Core>>::assertr(cmr, &u11).unwrap();
541        let a13 = Arc::<ConstructNode<Core>>::assertl(&a12, cmr).unwrap();
542        let c14 = Arc::<ConstructNode<Core>>::case(&a10, &a13).unwrap();
543        let c15 = Arc::<ConstructNode<Core>>::comp(&p8, &c14).unwrap();
544
545        let finalized: Arc<CommitNode<_>> = c15.finalize_types().unwrap();
546        let prog = finalized.encode_to_vec();
547        // In #286 we are encoding correctly...
548        assert_eq!(
549            hex::DisplayHex::as_hex(&prog).to_string(),
550            "dc920a28812b6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f243090e00b10e00680",
551        );
552
553        let prog = BitIter::from(prog);
554        let decode = CommitNode::<Core>::decode(prog).unwrap();
555
556        // ...but then during decoding we read the program incorrectly and this assertion fails.
557        assert_eq!(finalized, decode);
558    }
559
560    #[test]
561    fn regression_286_2() {
562        use crate::types::Error as TypeError;
563        use crate::Error;
564
565        // This one is smaller because it starts with a witness node which has a large type.
566        // This is a bit easier to grok but can't be serialized as a complete/valid program
567        // without providing the witness data, which limits its ability to share with the
568        // other libraries.
569        //
570        // It also exhibits the bug earlier than the other one -- it *should* just fail to
571        // typecheck and not be constructible. So we can't get an encoding of it.
572        let ctx = types::Context::new();
573
574        let w0 = Arc::<ConstructNode<Core>>::witness(&ctx, None);
575        let i1 = Arc::<ConstructNode<Core>>::iden(&ctx);
576        let d2 = Arc::<ConstructNode<Core>>::drop_(&i1);
577        let i3 = Arc::<ConstructNode<Core>>::iden(&ctx);
578        let i4 = Arc::<ConstructNode<Core>>::iden(&ctx);
579        let t5 = Arc::<ConstructNode<Core>>::take(&i4);
580        let ca6 = Arc::<ConstructNode<Core>>::case(&i3, &t5).unwrap();
581        let ca7 = Arc::<ConstructNode<Core>>::case(&d2, &ca6).unwrap();
582        let c8 = Arc::<ConstructNode<Core>>::comp(&w0, &ca7).unwrap();
583        let u9 = Arc::<ConstructNode<Core>>::unit(&ctx);
584        let c10 = Arc::<ConstructNode<Core>>::comp(&c8, &u9).unwrap();
585
586        // In #286 we incorrectly succeed finalizing the types, and then encode a bad program.
587        let err = c10.finalize_types().unwrap_err();
588        assert!(matches!(err, Error::Type(TypeError::OccursCheck { .. })));
589    }
590}