Skip to content

Commit af3d729

Browse files
committed
Merge BlockstreamResearch#59: Remove old witness parser
4ee3499 Lib: Take program text as input (Christian Lewe) 45207d9 Combine _compile and compile (Christian Lewe) 921d38a Fix: Remove unnecessary comments (Christian Lewe) 3a34bff Remove old witness file parsing (Christian Lewe) 6fb1732 Fix: Broken doc links (Christian Lewe) 0ec0422 Move IdentParser into parse module (Christian Lewe) 4d7f93d Remove `test` example program (Christian Lewe) 621cfe7 Remove old unit test (Christian Lewe) Pull request description: Remove the old witness file parser which used the Simplicity bit encoding and which was prone to fail. Temporarily disable witness expressions (during satisfaction) because we don't have the new witness file parser yet. Present error messages to prevent confusion. Refactor the library functions `compile` and `satisfy` so they are usable by the web IDE, which handles strings instead of files on disk. ACKs for top commit: apoelstra: ACK 4ee3499 Tree-SHA512: 5d9a969e42d7e68261f5a2bff720251cde11d96f4db331bd433ea2df8e3e106786d3dd48cd351f840805c1872434bac5d25ed8f168e87682bb0a73ff53abb84a
2 parents 806dfb1 + 4ee3499 commit af3d729

File tree

7 files changed

+63
-234
lines changed

7 files changed

+63
-234
lines changed

example_progs/test.simf

Lines changed: 0 additions & 4 deletions
This file was deleted.

example_progs/test.wit

Lines changed: 0 additions & 3 deletions
This file was deleted.

src/error.rs

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,8 @@ use std::sync::Arc;
33

44
use simplicity::elements;
55

6-
use crate::parse::{Identifier, JetName, MatchPattern, Position, Span, WitnessName};
6+
use crate::parse::{Identifier, JetName, MatchPattern, Position, Rule, Span, WitnessName};
77
use crate::types::{ResolvedType, UIntType};
8-
use crate::Rule;
98

