Skip to content

Commit 7150f0e

Browse files
committed
named: replace ad-hoc ConstructNode/CommitNode/WitnessNode with generic ones
Now we have a marker type WithNames which wraps a marker type from rust-simplicity, erasing its disconnect nodes and replacing its witness nodes with names. This gets us an analogue of ConstructNode, CommitNode, WitnessNode, RedeemNode, etc., all at once. Then replace: * to_commit_node, whose role was to forget names and finalize types, with a general method forget_names, which just forgets the names (then the user can call .finalize_types() themselves if they want) * to_witness_node, whose role was to replace names with actual witnesses, with a general method populate_witnesses, which does this (Actually, `to_witness_node` is not very general at all. It specifically converts a WtihNames<ConstructNode> to an unnamed ConstructNode. In this commit, it's possible to implement it in a general way, converting an arbitrary Node<WithNames<M>> to a Node<M>. But in a couple commits, when we replace ConstructNode with CommitNode in CompiledProgram, we will be forced to fix its signature to convert a CommitNode to a RedeemNode. The diff will be easier to read starting from this non-generic version than it would be if we started from the generic version.) Along the way, use core::convert::Infallible for error types so that we don't need to unwrap errors. This leaves the precondition on `populate_witnesses` that the user call `is_consistent` on witness nodes before converting from SimplicityHL to Simplicity. I believe I can remove this precondition and return an error if the witness values don't match the Simplicity node, but I need to update the library to get the IncompleteType type first, so that I can return a useful error. So I will defer this improvement.
1 parent 60f65b4 commit 7150f0e

File tree

2 files changed

+102
-158
lines changed

2 files changed

+102
-158
lines changed

src/lib.rs

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
//! Library for parsing and compiling SimplicityHL
22
3-
pub type ProgNode = Arc<named::ConstructNode>;
3+
pub type ProgNode = Arc<named::ConstructNode<Elements>>;
44

55
pub mod array;
66
pub mod ast;
@@ -123,7 +123,8 @@ impl CompiledProgram {
123123

124124
/// Access the Simplicity target code, without witness data.
125125
pub fn commit(&self) -> Arc<CommitNode<Elements>> {
126-
named::to_commit_node(&self.simplicity)
126+
named::forget_names(&self.simplicity)
127+
.finalize_types()
127128
.expect("Compiled SimplicityHL program has type 1 -> 1")
128129
}
129130

@@ -152,7 +153,7 @@ impl CompiledProgram {
152153
witness_values
153154
.is_consistent(&self.witness_types)
154155
.map_err(|e| e.to_string())?;
155-
let simplicity_witness = named::to_witness_node(&self.simplicity, witness_values);
156+
let simplicity_witness = named::populate_witnesses(&self.simplicity, witness_values);
156157
let simplicity_redeem = match env {
157158
Some(env) => simplicity_witness.finalize_pruned(env),
158159
None => simplicity_witness.finalize_unpruned(),

src/named.rs

Lines changed: 98 additions & 155 deletions
Original file line numberDiff line numberDiff line change
@@ -1,96 +1,126 @@
11
use std::sync::Arc;
22

33
use simplicity::dag::{InternalSharing, PostOrderIterItem};
4-
use simplicity::jet::{Elements, Jet};
4+
use simplicity::jet::Jet;
55
use simplicity::node::{
6-
self, CommitData, ConstructData as WitnessData, Constructible, Converter, CoreConstructible,
7-
Inner, JetConstructible, NoDisconnect, NoWitness, Node, WitnessConstructible,
6+
self, Converter, CoreConstructible, Inner, NoDisconnect, NoWitness, Node, WitnessConstructible,
87
};
9-
use simplicity::types::arrow::Arrow;
10-
use simplicity::{types, CommitNode, FailEntropy};
11-
use simplicity::{Cmr, ConstructNode as WitnessNode};
8+
use simplicity::Cmr;
9+
use simplicity::{types, FailEntropy};
1210

1311
use crate::str::WitnessName;
1412
use crate::value::StructuralValue;
1513
use crate::witness::WitnessValues;
1614

17-
/// Marker for [`ConstructNode`].
1815
#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Debug, Hash)]
19-
pub struct Construct<N> {
20-
/// Makes the type non-constructible.
21-
never: std::convert::Infallible,
22-
/// Required by Rust.
23-
phantom: std::marker::PhantomData<N>,
24-
}
25-
26-
/// Sharing ID of [`ConstructNode`].
27-
/// Cannot be constructed because there is no sharing.
28-
#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Debug, Hash)]
29-
pub enum ConstructId {}
16+
pub struct WithNames<T>(T);
3017

31-
impl<J: Jet> node::Marker for Construct<J> {
32-
type CachedData = ConstructData<J>;
18+
impl<M: node::Marker> node::Marker for WithNames<M> {
19+
type CachedData = M::CachedData;
3320
type Witness = WitnessName;
21+
// It's quite difficult to wrap M::Disconnect because of Rust's lack of HKTs, and
22+
// we don't use disconnect in this library right now, so punt on it for now.
3423
type Disconnect = NoDisconnect;
35-
type SharingId = ConstructId;
36-
type Jet = J;
24+
type SharingId = M::SharingId;
25+
type Jet = M::Jet;
26+
27+
fn compute_sharing_id(cmr: Cmr, cached_data: &Self::CachedData) -> Option<Self::SharingId> {
28+
M::compute_sharing_id(cmr, cached_data)
29+
}
30+
}
31+
32+
/// Helper trait so we can abstract over witness and disconnects of any type,
33+
/// as long as we can produce them from a no-witness no-disconnect object.
34+
pub trait Nullable {
35+
fn none() -> Self;
36+
}
3737

38-
fn compute_sharing_id(_: Cmr, _cached_data: &Self::CachedData) -> Option<Self::SharingId> {
38+
impl<T> Nullable for Option<T> {
39+
fn none() -> Self {
3940
None
4041
}
4142
}
4243

44+
impl Nullable for NoWitness {
45+
fn none() -> Self {
46+
NoWitness
47+
}
48+
}
49+
50+
impl Nullable for NoDisconnect {
51+
fn none() -> Self {
52+
NoDisconnect
53+
}
54+
}
55+
4356
/// [`simplicity::ConstructNode`] with named witness nodes.
4457
///
4558
/// Nodes other than witness don't have names.
46-
pub type ConstructNode = Node<Construct<Elements>>;
59+
pub type ConstructNode<J> = Node<WithNames<node::Construct<J>>>;
60+
61+
/// [`simplicity::CommitNode`] with named witness nodes.
62+
///
63+
/// Nodes other than witness don't have names.
64+
pub type CommitNode<J> = Node<WithNames<node::Commit<J>>>;
4765

4866
// FIXME: The following methods cannot be implemented for simplicity::node::Node because that is a foreign type
4967

5068
/// Convert [`ConstructNode`] into [`CommitNode`] by dropping the name of witness nodes.
51-
pub fn to_commit_node(node: &ConstructNode) -> Result<Arc<CommitNode<Elements>>, types::Error> {
69+
pub fn forget_names<M>(node: &Node<WithNames<M>>) -> Arc<Node<M>>
70+
where
71+
M: node::Marker,
72+
M::Disconnect: Nullable,
73+
M::Witness: Nullable,
74+
{
5275
struct Forgetter;
5376

54-
impl<J: Jet> Converter<Construct<J>, node::Commit<J>> for Forgetter {
55-
type Error = types::Error;
77+
impl<M> Converter<WithNames<M>, M> for Forgetter
78+
where
79+
M: node::Marker,
80+
M::Disconnect: Nullable,
81+
M::Witness: Nullable,
82+
{
83+
type Error = core::convert::Infallible;
5684

5785
fn convert_witness(
5886
&mut self,
59-
_: &PostOrderIterItem<&Node<Construct<J>>>,
87+
_: &PostOrderIterItem<&Node<WithNames<M>>>,
6088
_: &WitnessName,
61-
) -> Result<NoWitness, Self::Error> {
62-
Ok(NoWitness)
89+
) -> Result<M::Witness, Self::Error> {
90+
Ok(M::Witness::none())
6391
}
6492

6593
fn convert_disconnect(
6694
&mut self,
67-
_: &PostOrderIterItem<&Node<Construct<J>>>,
68-
_: Option<&Arc<CommitNode<J>>>,
95+
_: &PostOrderIterItem<&Node<WithNames<M>>>,
96+
_: Option<&Arc<Node<M>>>,
6997
_: &NoDisconnect,
70-
) -> Result<NoDisconnect, Self::Error> {
71-
Ok(NoDisconnect)
98+
) -> Result<M::Disconnect, Self::Error> {
99+
Ok(M::Disconnect::none())
72100
}
73101

74102
fn convert_data(
75103
&mut self,
76-
data: &PostOrderIterItem<&Node<Construct<J>>>,
77-
inner: Inner<&Arc<CommitNode<J>>, J, &NoDisconnect, &NoWitness>,
78-
) -> Result<Arc<CommitData<J>>, Self::Error> {
79-
let arrow = data.node.cached_data().arrow();
80-
let inner = inner.map(Arc::as_ref).map(CommitNode::<J>::cached_data);
81-
CommitData::new(arrow, inner).map(Arc::new)
104+
data: &PostOrderIterItem<&Node<WithNames<M>>>,
105+
_: Inner<&Arc<Node<M>>, M::Jet, &M::Disconnect, &M::Witness>,
106+
) -> Result<M::CachedData, Self::Error> {
107+
Ok(data.node.cached_data().clone())
82108
}
83109
}
84110

85-
node.convert::<InternalSharing, _, _>(&mut Forgetter)
111+
match node.convert::<InternalSharing, _, _>(&mut Forgetter) {
112+
Ok(ret) => ret,
113+
Err(inf) => match inf {},
114+
}
86115
}
87116

88-
/// Convert [`ConstructNode`] into [`WitnessNode`] by populating witness nodes with their assigned values.
117+
/// Converts a named [`ConstructNode`] into a standard [`node::ConstructNode`], by populating
118+
/// witness nodes with their assigned values.
89119
///
90120
/// Each witness node has a name. If there is no value assigned to this name,
91121
/// then the node is left empty.
92122
///
93-
/// When [`WitnessNode`] is finalized to [`node::RedeemNode`], there will be an error if any witness
123+
/// When [`node::ConstructNode`] is finalized to [`node::RedeemNode`], there will be an error if any witness
94124
/// node on a used (unpruned) branch is empty. It is the responsibility of the caller to ensure that
95125
/// all used witness nodes have an assigned value.
96126
///
@@ -99,18 +129,20 @@ pub fn to_commit_node(node: &ConstructNode) -> Result<Arc<CommitNode<Elements>>,
99129
/// It is the responsibility of the caller to ensure that the given witness `values` match the
100130
/// types in the construct `node`. This can be done by calling [`WitnessValues::is_consistent`]
101131
/// on the original SimplicityHL program before it is compiled to Simplicity.
102-
pub fn to_witness_node(node: &ConstructNode, values: WitnessValues) -> Arc<WitnessNode<Elements>> {
132+
pub fn populate_witnesses<J: Jet>(
133+
node: &ConstructNode<J>,
134+
values: WitnessValues,
135+
) -> Arc<node::ConstructNode<J>> {
103136
struct Populator {
104137
values: WitnessValues,
105-
inference_context: types::Context,
106138
}
107139

108-
impl<J: Jet> Converter<Construct<J>, node::Construct<J>> for Populator {
109-
type Error = ();
140+
impl<J: Jet> Converter<WithNames<node::Construct<J>>, node::Construct<J>> for Populator {
141+
type Error = core::convert::Infallible;
110142

111143
fn convert_witness(
112144
&mut self,
113-
_: &PostOrderIterItem<&Node<Construct<J>>>,
145+
_: &PostOrderIterItem<&ConstructNode<J>>,
114146
witness: &WitnessName,
115147
) -> Result<Option<simplicity::Value>, Self::Error> {
116148
let maybe_value = self
@@ -123,129 +155,40 @@ pub fn to_witness_node(node: &ConstructNode, values: WitnessValues) -> Arc<Witne
123155

124156
fn convert_disconnect(
125157
&mut self,
126-
_: &PostOrderIterItem<&Node<Construct<J>>>,
127-
_: Option<&Arc<WitnessNode<J>>>,
158+
_: &PostOrderIterItem<&ConstructNode<J>>,
159+
_: Option<&Arc<node::ConstructNode<J>>>,
128160
_: &NoDisconnect,
129-
) -> Result<Option<Arc<WitnessNode<J>>>, Self::Error> {
161+
) -> Result<Option<Arc<node::ConstructNode<J>>>, Self::Error> {
130162
Ok(None)
131163
}
132164

133165
fn convert_data(
134166
&mut self,
135-
_: &PostOrderIterItem<&Node<Construct<J>>>,
136-
inner: Inner<
137-
&Arc<WitnessNode<J>>,
167+
data: &PostOrderIterItem<&ConstructNode<J>>,
168+
_: Inner<
169+
&Arc<node::ConstructNode<J>>,
138170
J,
139-
&Option<Arc<WitnessNode<J>>>,
171+
&Option<Arc<node::ConstructNode<J>>>,
140172
&Option<simplicity::Value>,
141173
>,
142-
) -> Result<WitnessData<J>, Self::Error> {
143-
let inner = inner
144-
.map(Arc::as_ref)
145-
.map(WitnessNode::<J>::cached_data)
146-
.map_witness(Option::<simplicity::Value>::clone);
147-
Ok(WitnessData::from_inner(&self.inference_context, inner).unwrap())
148-
}
149-
}
150-
151-
let mut populator = Populator {
152-
inference_context: types::Context::new(),
153-
values,
154-
};
155-
node.convert::<InternalSharing, _, _>(&mut populator)
156-
.unwrap()
157-
}
158-
159-
/// Copy of [`node::ConstructData`] with an implementation of [`WitnessConstructible<WitnessName>`].
160-
#[derive(Clone, Debug)]
161-
pub struct ConstructData<J> {
162-
arrow: Arrow,
163-
phantom: std::marker::PhantomData<J>,
164-
}
165-
166-
impl<J> ConstructData<J> {
167-
/// Access the arrow of the node.
168-
pub fn arrow(&self) -> &Arrow {
169-
&self.arrow
170-
}
171-
}
172-
173-
impl<J> From<Arrow> for ConstructData<J> {
174-
fn from(arrow: Arrow) -> Self {
175-
Self {
176-
arrow,
177-
phantom: std::marker::PhantomData,
174+
) -> Result<node::ConstructData<J>, Self::Error> {
175+
Ok(data.node.cached_data().clone())
178176
}
179177
}
180-
}
181-
182-
impl<J> CoreConstructible for ConstructData<J> {
183-
fn iden(inference_context: &types::Context) -> Self {
184-
Arrow::iden(inference_context).into()
185-
}
186-
187-
fn unit(inference_context: &types::Context) -> Self {
188-
Arrow::unit(inference_context).into()
189-
}
190-
191-
fn injl(child: &Self) -> Self {
192-
Arrow::injl(&child.arrow).into()
193-
}
194-
195-
fn injr(child: &Self) -> Self {
196-
Arrow::injr(&child.arrow).into()
197-
}
198-
199-
fn take(child: &Self) -> Self {
200-
Arrow::take(&child.arrow).into()
201-
}
202-
203-
fn drop_(child: &Self) -> Self {
204-
Arrow::drop_(&child.arrow).into()
205-
}
206-
207-
fn comp(left: &Self, right: &Self) -> Result<Self, types::Error> {
208-
Arrow::comp(&left.arrow, &right.arrow).map(Self::from)
209-
}
210-
211-
fn case(left: &Self, right: &Self) -> Result<Self, types::Error> {
212-
Arrow::case(&left.arrow, &right.arrow).map(Self::from)
213-
}
214-
215-
fn assertl(left: &Self, right: Cmr) -> Result<Self, types::Error> {
216-
Arrow::assertl(&left.arrow, right).map(Self::from)
217-
}
218-
219-
fn assertr(left: Cmr, right: &Self) -> Result<Self, types::Error> {
220-
Arrow::assertr(left, &right.arrow).map(Self::from)
221-
}
222-
223-
fn pair(left: &Self, right: &Self) -> Result<Self, types::Error> {
224-
Arrow::pair(&left.arrow, &right.arrow).map(Self::from)
225-
}
226-
227-
fn fail(inference_context: &types::Context, entropy: FailEntropy) -> Self {
228-
Arrow::fail(inference_context, entropy).into()
229-
}
230-
231-
fn const_word(inference_context: &types::Context, word: simplicity::Word) -> Self {
232-
Arrow::const_word(inference_context, word).into()
233-
}
234-
235-
fn inference_context(&self) -> &types::Context {
236-
self.arrow.inference_context()
237-
}
238-
}
239178

240-
impl<J: Jet> JetConstructible<J> for ConstructData<J> {
241-
fn jet(inference_context: &types::Context, jet: J) -> Self {
242-
Arrow::jet(inference_context, jet).into()
179+
let mut populator = Populator { values };
180+
match node.convert::<InternalSharing, _, _>(&mut populator) {
181+
Ok(ret) => ret,
182+
Err(inf) => match inf {},
243183
}
244184
}
245185

246-
impl<J> WitnessConstructible<WitnessName> for ConstructData<J> {
186+
// This awkward construction is required by rust-simplicity to implement WitnessConstructible
187+
// for Node<WithNames<Construct>>. See
188+
// https://docs.rs/simplicity-lang/latest/simplicity/node/trait.WitnessConstructible.html#foreign-impls
189+
impl<J: Jet> WitnessConstructible<WitnessName> for node::ConstructData<J> {
247190
fn witness(inference_context: &types::Context, _: WitnessName) -> Self {
248-
Arrow::witness(inference_context, ()).into()
191+
WitnessConstructible::<Option<_>>::witness(inference_context, None)
249192
}
250193
}
251194

0 commit comments

Comments
 (0)