Skip to content

Commit 7da0f37

Browse files
committed
Merge #265: Remove WitnessNode
61b7ddc refactor: Remove WitnessNode (Christian Lewe) 3be0fd9 refactor: Remove Error::NoMoreWitnesses (Christian Lewe) Pull request description: Remove `WitnessNode` by merging its code with the nearly identical `ConstructNode`. ACKs for top commit: apoelstra: ACK 61b7ddc; successfully ran local tests Tree-SHA512: 1ab95ad4b73552cfa9285f7aeba10faa2c4936f708f722667c3c44f3ce1463351ca23319129fe0aa6640004ce665bb23fc6faa754c1c9ea81b71e83d06f62bb0
2 parents 211a30f + 61b7ddc commit 7da0f37

File tree

14 files changed

+199
-382
lines changed

14 files changed

+199
-382
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-5
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;
@@ -88,8 +88,6 @@ pub enum Error {
8888
Type(types::Error),
8989
// Execution error
9090
Execution(bit_machine::ExecutionError),
91-
/// Witness iterator ended early
92-
NoMoreWitnesses,
9391
/// Tried to parse a jet but the name wasn't recognized
9492
InvalidJetName(String),
9593
/// Policy error
@@ -107,7 +105,6 @@ impl fmt::Display for Error {
107105
Error::Type(ref e) => fmt::Display::fmt(e, f),
108106
Error::Execution(ref e) => fmt::Display::fmt(e, f),
109107
Error::InvalidJetName(s) => write!(f, "unknown jet `{}`", s),
110-
Error::NoMoreWitnesses => f.write_str("no more witness data available"),
111108
#[cfg(feature = "elements")]
112109
Error::Policy(ref e) => fmt::Display::fmt(e, f),
113110
}
@@ -121,7 +118,6 @@ impl std::error::Error for Error {
121118
Error::DisconnectRedeemTime => None,
122119
Error::Type(ref e) => Some(e),
123120
Error::Execution(ref e) => Some(e),
124-
Error::NoMoreWitnesses => None,
125121
Error::InvalidJetName(..) => None,
126122
#[cfg(feature = "elements")]
127123
Error::Policy(ref e) => Some(e),

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/convert.rs

+9-2
Original file line numberDiff line numberDiff line change
@@ -152,6 +152,10 @@ pub trait Converter<N: Marker, M: Marker> {
152152
/// Does not do any type-checking and may attach an invalid witness to a combinator.
153153
///
154154
/// If it encounters a disconnect node, it simply returns an error.
155+
///
156+
/// **This finalizer should not be used in production.
157+
/// It introduces default values ([`Value::zero`]) to handle missing witness data,
158+
/// which might trigger unexpected spending paths.**
155159
// FIXME we should do type checking, but this would require a method to check
156160
// type compatibility between a Value and a type::Final.
157161
pub struct SimpleFinalizer<W: Iterator<Item = Value>> {
@@ -169,10 +173,13 @@ impl<W: Iterator<Item = Value>, J: Jet> Converter<Commit<J>, Redeem<J>> for Simp
169173

170174
fn convert_witness(
171175
&mut self,
172-
_: &PostOrderIterItem<&CommitNode<J>>,
176+
data: &PostOrderIterItem<&CommitNode<J>>,
173177
_: &NoWitness,
174178
) -> Result<Value, Self::Error> {
175-
self.iter.next().ok_or(crate::Error::NoMoreWitnesses)
179+
Ok(self
180+
.iter
181+
.next()
182+
.unwrap_or_else(|| Value::zero(&data.node.arrow().target)))
176183
}
177184

178185
fn convert_disconnect(

0 commit comments

Comments
 (0)