109
/// Helper trait to convert `Result<T, E>` into `Result<T, RichError>`.
1110
pub trait WithSpan<T> {

src/lib.rs

Lines changed: 25 additions & 199 deletions
Original file line numberDiff line numberDiff line change
@@ -15,144 +15,43 @@ pub mod pattern;
1515
pub mod types;
1616
pub mod value;
1717

18-
use std::{collections::HashMap, path::Path, sync::Arc};
18+
use std::sync::Arc;
1919

20-
use named::{ConstructExt, Named};
21-
use pest::Parser;
22-
use pest_derive::Parser;
23-
use simplicity::{
24-
dag::{NoSharing, PostOrderIterItem},
25-
jet::Elements,
26-
node::{Commit, Converter, Inner, Node, Redeem, RedeemData},
27-
BitIter, CommitNode, RedeemNode,
28-
};
20+
use named::ConstructExt;
21+
use simplicity::{dag::NoSharing, jet::Elements, node::Redeem, CommitNode, RedeemNode};
2922

3023
pub extern crate simplicity;
3124
pub use simplicity::elements;
25+
use simplicity::node::SimpleFinalizer;
3226

33-
use crate::{
34-
error::{RichError, WithFile},
35-
named::{NamedCommitNode, NamedExt},
36-
parse::PestParse,
37-
};
27+
use crate::{error::WithFile, named::NamedExt};
3828

39-
#[derive(Parser)]
40-
#[grammar = "minimal.pest"]
41-
pub struct IdentParser;
42-
43-
pub fn _compile(file: &Path) -> Result<Arc<Node<Named<Commit<Elements>>>>, String> {
44-
let file = Arc::from(std::fs::read_to_string(file).unwrap());
45-
let parse_program = IdentParser::parse(Rule::program, &file)
46-
.map_err(RichError::from)
47-
.and_then(|mut pairs| parse::Program::parse(pairs.next().unwrap()))
48-
.with_file(file.clone())?;
49-
let ast_program = ast::Program::analyze(&parse_program).with_file(file.clone())?;
50-
let simplicity_named_commit = ast_program.compile().with_file(file.clone())?;
51-
let simplicity_redeem = simplicity_named_commit
29+
pub fn compile(prog_text: &str) -> Result<Arc<CommitNode<Elements>>, String> {
30+
let parse_program = parse::Program::parse(prog_text)?;
31+
let ast_program = ast::Program::analyze(&parse_program).with_file(prog_text)?;
32+
let simplicity_named_construct = ast_program.compile().with_file(prog_text)?;
33+
let simplicity_named_commit = simplicity_named_construct
5234
.finalize_types_main()
53-
.expect("Type check error");
54-
Ok(simplicity_redeem)
55-
}
56-
57-
pub fn compile(file: &Path) -> Result<CommitNode<Elements>, String> {
58-
let simplicity_named_commit = _compile(file)?;
59-
Ok(Arc::try_unwrap(simplicity_named_commit.to_commit_node()).unwrap())
35+
.expect("Failed to set program source and target type to unit");
36+
Ok(simplicity_named_commit.to_commit_node())
6037
}
6138

62-
pub fn satisfy(prog: &Path, wit_file: &Path) -> Result<RedeemNode<Elements>, String> {
63-
#[derive(serde::Serialize, serde::Deserialize)]
64-
#[serde(transparent)]
65-
struct WitFileData {
66-
map: HashMap<String, String>,
67-
}
68-
69-
impl Converter<Named<Commit<Elements>>, Redeem<Elements>> for WitFileData {
70-
type Error = std::convert::Infallible;
71-
72-
fn convert_witness(
73-
&mut self,
74-
data: &PostOrderIterItem<&NamedCommitNode>,
75-
_witness: &<Named<Commit<Elements>> as simplicity::node::Marker>::Witness,
76-
) -> Result<<Redeem<Elements> as simplicity::node::Marker>::Witness, Self::Error> {
77-
let key = data.node.name();
78-
let ty = &data.node.arrow().target;
79-
match self.map.get(key.as_ref()) {
80-
Some(wit) => {
81-
let bytes: Vec<u8> = hex_conservative::FromHex::from_hex(wit).unwrap();
82-
let total_bit_len = bytes.len() * 8;
83-
let mut bit_iter = BitIter::new(bytes.into_iter());
84-
let value = bit_iter.read_value(&data.node.arrow().target);
85-
let v = match value {
86-
Ok(v) => v,
87-
Err(e) => panic!("Error reading witness: {:?}", e),
88-
};
89-
// TODO: Make sure that remaining iterator is empty or all zeros till the specified remaining len.
90-
let bit_len = ty.bit_width();
91-
let remaining = total_bit_len - bit_len;
92-
assert!(remaining < 8);
93-
for _ in 0..remaining {
94-
assert!(!bit_iter.next().unwrap());
95-
}
96-
assert!(bit_iter.next().is_none());
97-
Ok(v)
98-
}
99-
None => panic!("Value not found{}", key),
100-
}
101-
}
102-
103-
fn convert_disconnect(
104-
&mut self,
105-
_data: &PostOrderIterItem<&NamedCommitNode>,
106-
_maybe_converted: Option<&Arc<simplicity::node::Node<Redeem<Elements>>>>,
107-
_disconnect: &<Named<Commit<Elements>> as simplicity::node::Marker>::Disconnect,
108-
) -> Result<<Redeem<Elements> as simplicity::node::Marker>::Disconnect, Self::Error>
109-
{
110-
todo!()
111-
}
112-
113-
fn convert_data(
114-
&mut self,
115-
data: &PostOrderIterItem<&NamedCommitNode>,
116-
inner: Inner<
117-
&Arc<simplicity::node::Node<Redeem<Elements>>>,
118-
<Redeem<Elements> as simplicity::node::Marker>::Jet,
119-
&<Redeem<Elements> as simplicity::node::Marker>::Disconnect,
120-
&<Redeem<Elements> as simplicity::node::Marker>::Witness,
121-
>,
122-
) -> Result<<Redeem<Elements> as simplicity::node::Marker>::CachedData, Self::Error>
123-
{
124-
let converted_data = inner
125-
.map(|node| node.cached_data())
126-
.map_disconnect(|node| node.cached_data())
127-
.map_witness(Arc::clone);
128-
Ok(Arc::new(RedeemData::new(
129-
data.node.arrow().shallow_clone(),
130-
converted_data,
131-
)))
132-
}
133-
}
134-
135-
let simplicity_named_commit = _compile(prog)?;
136-
let simplicity_named_commit =
137-
Arc::<_>::try_unwrap(simplicity_named_commit).expect("Only one reference to commit node");
138-
139-
let file = std::fs::File::open(wit_file).expect("Error opening witness file");
140-
let rdr = std::io::BufReader::new(file);
141-
let mut wit_data: WitFileData =
142-
serde_json::from_reader(rdr).expect("Error reading witness file");
143-
144-
let simplicity_redeem = simplicity_named_commit
145-
.convert::<NoSharing, Redeem<Elements>, _>(&mut wit_data)
146-
.unwrap();
147-
Ok(Arc::try_unwrap(simplicity_redeem).unwrap())
39+
pub fn satisfy(prog_text: &str) -> Result<Arc<RedeemNode<Elements>>, String> {
40+
let simplicity_named_commit = compile(prog_text)?;
41+
let mut finalizer = SimpleFinalizer::new(std::iter::empty());
42+
simplicity_named_commit
43+
.convert::<NoSharing, Redeem<Elements>, _>(&mut finalizer)
44+
.map_err(|_| {
45+
"Witness expressions are temporarily not supported. Cannot satisfy.".to_string()
46+
})
14847
}
14948

15049
#[cfg(test)]
15150
mod tests {
15251
use base64::display::Base64Display;
15352
use base64::engine::general_purpose::STANDARD;
154-
use simplicity::node::{CoreConstructible as _, JetConstructible as _};
155-
use simplicity::{encode, BitMachine, BitWriter, Cmr, Value};
53+
use simplicity::{encode, BitMachine, BitWriter};
54+
use std::path::Path;
15655

15756
use crate::*;
15857

@@ -171,71 +70,21 @@ mod tests {
17170
_test_progs("./example_progs/sighash_all.simf");
17271
_test_progs("./example_progs/sighash_all_anyprevoutanyscript.simf");
17372
_test_progs("./example_progs/sighash_none.simf");
174-
_test_progs("./example_progs/test.simf");
17573
_test_progs("./example_progs/tuple.simf");
17674
_test_progs("./example_progs/unwrap.simf");
17775
}
17876

17977
fn _test_progs(file: &str) {
18078
println!("Testing {file}");
181-
let file = Path::new(file);
182-
let simplicity_named_commit = match _compile(file) {
79+
let path = Path::new(file);
80+
let text = std::fs::read_to_string(path).unwrap();
81+
let redeem_prog = match satisfy(&text) {
18382
Ok(commit) => commit,
18483
Err(error) => {
18584
panic!("{error}");
18685
}
18786
};
18887

189-
struct MyConverter;
190-
191-
impl Converter<Named<Commit<Elements>>, Redeem<Elements>> for MyConverter {
192-
type Error = std::convert::Infallible;
193-
194-
fn convert_witness(
195-
&mut self,
196-
_data: &PostOrderIterItem<&NamedCommitNode>,
197-
_witness: &<Named<Commit<Elements>> as simplicity::node::Marker>::Witness,
198-
) -> Result<<Redeem<Elements> as simplicity::node::Marker>::Witness, Self::Error>
199-
{
200-
Ok(Value::u32(20))
201-
}
202-
203-
fn convert_disconnect(
204-
&mut self,
205-
_data: &PostOrderIterItem<&NamedCommitNode>,
206-
_maybe_converted: Option<&Arc<simplicity::node::Node<Redeem<Elements>>>>,
207-
_disconnect: &<Named<Commit<Elements>> as simplicity::node::Marker>::Disconnect,
208-
) -> Result<<Redeem<Elements> as simplicity::node::Marker>::Disconnect, Self::Error>
209-
{
210-
todo!()
211-
}
212-
213-
fn convert_data(
214-
&mut self,
215-
data: &PostOrderIterItem<&NamedCommitNode>,
216-
inner: Inner<
217-
&Arc<simplicity::node::Node<Redeem<Elements>>>,
218-
<Redeem<Elements> as simplicity::node::Marker>::Jet,
219-
&<Redeem<Elements> as simplicity::node::Marker>::Disconnect,
220-
&<Redeem<Elements> as simplicity::node::Marker>::Witness,
221-
>,
222-
) -> Result<<Redeem<Elements> as simplicity::node::Marker>::CachedData, Self::Error>
223-
{
224-
let converted_data = inner
225-
.map(|node| node.cached_data())
226-
.map_disconnect(|node| node.cached_data())
227-
.map_witness(Arc::clone);
228-
Ok(Arc::new(RedeemData::new(
229-
data.node.arrow().shallow_clone(),
230-
converted_data,
231-
)))
232-
}
233-
}
234-
235-
let redeem_prog = simplicity_named_commit
236-
.convert::<NoSharing, Redeem<Elements>, _>(&mut MyConverter)
237-
.unwrap();
238-
23988
let mut vec = Vec::new();
24089
let mut writer = BitWriter::new(&mut vec);
24190
let _encoded = encode::encode_program(&redeem_prog, &mut writer).unwrap();
@@ -248,27 +97,4 @@ mod tests {
24897
.exec(&redeem_prog, &env)
24998
.expect("Machine execution failure");
25099
}
251-
252-
#[test]
253-
fn temp_progs() {
254-
let inp = ProgNode::const_word(Value::u32(10));
255-
let node = ProgNode::jet(Elements::ParseLock);
256-
println!("l1: {}", node.arrow());
257-
let node = ProgNode::comp(&inp, &node).unwrap();
258-
println!("l2: {}", node.arrow());
259-
let node = ProgNode::pair(&node, &ProgNode::unit()).unwrap();
260-
println!("l3: {}", node.arrow());
261-
let later_operation = ProgNode::take(&ProgNode::unit());
262-
println!("l4: {}", later_operation.arrow());
263-
let assert_node = ProgNode::assertl(&later_operation, Cmr::unit()).unwrap();
264-
println!("l5: {}", assert_node.arrow());
265-
let comp = ProgNode::comp(&node, &assert_node).unwrap();
266-
println!("l6: {}", comp.arrow());
267-
// let node2 = ProgNode::assert(&node, Cmr::unit()).unwrap();
268-
// println!("l3: {}", node2.arrow());
269-
// let node3 = ProgNode::comp(&ProgNode::pair(&ProgNode::unit(), &ProgNode::unit()).unwrap(), &node2).unwrap();
270-
// println!("l4: {}", node3.arrow());
271-
let res = comp.finalize_types_main().unwrap();
272-
dbg!(&res);
273-
}
274100
}

src/main.rs

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -15,31 +15,32 @@ fn main() {
1515
}
1616

1717
fn run() -> Result<(), String> {
18-
// Get the command-line arguments as a Vec<String>.
1918
let args: Vec<String> = env::args().collect();
2019

21-
// Check if at least two arguments are provided.
2220
if args.len() < 2 {
2321
println!("Usage: {} <prog.simpl> [sig.wit (optional)]", args[0]);
2422
println!("If no witness file is provided, the program will be compiled and printed.");
2523
println!("If a witness file is provided, the program will be satisfied and printed.");
2624
return Ok(());
2725
}
2826

29-
// Extract the first argument (arg1).
3027
let prog_file = &args[1];
3128
let prog_path = std::path::Path::new(prog_file);
29+
let prog_text = std::fs::read_to_string(prog_path).map_err(|e| e.to_string())?;
3230

33-
// Check if a second argument (arg2) is provided.
3431
if args.len() >= 3 {
35-
let witness_file = &args[2];
36-
let wit_path = std::path::Path::new(witness_file);
37-
let res = satisfy(prog_path, wit_path)?;
32+
// TODO: Re-enable witness file parsing
33+
println!(
34+
"Warning: Witness expressions are temporarily disabled. Skipping the witness file..."
35+
);
36+
// let witness_file = &args[2];
37+
// let wit_path = std::path::Path::new(witness_file);
38+
let res = satisfy(&prog_text)?;
3839
let redeem_bytes = res.encode_to_vec();
3940
println!("{}", Base64Display::new(&redeem_bytes, &STANDARD));
4041
} else {
4142
// No second argument is provided. Just compile the program.
42-
let prog = compile(prog_path)?;
43+
let prog = compile(&prog_text)?;
4344
let res = prog.encode_to_vec();
4445
println!("{}", Base64Display::new(&res, &STANDARD));
4546
}

src/parse.rs

Lines changed: 25 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -8,13 +8,18 @@ use std::sync::Arc;
88

99
use either::Either;
1010
use miniscript::iter::{Tree, TreeLike};
11+
use pest::Parser;
12+
use pest_derive::Parser;
1113
use simplicity::elements::hex::FromHex;
1214

13-
use crate::error::{Error, RichError, WithSpan};
15+
use crate::error::{Error, RichError, WithFile, WithSpan};
1416
use crate::num::NonZeroPow2Usize;
1517
use crate::pattern::Pattern;
1618
use crate::types::{AliasedType, BuiltinAlias, TypeConstructible, UIntType};
17-
use crate::Rule;
19+
20+
#[derive(Parser)]
21+
#[grammar = "minimal.pest"]
22+
pub struct IdentParser;
1823

1924
/// Position of an object inside a file.
2025
///
@@ -484,22 +489,27 @@ impl fmt::Display for MatchPattern {
484489
}
485490
}
486491

487-
pub trait PestParse: Sized {
492+
trait PestParse: Sized {
488493
fn parse(pair: pest::iterators::Pair<Rule>) -> Result<Self, RichError>;
489494
}
490495

491-
impl PestParse for Program {
492-
fn parse(pair: pest::iterators::Pair<Rule>) -> Result<Self, RichError> {
493-
assert!(matches!(pair.as_rule(), Rule::program));
494-
let mut stmts = Vec::new();
495-
for inner_pair in pair.into_inner() {
496-
match inner_pair.as_rule() {
497-
Rule::statement => stmts.push(Statement::parse(inner_pair)?),
498-
Rule::EOI => (),
499-
_ => unreachable!(),
500-
};
501-
}
502-
Ok(Program { statements: stmts })
496+
impl Program {
497+
pub fn parse(file: &str) -> Result<Self, RichError> {
498+
let mut pairs = IdentParser::parse(Rule::program, file)
499+
.map_err(RichError::from)
500+
.with_file(file)?;
501+
let pair = pairs.next().unwrap();
502+
503+
debug_assert!(matches!(pair.as_rule(), Rule::program));
504+
let statements = pair
505+
.into_inner()
506+
.filter_map(|pair| match pair.as_rule() {
507+
Rule::statement => Some(Statement::parse(pair)),
508+
Rule::EOI => None,
509+
_ => unreachable!("Corrupt grammar"),
510+
})
511+
.collect::<Result<Vec<Statement>, RichError>>()?;
512+
Ok(Program { statements })
503513
}
504514
}
505515

0 commit comments

Comments
 (0)