-
Notifications
You must be signed in to change notification settings - Fork 12
/
mod.rs
704 lines (631 loc) · 26.2 KB
/
mod.rs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
// SPDX-License-Identifier: CC0-1.0
//! Simplicity Program Nodes
//!
//! The types in this module are used to represent Simplicity expressions as
//! DAGs. The nodes of this DAG are individual combinators along with some
//! cached data about the node, which depends on the specific node type.
//!
//! All nodes represent the root of an expression. Expressions whose source
//! and target types are both unit are called "programs". Generally speaking,
//! any nodes that users hold directly will be programs.
//!
//! There are three main node types:
//!
//! 1. [`crate::RedeemNode`] represents a Simplicity node as it exists on the blockchain.
//! Every witness node is populated with a correctly-typed [`Value`], every
//! disconnect node has a child, every non-executed branch of a `case` combinator
//! is pruned, and there is nothing you can do to modify the expression.
//!
//! `RedeemNode`s can be executed on the bit machine and have complete types
//! and resource bounds, and can be serialized in the consensus bit-encoding.
//!
//! 2. [`CommitNode`] represents a Simplicity node as it is *committed to* on
//! the blockchain. This means that witness and (TODO) disconnect nodes are not
//! populated, but type inference is complete and resource bounds are
//! available. `case` combinators may have pruned children (in which case they
//! are instead considered `assertl` or `assertr` combinators), or not.
//!
//! There is a bit-encoding for `CommitNode`s which is essentially only used
//! by this library. It consists of the bit-encoding.of the combinators, fully
//! shared *except* that witness and disconnect nodes (and their ancestors)
//! are unshared. No witness data is included.
//!
//! TODO there is also a human-readable encoding.
//!
//! 3. [`ConstructNode`] represents an "under-construction" Simplicity expression.
//! These nodes' types are not necessarily complete, and are inferred as the
//! program is constructed. This is the only node that you can programmatically
//! construct. It has no encoding, human-readable or bitwise, and is intended
//! to exist only ephemerally.
//!
//! The following conversions are possible between the node types:
//!
//! 1. [`ConstructNode::finalize_types`] converts a [`ConstructNode`] to a
//! [`CommitNode`] by setting any free variables, as well as the source and
//! target of the root node, to unit.
//!
//! This conversion requires no input from the user but may fail with a type
//! error, in case of infinitely-sized types or in the case that the unit
//! bounds cannot be applied.
//!
//! 2. [`CommitNode::finalize`] converts a [`CommitNode`] to a [`RedeemNode`]
//! by attaching witnesses to each witness node, and deciding whether to hide
//! branches for each `case` node.
//!
//! 3. [`CommitNode::unfinalize_types`] converts a [`CommitNode`] to a
//! [`ConstructNode`] by throwing away all types and re-inferring them. It
//! cannot fail.
//!
//! 4. [`RedeemNode::unfinalize`] converts a [`RedeemNode`] to a [`CommitNode`]
//! by throwing away witness and (TODO) disconnect data. It cannot recover
//! pruned branches so is of limited usefulness, but it is included for
//! completeness.
//!
use crate::dag::{DagLike, MaxSharing, NoSharing, SharingTracker};
use crate::jet::Jet;
use crate::{types, Cmr, FailEntropy, Value};
use std::sync::Arc;
use std::{fmt, hash};
mod commit;
mod construct;
mod convert;
mod disconnect;
mod display;
mod inner;
mod redeem;
mod witness;
pub use commit::{Commit, CommitData, CommitNode};
pub use construct::{Construct, ConstructData, ConstructNode};
pub use convert::{Converter, Hide, SimpleFinalizer};
pub use disconnect::{Disconnectable, NoDisconnect};
use display::DisplayExpr;
pub use inner::Inner;
pub use redeem::{Redeem, RedeemData, RedeemNode};
pub use witness::{Witness, WitnessData, WitnessNode};
// This trait should only be implemented on empty types, so we can demand
// every trait bound under the sun. Doing so will make #[derive]s easier
// for downstream users.
pub trait Marker:
Copy + Clone + PartialEq + Eq + PartialOrd + Ord + fmt::Debug + hash::Hash
{
/// Precomputed data about the node, such as its type arrow or various Merkle roots.
type CachedData: Clone;
/// Type of witness data attached to DAGs of this node type. Typically either [`Value`]
/// or [`NoWitness`].
type Witness: Clone;
/// Type of disconnect data attached to DAGs of this node type.
type Disconnect: Disconnectable<Node<Self>> + Clone;
/// A type which uniquely identifies a node, for purposes of sharing
/// during iteration over the DAG.
type SharingId: hash::Hash + Clone + Eq;
/// The jet catalogue used with this node type.
type Jet: Jet;
/// Yields the sharing ID for a given type, starting from its CMR and its cached data.
///
/// If the type cannot be uniquely identified (e.g. because it is missing data), then
/// this method returns `None`. In this case, the node will not be shared with any
/// other node.
fn compute_sharing_id(cmr: Cmr, cached_data: &Self::CachedData) -> Option<Self::SharingId>;
}
/// Null data type used as dummy for [`Marker::Witness`]
#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Debug, Hash)]
pub struct NoWitness;
pub trait Constructible<J, X, W>:
JetConstructible<J>
+ DisconnectConstructible<X>
+ WitnessConstructible<W>
+ CoreConstructible
+ Sized
{
fn from_inner(
inference_context: &types::Context,
inner: Inner<&Self, J, &X, W>,
) -> Result<Self, types::Error> {
match inner {
Inner::Iden => Ok(Self::iden(inference_context)),
Inner::Unit => Ok(Self::unit(inference_context)),
Inner::InjL(child) => Ok(Self::injl(child)),
Inner::InjR(child) => Ok(Self::injr(child)),
Inner::Take(child) => Ok(Self::take(child)),
Inner::Drop(child) => Ok(Self::drop_(child)),
Inner::Comp(left, right) => Self::comp(left, right),
Inner::Case(left, right) => Self::case(left, right),
Inner::AssertL(left, r_cmr) => Self::assertl(left, r_cmr),
Inner::AssertR(l_cmr, right) => Self::assertr(l_cmr, right),
Inner::Pair(left, right) => Self::pair(left, right),
Inner::Disconnect(left, right) => Self::disconnect(left, right),
Inner::Fail(entropy) => Ok(Self::fail(inference_context, entropy)),
Inner::Word(ref w) => Ok(Self::const_word(inference_context, Arc::clone(w))),
Inner::Jet(j) => Ok(Self::jet(inference_context, j)),
Inner::Witness(w) => Ok(Self::witness(inference_context, w)),
}
}
}
impl<J, X, W, T> Constructible<J, X, W> for T where
T: DisconnectConstructible<X>
+ JetConstructible<J>
+ WitnessConstructible<W>
+ CoreConstructible
+ Sized
{
}
pub trait CoreConstructible: Sized {
fn iden(inference_context: &types::Context) -> Self;
fn unit(inference_context: &types::Context) -> Self;
fn injl(child: &Self) -> Self;
fn injr(child: &Self) -> Self;
fn take(child: &Self) -> Self;
fn drop_(child: &Self) -> Self;
fn comp(left: &Self, right: &Self) -> Result<Self, types::Error>;
fn case(left: &Self, right: &Self) -> Result<Self, types::Error>;
fn assertl(left: &Self, right: Cmr) -> Result<Self, types::Error>;
fn assertr(left: Cmr, right: &Self) -> Result<Self, types::Error>;
fn pair(left: &Self, right: &Self) -> Result<Self, types::Error>;
fn fail(inference_context: &types::Context, entropy: FailEntropy) -> Self;
fn const_word(inference_context: &types::Context, word: Arc<Value>) -> Self;
/// Accessor for the type inference context used to create the object.
fn inference_context(&self) -> &types::Context;
/// Create a DAG that takes any input and returns `value` as constant output.
///
/// _Overall type: A → B where value: B_
fn scribe(inference_context: &types::Context, value: &Value) -> Self {
let mut stack = vec![];
for data in value.post_order_iter::<NoSharing>() {
match data.node {
Value::Unit => stack.push(Self::unit(inference_context)),
Value::Left(..) => {
let child = stack.pop().unwrap();
stack.push(Self::injl(&child));
}
Value::Right(..) => {
let child = stack.pop().unwrap();
stack.push(Self::injr(&child));
}
Value::Product(..) => {
let right = stack.pop().unwrap();
let left = stack.pop().unwrap();
stack.push(
Self::pair(&left, &right).expect("source of scribe has no constraints"),
);
}
}
}
assert_eq!(stack.len(), 1);
stack.pop().unwrap()
}
/// Create a DAG that takes any input and returns bit `0` as constant output.
///
/// _Overall type: A → 2_
fn bit_false(inference_context: &types::Context) -> Self {
let unit = Self::unit(inference_context);
Self::injl(&unit)
}
/// Create a DAG that takes any input and returns bit `1` as constant output.
///
/// _Overall type: A → 2_
fn bit_true(inference_context: &types::Context) -> Self {
let unit = Self::unit(inference_context);
Self::injr(&unit)
}
/// Create a DAG that takes a bit and an input,
/// such that the `left` child is evaluated on the input if the bit is `1` _(if branch)_
/// and the `right` child is evaluated on the input otherwise _(else branch)_.
///
/// _Overall type: 2 × A → B where `left`: A → B and `right`: A → B_
///
/// _Type inference will fail if children are not of the correct type._
fn cond(left: &Self, right: &Self) -> Result<Self, types::Error> {
let drop_left = Self::drop_(left);
let drop_right = Self::drop_(right);
Self::case(&drop_right, &drop_left)
}
/// Create a DAG that asserts that its child returns `true`, and fails otherwise.
/// The `hash` identifies the assertion and is returned upon failure.
///
/// _Overall type: A → 1 where `child`: A → 2_
///
/// _Type inference will fail if children are not of the correct type._
fn assert(child: &Self, hash: Cmr) -> Result<Self, types::Error> {
let unit = Self::unit(child.inference_context());
let pair_child_unit = Self::pair(child, &unit)?;
let assertr_hidden_unit = Self::assertr(hash, &unit)?;
Self::comp(&pair_child_unit, &assertr_hidden_unit)
}
/// Create a DAG that computes Boolean _NOT_ of the `child`.
///
/// _Overall type: A → 2 where `child`: A → 2_
///
/// _Type inference will fail if children are not of the correct type._
#[allow(clippy::should_implement_trait)]
fn not(child: &Self) -> Result<Self, types::Error> {
let unit = Self::unit(child.inference_context());
let pair_child_unit = Self::pair(child, &unit)?;
let bit_true = Self::bit_true(child.inference_context());
let bit_false = Self::bit_false(child.inference_context());
let case_true_false = Self::case(&bit_true, &bit_false)?;
Self::comp(&pair_child_unit, &case_true_false)
}
/// Create a DAG that computes Boolean _AND_ of the `left` and `right` child.
///
/// _Overall type: A → 2 where `left`: A → 2 and `right`: A → 2_
///
/// _Type inference will fail if children are not of the correct type._
fn and(left: &Self, right: &Self) -> Result<Self, types::Error> {
left.inference_context()
.check_eq(right.inference_context())?;
let iden = Self::iden(left.inference_context());
let pair_left_iden = Self::pair(left, &iden)?;
let bit_false = Self::bit_false(left.inference_context());
let drop_right = Self::drop_(right);
let case_false_right = Self::case(&bit_false, &drop_right)?;
Self::comp(&pair_left_iden, &case_false_right)
}
/// Create a DAG that computes Boolean _OR_ of the `left` and `right`.
///
/// _Overall type: A → 2 where `left`: A → 2 and `right`: A → 2_
///
/// _Type inference will fail if children are not of the correct type._
fn or(left: &Self, right: &Self) -> Result<Self, types::Error> {
left.inference_context()
.check_eq(right.inference_context())?;
let iden = Self::iden(left.inference_context());
let pair_left_iden = Self::pair(left, &iden)?;
let drop_right = Self::drop_(right);
let bit_true = Self::bit_true(left.inference_context());
let case_right_true = Self::case(&drop_right, &bit_true)?;
Self::comp(&pair_left_iden, &case_right_true)
}
}
pub trait DisconnectConstructible<X>: Sized {
fn disconnect(left: &Self, right: &X) -> Result<Self, types::Error>;
}
pub trait JetConstructible<J>: Sized {
fn jet(inference_context: &types::Context, jet: J) -> Self;
}
pub trait WitnessConstructible<W>: Sized {
fn witness(inference_context: &types::Context, witness: W) -> Self;
}
/// A node in a Simplicity expression.
///
/// There are three node types provided by this library: `ConstructNode`, `CommitNode`,
/// and `RedeemNode`, which represent Simplicty programs during construction, at
/// commitment time, and at redemption time, respectively.
///
/// This generic structure is used to define conversions and mapping functions over
/// nodes and DAGs, and allows users to define their own custom node types.
///
/// For equality and hashing purposes, nodes are characterized entirely by their
/// CMR and cached data. Users who create custom nodes should define a custom type
/// for [`Marker::CachedData`] and think carefully about whether and how to
/// implement the [`std::hash::Hash`] or equality traits.
pub struct Node<N: Marker> {
inner: Inner<Arc<Node<N>>, N::Jet, N::Disconnect, N::Witness>,
cmr: Cmr,
data: N::CachedData,
}
impl<N: Marker> PartialEq for Node<N>
where
N::CachedData: PartialEq,
{
fn eq(&self, other: &Self) -> bool {
self.cmr == other.cmr && self.data == other.data
}
}
impl<N: Marker> Eq for Node<N> where N::CachedData: Eq {}
impl<N: Marker> hash::Hash for Node<N>
where
N::CachedData: hash::Hash,
{
fn hash<H: hash::Hasher>(&self, h: &mut H) {
self.cmr.hash(h);
self.data.hash(h);
}
}
impl<N: Marker> fmt::Debug for Node<N>
where
for<'a> &'a Node<N>: DagLike,
{
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt::Display::fmt(self, f)
}
}
impl<N: Marker> fmt::Display for Node<N>
where
for<'a> &'a Node<N>: DagLike,
{
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
self.post_order_iter::<MaxSharing<N>>().into_display(
f,
|node, f| fmt::Display::fmt(&node.inner, f),
|_, _| Ok(()),
)
}
}
impl<N> CoreConstructible for Arc<Node<N>>
where
N: Marker,
N::CachedData: CoreConstructible,
{
fn iden(inference_context: &types::Context) -> Self {
Arc::new(Node {
cmr: Cmr::iden(),
data: N::CachedData::iden(inference_context),
inner: Inner::Iden,
})
}
fn unit(inference_context: &types::Context) -> Self {
Arc::new(Node {
cmr: Cmr::unit(),
data: N::CachedData::unit(inference_context),
inner: Inner::Unit,
})
}
fn injl(child: &Self) -> Self {
Arc::new(Node {
cmr: Cmr::injl(child.cmr()),
data: N::CachedData::injl(&child.data),
inner: Inner::InjL(Arc::clone(child)),
})
}
fn injr(child: &Self) -> Self {
Arc::new(Node {
cmr: Cmr::injr(child.cmr()),
data: N::CachedData::injr(&child.data),
inner: Inner::InjR(Arc::clone(child)),
})
}
fn take(child: &Self) -> Self {
Arc::new(Node {
cmr: Cmr::take(child.cmr()),
data: N::CachedData::take(&child.data),
inner: Inner::Take(Arc::clone(child)),
})
}
fn drop_(child: &Self) -> Self {
Arc::new(Node {
cmr: Cmr::drop(child.cmr()),
data: N::CachedData::drop_(&child.data),
inner: Inner::Drop(Arc::clone(child)),
})
}
fn comp(left: &Self, right: &Self) -> Result<Self, types::Error> {
Ok(Arc::new(Node {
cmr: Cmr::comp(left.cmr(), right.cmr()),
data: N::CachedData::comp(&left.data, &right.data)?,
inner: Inner::Comp(Arc::clone(left), Arc::clone(right)),
}))
}
fn case(left: &Self, right: &Self) -> Result<Self, types::Error> {
Ok(Arc::new(Node {
cmr: Cmr::case(left.cmr(), right.cmr()),
data: N::CachedData::case(&left.data, &right.data)?,
inner: Inner::Case(Arc::clone(left), Arc::clone(right)),
}))
}
fn assertl(left: &Self, r_cmr: Cmr) -> Result<Self, types::Error> {
Ok(Arc::new(Node {
cmr: Cmr::case(left.cmr(), r_cmr),
data: N::CachedData::assertl(&left.data, r_cmr)?,
inner: Inner::AssertL(Arc::clone(left), r_cmr),
}))
}
fn assertr(l_cmr: Cmr, right: &Self) -> Result<Self, types::Error> {
Ok(Arc::new(Node {
cmr: Cmr::case(l_cmr, right.cmr()),
data: N::CachedData::assertr(l_cmr, &right.data)?,
inner: Inner::AssertR(l_cmr, Arc::clone(right)),
}))
}
fn pair(left: &Self, right: &Self) -> Result<Self, types::Error> {
Ok(Arc::new(Node {
cmr: Cmr::pair(left.cmr(), right.cmr()),
data: N::CachedData::pair(&left.data, &right.data)?,
inner: Inner::Pair(Arc::clone(left), Arc::clone(right)),
}))
}
fn fail(inference_context: &types::Context, entropy: FailEntropy) -> Self {
Arc::new(Node {
cmr: Cmr::fail(entropy),
data: N::CachedData::fail(inference_context, entropy),
inner: Inner::Fail(entropy),
})
}
fn const_word(inference_context: &types::Context, value: Arc<Value>) -> Self {
Arc::new(Node {
cmr: Cmr::const_word(&value),
data: N::CachedData::const_word(inference_context, Arc::clone(&value)),
inner: Inner::Word(value),
})
}
fn inference_context(&self) -> &types::Context {
self.data.inference_context()
}
}
impl<N> DisconnectConstructible<N::Disconnect> for Arc<Node<N>>
where
N: Marker,
N::CachedData: DisconnectConstructible<N::Disconnect>,
{
fn disconnect(left: &Self, right: &N::Disconnect) -> Result<Self, types::Error> {
Ok(Arc::new(Node {
cmr: Cmr::disconnect(left.cmr()),
data: N::CachedData::disconnect(&left.data, right)?,
inner: Inner::Disconnect(Arc::clone(left), right.clone()),
}))
}
}
impl<N> WitnessConstructible<N::Witness> for Arc<Node<N>>
where
N: Marker,
N::CachedData: WitnessConstructible<N::Witness>,
{
fn witness(inference_context: &types::Context, value: N::Witness) -> Self {
Arc::new(Node {
cmr: Cmr::witness(),
data: N::CachedData::witness(inference_context, value.clone()),
inner: Inner::Witness(value),
})
}
}
impl<N> JetConstructible<N::Jet> for Arc<Node<N>>
where
N: Marker,
N::CachedData: JetConstructible<N::Jet>,
{
fn jet(inference_context: &types::Context, jet: N::Jet) -> Self {
Arc::new(Node {
cmr: Cmr::jet(jet),
data: N::CachedData::jet(inference_context, jet),
inner: Inner::Jet(jet),
})
}
}
impl<N: Marker> Node<N> {
/// Accessor for the node's "inner value", i.e. its combinator
pub fn inner(&self) -> &Inner<Arc<Node<N>>, N::Jet, N::Disconnect, N::Witness> {
&self.inner
}
/// Accessor for the node's CMR
pub fn cmr(&self) -> Cmr {
self.cmr
}
/// Accessor for the node's cached data
pub fn sharing_id(&self) -> Option<N::SharingId> {
N::compute_sharing_id(self.cmr, &self.data)
}
/// Accessor for the node's cached data
pub fn cached_data(&self) -> &N::CachedData {
&self.data
}
/// Contruct a node from its constituent parts.
///
/// This method can be used to directly costruct a node. It will compute the CMR
/// automatically based on the value of `inner` but requires that `cached_data`
/// be provided.
///
/// If available, [`Constructible'] and its dependent traits will be easier to
/// use.
pub fn from_parts(
inner: Inner<Arc<Self>, N::Jet, N::Disconnect, N::Witness>,
data: N::CachedData,
) -> Self {
let cmr = match inner {
Inner::Unit => Cmr::unit(),
Inner::Iden => Cmr::iden(),
Inner::InjL(ref c) => Cmr::injl(c.cmr()),
Inner::InjR(ref c) => Cmr::injr(c.cmr()),
Inner::Take(ref c) => Cmr::take(c.cmr()),
Inner::Drop(ref c) => Cmr::drop(c.cmr()),
Inner::Comp(ref cl, ref cr) => Cmr::comp(cl.cmr(), cr.cmr()),
Inner::Case(ref cl, ref cr) => Cmr::case(cl.cmr(), cr.cmr()),
Inner::AssertL(ref c, cmr) => Cmr::case(c.cmr(), cmr),
Inner::AssertR(cmr, ref c) => Cmr::case(cmr, c.cmr()),
Inner::Pair(ref cl, ref cr) => Cmr::pair(cl.cmr(), cr.cmr()),
Inner::Disconnect(ref cl, _) => Cmr::disconnect(cl.cmr()),
Inner::Witness(_) => Cmr::witness(),
Inner::Fail(entropy) => Cmr::fail(entropy),
Inner::Jet(j) => Cmr::jet(j),
Inner::Word(ref w) => Cmr::const_word(w),
};
Node { cmr, inner, data }
}
/// Generic conversion function from one type of node to another, with the
/// ability to prune during the conversion.
///
/// Parameterized over what kind of sharing to use when iterating over the
/// DAG, and what conversion logic to use.
///
/// See the documentation for [`Converter`] for details.
pub fn convert<S, M, C>(&self, converter: &mut C) -> Result<Arc<Node<M>>, C::Error>
where
S: for<'a> SharingTracker<&'a Self> + Default,
M: Marker<Jet = <N as Marker>::Jet>,
C: Converter<N, M>,
{
let mut converted: Vec<Arc<Node<M>>> = vec![];
for data in self.post_order_iter::<S>() {
// First, tell the converter about the iterator state..
converter.visit_node(&data);
// Construct an Inner<usize> where pointers are replaced by indices.
// Note that `map_left_right`'s internal logic will ensure that these
// `unwrap`s are only called when they will succeed.
let indexed_inner: Inner<usize, N::Jet, &N::Disconnect, &N::Witness> = data
.node
.inner
.as_ref()
.map_left_right(|_| data.left_index.unwrap(), |_| data.right_index.unwrap());
// Then, convert witness data, if this is a witness node.
let witness_inner: Inner<&usize, M::Jet, &&N::Disconnect, M::Witness> = indexed_inner
.as_ref()
.map_witness_result(|wit| converter.convert_witness(&data, wit))?;
// Then convert disconnect nodes data.
let maybe_converted = data.right_index.map(|idx| &converted[idx]);
let witness_inner: Inner<&usize, N::Jet, M::Disconnect, M::Witness> = witness_inner
.map_disconnect_result(|disc| {
converter.convert_disconnect(&data, maybe_converted, disc)
})?;
// Then put the converted nodes in place (it's easier to do this in this
// order because of the way the reference types work out).
let converted_inner: Inner<Arc<Node<M>>, M::Jet, M::Disconnect, M::Witness> =
witness_inner.map(|idx| Arc::clone(&converted[*idx]));
// Next, prune case nodes into asserts, if applicable
let pruned_inner = if let Inner::Case(left, right) = converted_inner {
let hide = converter.prune_case(&data, &left, &right)?;
match hide {
Hide::Neither => Inner::Case(left, right),
Hide::Left => Inner::AssertR(left.cmr(), right),
Hide::Right => Inner::AssertL(left, right.cmr()),
}
} else {
converted_inner
};
// Finally, construct the node
converted.push(Arc::new(Node {
data: converter.convert_data(&data, pruned_inner.as_ref())?,
cmr: data.node.cmr,
inner: pruned_inner,
}));
}
Ok(converted.pop().unwrap())
}
/// Display the Simplicity expression as a linear string.
///
/// The linear string has no sharing and may be **exponentially larger**
/// than the originally shared expression!
pub fn display_expr(&self) -> DisplayExpr<N> {
DisplayExpr::from(self)
}
}
#[cfg(test)]
#[cfg(all(feature = "test-utils", feature = "elements"))]
mod tests {
use ffi::tests::TestData;
use crate::analysis::Cost;
use crate::ffi;
use crate::jet::Elements;
use crate::BitIter;
use crate::RedeemNode;
fn check_merkle_roots(test: &TestData) {
let prog = BitIter::from(test.prog.as_slice());
let witness = BitIter::from(test.witness.as_slice());
ffi::tests::run_program(&test.prog, &test.witness, ffi::tests::TestUpTo::CheckOneOne)
.unwrap();
let prog = RedeemNode::<Elements>::decode(prog, witness).unwrap();
assert_eq!(prog.cmr().to_byte_array(), test.cmr);
assert_eq!(prog.amr().to_byte_array(), test.amr);
assert_eq!(prog.imr().to_byte_array(), test.imr);
assert_eq!(prog.bounds().cost, Cost::from_milliweight(test.cost))
}
#[test]
fn progs_cmr() {
let schnorr0 = ffi::tests::schnorr0_test_data();
let schnorr6 = ffi::tests::schnorr6_test_data();
let ctx8_unpruned = ffi::tests::ctx8_unpruned_test_data();
let ctx8_pruned = ffi::tests::ctx8_pruned_test_data();
// check_merkle_roots(&hash_block); Need 1 -> 1 for now.
check_merkle_roots(&schnorr0);
check_merkle_roots(&schnorr6);
check_merkle_roots(&ctx8_unpruned);
check_merkle_roots(&ctx8_pruned);
}
}