Skip to content

Commit 7b5b738

Browse files
committed
Merge BlockstreamResearch#209: Better human
05c81ba Test: Witness nodes have exactly one name (Christian Lewe) 61c5227 Test: Human encoding: Refactor parsing (Christian Lewe) b0c0ef7 Test: Human encoding: Filled holes (Christian Lewe) e8e82ac Test: Human encoding: Filled witness (Christian Lewe) b8ccec8 Test: Human encoding: Refactor finalization (Christian Lewe) 9c45abf Test: Human encoding: Move parsing test (Christian Lewe) 8a5fbbc Doc: Forest::to_witness_node (Christian Lewe) 37d728c Human encoding: Store hole name in named disc nodes (Christian Lewe) f207d6e Derive traits (Christian Lewe) Pull request description: Fixes BlockstreamResearch#181 ACKs for top commit: apoelstra: ACK 05c81ba Tree-SHA512: c73d49cf16985d99c2b1a6086be8398a036d1367eaf7e9bd00308c58f1887e54c8bb288b0188470908d443804b3860bd271deb7373daeb29ac2fe82616a906db
2 parents e8cfeeb + 05c81ba commit 7b5b738

File tree

5 files changed

+228
-108
lines changed

5 files changed

+228
-108
lines changed

src/dag.rs

