Skip to content

Commit 61b7ddc

Browse files
committed
refactor: Remove WitnessNode
The difference between ConstructNode and WitnessNode was always confusing. ConstructNode came earlier and was used mostly for decoding programs. Its witness nodes were always empty because there are no witness values during decoding. WitnessNode was added later for creating satisfied Simplicity policies. Its witness nodes were potentially filled (Option<Value>) because a witness value might exist (satisfied fragment) or not exist (unsatisfied fragment). The code base for ConstructNode and for WitnessNode is almost identical. Small changes make both identical and allow us to remove one of them. In this commit, we set <Construct as Marker>::Witness to Option<Value> and by copy over the finalization code from WitnessNode. We replace all code occurrences of `Witness*` with `Construct*`. Finally, we delete src/node/witness.rs entirely.
1 parent 3be0fd9 commit 61b7ddc

File tree

13 files changed

+190
-376
lines changed

13 files changed

+190
-376
lines changed

src/bit_encoding/decode.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ use crate::dag::{Dag, DagLike, InternalSharing};
99
use crate::jet::Jet;
1010
use crate::merkle::cmr::Cmr;
1111
use crate::node::{
12-
ConstructNode, CoreConstructible, DisconnectConstructible, JetConstructible, NoWitness,
12+
ConstructNode, CoreConstructible, DisconnectConstructible, JetConstructible,
1313
WitnessConstructible,
1414
};
1515
use crate::types;
@@ -235,7 +235,7 @@ pub fn decode_expression<I: Iterator<Item = u8>, J: Jet>(
235235
converted[i].get()?,
236236
&Some(Arc::clone(converted[j].get()?)),
237237
)?),
238-
DecodeNode::Witness => Node(ArcNode::witness(&inference_context, NoWitness)),
238+
DecodeNode::Witness => Node(ArcNode::witness(&inference_context, None)),
239239
DecodeNode::Fail(entropy) => Node(ArcNode::fail(&inference_context, entropy)),
240240
DecodeNode::Hidden(cmr) => {
241241
if !hidden_set.insert(cmr) {

src/human_encoding/mod.rs

+3-3
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ mod parse;
1313
use crate::dag::{DagLike, MaxSharing};
1414
use crate::jet::Jet;
1515
use crate::node::{self, CommitNode, NoWitness};
16-
use crate::{Cmr, Imr, Value, WitnessNode};
16+
use crate::{Cmr, ConstructNode, Imr, Value};
1717

1818
use std::collections::HashMap;
1919
use std::str;
@@ -213,9 +213,9 @@ impl<J: Jet> Forest<J> {
213213
pub fn to_witness_node(
214214
&self,
215215
witness: &HashMap<Arc<str>, Value>,
216-
) -> Option<Arc<WitnessNode<J>>> {
216+
) -> Option<Arc<ConstructNode<J>>> {
217217
let main = self.roots.get("main")?;
218-
Some(main.to_witness_node(witness, self.roots()))
218+
Some(main.to_construct_node(witness, self.roots()))
219219
}
220220
}
221221

src/human_encoding/named_node.rs

+13-14
Original file line numberDiff line numberDiff line change
@@ -6,13 +6,12 @@ use crate::dag::{InternalSharing, MaxSharing, PostOrderIterItem};
66
use crate::human_encoding::{Error, ErrorSet, Position, WitnessOrHole};
77
use crate::jet::Jet;
88
use crate::node::{
9-
self, Commit, CommitData, CommitNode, Converter, Inner, NoDisconnect, NoWitness, Node, Witness,
10-
WitnessData,
9+
self, Commit, CommitData, CommitNode, Construct, ConstructData, Constructible as _, Converter,
10+
CoreConstructible as _, Inner, NoDisconnect, NoWitness, Node,
1111
};
12-
use crate::node::{Construct, ConstructData, Constructible as _, CoreConstructible as _};
1312
use crate::types;
1413
use crate::types::arrow::{Arrow, FinalArrow};
15-
use crate::{encode, Value, WitnessNode};
14+
use crate::{encode, ConstructNode, Value};
1615
use crate::{BitWriter, Cmr, Imr};
1716

1817
use std::collections::HashMap;
@@ -108,19 +107,19 @@ impl<J: Jet> NamedCommitNode<J> {
108107
.unwrap()
109108
}
110109

111-
pub fn to_witness_node(
110+
pub fn to_construct_node(
112111
&self,
113112
witness: &HashMap<Arc<str>, Value>,
114113
disconnect: &HashMap<Arc<str>, Arc<NamedCommitNode<J>>>,
115-
) -> Arc<WitnessNode<J>> {
114+
) -> Arc<ConstructNode<J>> {
116115
struct Populator<'a, J: Jet> {
117116
witness_map: &'a HashMap<Arc<str>, Value>,
118117
disconnect_map: &'a HashMap<Arc<str>, Arc<NamedCommitNode<J>>>,
119118
inference_context: types::Context,
120119
phantom: PhantomData<J>,
121120
}
122121

123-
impl<J: Jet> Converter<Named<Commit<J>>, Witness<J>> for Populator<'_, J> {
122+
impl<J: Jet> Converter<Named<Commit<J>>, Construct<J>> for Populator<'_, J> {
124123
type Error = ();
125124

126125
fn convert_witness(
@@ -140,9 +139,9 @@ impl<J: Jet> NamedCommitNode<J> {
140139
fn convert_disconnect(
141140
&mut self,
142141
data: &PostOrderIterItem<&Node<Named<Commit<J>>>>,
143-
maybe_converted: Option<&Arc<Node<Witness<J>>>>,
142+
maybe_converted: Option<&Arc<Node<Construct<J>>>>,
144143
_: &Arc<str>,
145-
) -> Result<Option<Arc<Node<Witness<J>>>>, Self::Error> {
144+
) -> Result<Option<Arc<Node<Construct<J>>>>, Self::Error> {
146145
if let Some(converted) = maybe_converted {
147146
Ok(Some(converted.clone()))
148147
} else {
@@ -172,16 +171,16 @@ impl<J: Jet> NamedCommitNode<J> {
172171
&mut self,
173172
_: &PostOrderIterItem<&Node<Named<Commit<J>>>>,
174173
inner: Inner<
175-
&Arc<Node<Witness<J>>>,
174+
&Arc<Node<Construct<J>>>,
176175
J,
177-
&Option<Arc<WitnessNode<J>>>,
176+
&Option<Arc<ConstructNode<J>>>,
178177
&Option<Value>,
179178
>,
180-
) -> Result<WitnessData<J>, Self::Error> {
179+
) -> Result<ConstructData<J>, Self::Error> {
181180
let inner = inner
182181
.map(|node| node.cached_data())
183182
.map_witness(|maybe_value| maybe_value.clone());
184-
Ok(WitnessData::from_inner(&self.inference_context, inner)
183+
Ok(ConstructData::from_inner(&self.inference_context, inner)
185184
.expect("types are already finalized"))
186185
}
187186
}
@@ -260,7 +259,7 @@ impl<J: Jet> NamedConstructNode<J> {
260259
.as_ref()
261260
.map(|data| &data.cached_data().internal)
262261
.map_disconnect(|_| &None)
263-
.map_witness(|_| NoWitness),
262+
.map_witness(|_| None),
264263
)?;
265264
let named_data = NamedConstructData {
266265
internal: construct_data,

src/human_encoding/parse/mod.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -591,7 +591,7 @@ mod tests {
591591
assert_eq!(main.cmr().to_string(), cmr);
592592

593593
let program = main
594-
.to_witness_node(witness, &forest)
594+
.to_construct_node(witness, &forest)
595595
.finalize_unpruned()
596596
.expect("finalize");
597597

src/lib.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,7 @@ pub use crate::merkle::{
6565
tmr::Tmr,
6666
FailEntropy, HasCmr,
6767
};
68-
pub use crate::node::{CommitNode, ConstructNode, Hiding, RedeemNode, WitnessNode};
68+
pub use crate::node::{CommitNode, ConstructNode, Hiding, RedeemNode};
6969
pub use crate::value::{Value, Word};
7070
pub use simplicity_sys as ffi;
7171
use std::fmt;

src/node/commit.rs

+9-4
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
use crate::dag::{DagLike, MaxSharing, NoSharing, PostOrderIterItem};
44
use crate::jet::Jet;
55
use crate::types::arrow::{Arrow, FinalArrow};
6-
use crate::{encode, types};
6+
use crate::{encode, types, Value};
77
use crate::{Amr, BitIter, BitWriter, Cmr, Error, FirstPassImr, Imr};
88

99
use super::{
@@ -213,8 +213,8 @@ impl<J: Jet> CommitNode<J> {
213213
&mut self,
214214
_: &PostOrderIterItem<&CommitNode<J>>,
215215
_: &NoWitness,
216-
) -> Result<NoWitness, Self::Error> {
217-
Ok(NoWitness)
216+
) -> Result<Option<Value>, Self::Error> {
217+
Ok(None)
218218
}
219219

220220
fn convert_disconnect(
@@ -229,7 +229,12 @@ impl<J: Jet> CommitNode<J> {
229229
fn convert_data(
230230
&mut self,
231231
_: &PostOrderIterItem<&CommitNode<J>>,
232-
inner: Inner<&Arc<ConstructNode<J>>, J, &Option<Arc<ConstructNode<J>>>, &NoWitness>,
232+
inner: Inner<
233+
&Arc<ConstructNode<J>>,
234+
J,
235+
&Option<Arc<ConstructNode<J>>>,
236+
&Option<Value>,
237+
>,
233238
) -> Result<ConstructData<J>, Self::Error> {
234239
let inner = inner
235240
.map(|node| node.arrow())

src/node/construct.rs

+106-9
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,17 @@
11
// SPDX-License-Identifier: CC0-1.0
22

33
use crate::dag::{InternalSharing, PostOrderIterItem};
4-
use crate::encode;
54
use crate::jet::Jet;
65
use crate::types::{self, arrow::Arrow};
7-
use crate::{BitIter, BitWriter, Cmr, FailEntropy};
6+
use crate::{encode, BitIter, BitWriter, Cmr, FailEntropy, RedeemNode, Value, Word};
87

9-
use crate::value::Word;
108
use std::io;
119
use std::marker::PhantomData;
1210
use std::sync::Arc;
1311

1412
use super::{
1513
Commit, CommitData, CommitNode, Converter, Inner, Marker, NoDisconnect, NoWitness, Node,
14+
Redeem, RedeemData,
1615
};
1716
use super::{CoreConstructible, DisconnectConstructible, JetConstructible, WitnessConstructible};
1817

@@ -33,7 +32,7 @@ pub struct Construct<J> {
3332

3433
impl<J: Jet> Marker for Construct<J> {
3534
type CachedData = ConstructData<J>;
36-
type Witness = NoWitness;
35+
type Witness = Option<Value>;
3736
type Disconnect = Option<Arc<ConstructNode<J>>>;
3837
type SharingId = ConstructId;
3938
type Jet = J;
@@ -90,7 +89,7 @@ impl<J: Jet> ConstructNode<J> {
9089
fn convert_witness(
9190
&mut self,
9291
_: &PostOrderIterItem<&ConstructNode<J>>,
93-
_: &NoWitness,
92+
_: &Option<Value>,
9493
) -> Result<NoWitness, Self::Error> {
9594
Ok(NoWitness)
9695
}
@@ -119,6 +118,104 @@ impl<J: Jet> ConstructNode<J> {
119118
self.convert::<InternalSharing, _, _>(&mut FinalizeTypes(PhantomData))
120119
}
121120

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+
122219
/// Decode a Simplicity expression from bits, without witness data.
123220
///
124221
/// # Usage
@@ -278,10 +375,10 @@ impl<J: Jet> DisconnectConstructible<Option<Arc<ConstructNode<J>>>> for Construc
278375
}
279376
}
280377

281-
impl<J> WitnessConstructible<NoWitness> for ConstructData<J> {
282-
fn witness(inference_context: &types::Context, witness: NoWitness) -> Self {
378+
impl<J> WitnessConstructible<Option<Value>> for ConstructData<J> {
379+
fn witness(inference_context: &types::Context, _witness: Option<Value>) -> Self {
283380
ConstructData {
284-
arrow: Arrow::witness(inference_context, witness),
381+
arrow: Arrow::witness(inference_context, NoWitness),
285382
phantom: PhantomData,
286383
}
287384
}
@@ -340,7 +437,7 @@ mod tests {
340437
fn occurs_check_3() {
341438
let ctx = types::Context::new();
342439
// A similar example that caused a slightly different deadlock in the past.
343-
let wit = Arc::<ConstructNode<Core>>::witness(&ctx, NoWitness);
440+
let wit = Arc::<ConstructNode<Core>>::witness(&ctx, None);
344441
let drop = Arc::<ConstructNode<Core>>::drop_(&wit);
345442

346443
let comp1 = Arc::<ConstructNode<Core>>::comp(&drop, &drop).unwrap();

src/node/mod.rs

-2
Original file line numberDiff line numberDiff line change
@@ -78,7 +78,6 @@ mod display;
7878
mod hiding;
7979
mod inner;
8080
mod redeem;
81-
mod witness;
8281

8382
use crate::value::Word;
8483
pub use commit::{Commit, CommitData, CommitNode};
@@ -89,7 +88,6 @@ use display::DisplayExpr;
8988
pub use hiding::Hiding;
9089
pub use inner::Inner;
9190
pub use redeem::{Redeem, RedeemData, RedeemNode};
92-
pub use witness::{Witness, WitnessData, WitnessNode};
9391

9492
// This trait should only be implemented on empty types, so we can demand
9593
// every trait bound under the sun. Doing so will make #[derive]s easier

0 commit comments

Comments
 (0)