+3
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ use crate::node::{self, Disconnectable, Node};
1414
/// as whether they represent `witness` or `disconnect` combinators, which
1515
/// are treated specially when working with the graph structure of
1616
/// Simplicty programs.
17+
#[derive(Clone, Debug)]
1718
pub enum Dag<T> {
1819
/// Combinator with no children
1920
Nullary,
@@ -347,6 +348,7 @@ pub trait DagLike: Sized {
347348
/// To avoid confusion, this structure cannot be directly costructed.
348349
/// Instead it is implicit in the [`DagLike::rtl_post_order_iter`]
349350
/// method.
351+
#[derive(Clone, Debug)]
350352
pub struct SwapChildren<D>(D);
351353

352354
impl<D: DagLike> DagLike for SwapChildren<D> {
@@ -510,6 +512,7 @@ pub struct PostOrderIter<D, S> {
510512
}
511513

512514
/// A set of data yielded by a `PostOrderIter`
515+
#[derive(Clone, Debug)]
513516
pub struct PostOrderIterItem<D> {
514517
/// The actual node data
515518
pub node: D,

src/human_encoding/mod.rs

+141-74
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ mod serialize;
1313

1414
use crate::dag::{DagLike, MaxSharing};
1515
use crate::jet::Jet;
16-
use crate::node::{self, CommitNode};
16+
use crate::node::{self, CommitNode, NoWitness};
1717
use crate::{Cmr, Imr, Value, WitnessNode};
1818

1919
use std::collections::HashMap;
@@ -22,7 +22,6 @@ use std::sync::Arc;
2222

2323
pub use self::error::{Error, ErrorSet};
2424
pub use self::named_node::NamedCommitNode;
25-
pub use self::parse::parse;
2625

2726
/// Line/column pair
2827
///
@@ -77,6 +76,12 @@ impl WitnessOrHole {
7776
}
7877
}
7978

79+
impl<'a> From<&'a NoWitness> for WitnessOrHole {
80+
fn from(_: &NoWitness) -> Self {
81+
WitnessOrHole::Witness
82+
}
83+
}
84+
8085
#[derive(Clone, Debug, PartialEq, Eq)]
8186
pub struct Forest<J: Jet> {
8287
roots: HashMap<Arc<str>, Arc<NamedCommitNode<J>>>,
@@ -136,7 +141,6 @@ impl<J: Jet> Forest<J> {
136141
let mut witness_lines = vec![];
137142
let mut const_lines = vec![];
138143
let mut program_lines = vec![];
139-
let mut disc_idx = 0;
140144
// Pass 1: compute string data for every node
141145
for root in self.roots.values() {
142146
for data in root.as_ref().post_order_iter::<MaxSharing<_>>() {
@@ -161,9 +165,8 @@ impl<J: Jet> Forest<J> {
161165
} else if let node::Inner::AssertL(_, cmr) = node.inner() {
162166
expr_str.push_str(" #");
163167
expr_str.push_str(&cmr.to_string());
164-
} else if let node::Inner::Disconnect(_, node::NoDisconnect) = node.inner() {
165-
expr_str.push_str(&format!(" ?disc_expr_{}", disc_idx));
166-
disc_idx += 1;
168+
} else if let node::Inner::Disconnect(_, hole_name) = node.inner() {
169+
expr_str.push_str(&format!(" ?{}", hole_name));
167170
}
168171

169172
let arrow = node.arrow();
@@ -205,6 +208,9 @@ impl<J: Jet> Forest<J> {
205208
ret
206209
}
207210

211+
/// Convert the forest into a witness node.
212+
///
213+
/// Succeeds if the forest contains a "main" root and returns `None` otherwise.
208214
pub fn to_witness_node(
209215
&self,
210216
witness: &HashMap<Arc<str>, Arc<Value>>,
@@ -217,99 +223,160 @@ impl<J: Jet> Forest<J> {
217223
#[cfg(test)]
218224
mod tests {
219225
use crate::human_encoding::Forest;
220-
use crate::jet::Core;
221-
use crate::{Error, Value};
226+
use crate::jet::{Core, Jet};
227+
use crate::{BitMachine, Value};
222228
use std::collections::HashMap;
223229
use std::sync::Arc;
224230

225-
#[test]
226-
fn to_witness_node_missing_witness() {
227-
let s = "
228-
wit1 := witness : 1 -> 2^32
229-
wit2 := witness : 1 -> 2^32
231+
fn assert_finalize_ok<J: Jet>(
232+
s: &str,
233+
witness: &HashMap<Arc<str>, Arc<Value>>,
234+
env: &J::Environment,
235+
) {
236+
let program = Forest::<J>::parse(s)
237+
.expect("Failed to parse human encoding")
238+
.to_witness_node(witness)
239+
.expect("Forest is missing expected root")
240+
.finalize()
241+
.expect("Failed to finalize");
242+
let mut mac = BitMachine::for_program(&program);
243+
mac.exec(&program, env).expect("Failed to run program");
244+
}
230245

231-
wits_are_equal := comp (pair wit1 wit2) jet_eq_32 : 1 -> 2
232-
main := comp wits_are_equal jet_verify : 1 -> 1
233-
";
234-
let forest = Forest::<Core>::parse(s).unwrap();
235-
let mut witness = HashMap::new();
236-
witness.insert(Arc::from("wit1"), Value::u32(1337));
237-
238-
match forest.to_witness_node(&witness).unwrap().finalize() {
239-
Ok(_) => panic!("Insufficient witness map should fail"),
240-
Err(Error::IncompleteFinalization) => {}
241-
Err(error) => panic!("Unexpected error {}", error),
246+
fn assert_finalize_err<J: Jet>(
247+
s: &str,
248+
witness: &HashMap<Arc<str>, Arc<Value>>,
249+
env: &J::Environment,
250+
err_msg: &'static str,
251+
) {
252+
let program = match Forest::<J>::parse(s)
253+
.expect("Failed to parse human encoding")
254+
.to_witness_node(witness)
255+
.expect("Forest is missing expected root")
256+
.finalize()
257+
{
258+
Ok(program) => program,
259+
Err(error) => {
260+
assert_eq!(&error.to_string(), err_msg);
261+
return;
262+
}
263+
};
264+
let mut mac = BitMachine::for_program(&program);
265+
match mac.exec(&program, env) {
266+
Ok(_) => panic!("Execution is expected to fail"),
267+
Err(error) => assert_eq!(&error.to_string(), err_msg),
242268
}
243269
}
244270

245271
#[test]
246-
fn parse_duplicate_witness_in_disconnected_branch() {
272+
fn filled_witness() {
247273
let s = "
248-
wit1 := witness
249-
main := comp wit1 comp disconnect iden ?dis2 unit
250-
251-
wit1 := witness
252-
dis2 := wit1
274+
a := witness
275+
b := witness
276+
main := comp
277+
comp
278+
pair a b
279+
jet_lt_8
280+
jet_verify
253281
";
254282

255-
match Forest::<Core>::parse(s) {
256-
Ok(_) => panic!("Duplicate witness names should fail"),
257-
Err(set) => {
258-
let errors: Vec<_> = set.iter().collect();
259-
assert_eq!(1, errors.len());
260-
match errors[0] {
261-
super::error::Error::NameRepeated { .. } => {}
262-
error => panic!("Unexpected error {}", error),
263-
}
264-
}
265-
}
283+
let a_less_than_b = HashMap::from([
284+
(Arc::from("a"), Value::u8(0x00)),
285+
(Arc::from("b"), Value::u8(0x01)),
286+
]);
287+
assert_finalize_ok::<Core>(s, &a_less_than_b, &());
288+
289+
let b_greater_equal_a = HashMap::from([
290+
(Arc::from("a"), Value::u8(0x01)),
291+
(Arc::from("b"), Value::u8(0x01)),
292+
]);
293+
assert_finalize_err::<Core>(s, &b_greater_equal_a, &(), "Jet failed during execution");
266294
}
267295

268296
#[test]
269-
fn to_witness_node_unfilled_hole() {
270-
let s = "
271-
wit1 := witness
272-
main := comp wit1 comp disconnect iden ?dis2 unit
273-
";
274-
let forest = Forest::<Core>::parse(s).unwrap();
275-
let witness = HashMap::new();
276-
277-
match forest.to_witness_node(&witness).unwrap().finalize() {
278-
Ok(_) => panic!("Duplicate witness names should fail"),
279-
Err(Error::IncompleteFinalization) => {}
280-
Err(error) => panic!("Unexpected error {}", error),
281-
}
297+
fn unfilled_witness() {
298+
let witness = HashMap::from([(Arc::from("wit1"), Value::u32(1337))]);
299+
assert_finalize_err::<Core>(
300+
"
301+
wit1 := witness : 1 -> 2^32
302+
wit2 := witness : 1 -> 2^32
303+
304+
wits_are_equal := comp (pair wit1 wit2) jet_eq_32 : 1 -> 2
305+
main := comp wits_are_equal jet_verify : 1 -> 1
306+
",
307+
&witness,
308+
&(),
309+
"unable to satisfy program",
310+
);
282311
}
283312

284313
#[test]
285-
fn to_witness_node_pruned_witness() {
314+
fn unfilled_witness_pruned() {
286315
let s = "
287316
wit1 := witness
288317
wit2 := witness
289318
main := comp (pair wit1 unit) case unit wit2
290319
";
291-
let forest = Forest::<Core>::parse(s).unwrap();
292-
let mut witness = HashMap::new();
293-
witness.insert(Arc::from("wit1"), Value::u1(0));
294-
295-
match forest.to_witness_node(&witness).unwrap().finalize() {
296-
Ok(_) => {}
297-
Err(e) => panic!("Unexpected error {}", e),
298-
}
320+
let wit2_is_pruned = HashMap::from([(Arc::from("wit1"), Value::u1(0))]);
321+
assert_finalize_ok::<Core>(s, &wit2_is_pruned, &());
322+
323+
let wit2_is_missing = HashMap::from([(Arc::from("wit1"), Value::u1(1))]);
324+
// FIXME The finalization should fail
325+
// This doesn't happen because we don't run the program,
326+
// so we cannot always determine which nodes must be pruned
327+
assert_finalize_err::<Core>(
328+
s,
329+
&wit2_is_missing,
330+
&(),
331+
"Execution reached a pruned branch: bf12681a76fc7c00c63e583c25cc97237337d6aca30d3f4a664075445385c648"
332+
);
333+
334+
let wit2_is_present = HashMap::from([
335+
(Arc::from("wit1"), Value::u1(1)),
336+
(Arc::from("wit2"), Value::unit()),
337+
]);
338+
assert_finalize_ok::<Core>(s, &wit2_is_present, &());
339+
}
299340

300-
witness.insert(Arc::from("wit1"), Value::u1(1));
341+
#[test]
342+
fn filled_hole() {
343+
let empty = HashMap::new();
344+
assert_finalize_ok::<Core>(
345+
"
346+
id1 := iden : 2^256 * 1 -> 2^256 * 1
347+
main := comp (disconnect id1 ?hole) unit
348+
hole := unit
349+
",
350+
&empty,
351+
&(),
352+
);
353+
}
301354

302-
match forest.to_witness_node(&witness).unwrap().finalize() {
303-
Ok(_) => {}
304-
Err(Error::IncompleteFinalization) => {}
305-
Err(e) => panic!("Unexpected error {}", e),
306-
}
355+
#[test]
356+
fn unfilled_hole() {
357+
let empty = HashMap::new();
358+
assert_finalize_err::<Core>(
359+
"
360+
wit1 := witness
361+
main := comp wit1 comp disconnect iden ?dis2 unit
362+
",
363+
&empty,
364+
&(),
365+
"unable to satisfy program",
366+
);
367+
}
307368

308-
witness.insert(Arc::from("wit2"), Value::unit());
369+
#[test]
370+
fn witness_name_override() {
371+
let s = "
372+
wit1 := witness
373+
wit2 := wit1
374+
main := comp wit2 iden
375+
";
376+
let wit1_populated = HashMap::from([(Arc::from("wit1"), Value::unit())]);
377+
assert_finalize_err::<Core>(s, &wit1_populated, &(), "unable to satisfy program");
309378

310-
match forest.to_witness_node(&witness).unwrap().finalize() {
311-
Ok(_) => {}
312-
Err(e) => panic!("Unexpected error {}", e),
313-
}
379+
let wit2_populated = HashMap::from([(Arc::from("wit2"), Value::unit())]);
380+
assert_finalize_ok::<Core>(s, &wit2_populated, &());
314381
}
315382
}

0 commit comments

Comments
 (0)