Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .github/workflows/rust.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ on: # yamllint disable-line rule:truthy
branches:
- master
- 'test-ci/**'
workflow_dispatch:

name: Continuous integration

Expand Down
58 changes: 54 additions & 4 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ pest = "2.1.3"
pest_derive = "2.7.1"
serde = { version = "1.0.188", features = ["derive"], optional = true }
serde_json = { version = "1.0.105", optional = true }
serde_yaml = "0.8"
simplicity-lang = { version = "0.7.0" }
miniscript = "12.3.1"
either = "1.12.0"
Expand Down
26 changes: 14 additions & 12 deletions src/ast.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1442,24 +1442,26 @@ fn analyze_named_module(
ModuleItem::Module(module) if module.name == name => Some(module),
_ => None,
});
let witness_module = iter
.next()
.ok_or(Error::ModuleRequired(name.shallow_clone()))
.with_span(from)?;

// witness file
let witness_module = iter.next();
if iter.next().is_some() {
return Err(Error::ModuleRedefined(name)).with_span(from);
}
let mut map = HashMap::new();
for assignment in witness_module.assignments() {
if map.contains_key(assignment.name()) {
return Err(Error::WitnessReassigned(assignment.name().shallow_clone()))
.with_span(assignment);
if let Some(witness_module) = witness_module {
for assignment in witness_module.assignments() {
if map.contains_key(assignment.name()) {
return Err(Error::WitnessReassigned(assignment.name().shallow_clone()))
.with_span(assignment);
}
map.insert(
assignment.name().shallow_clone(),
assignment.value().clone(),
);
}
map.insert(
assignment.name().shallow_clone(),
assignment.value().clone(),
);
}

Ok(map)
}

Expand Down
25 changes: 25 additions & 0 deletions src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -339,6 +339,11 @@ pub enum Error {
ModuleRedefined(ModuleName),
ArgumentMissing(WitnessName),
ArgumentTypeMismatch(WitnessName, ResolvedType, ResolvedType),
InvalidJsonFormat(String),
UndefinedWitness(WitnessName),
UndefinedParameter(WitnessName),
WitnessMultipleAssignments(WitnessName),
ArgumentMultipleAssignments(WitnessName),
}

#[rustfmt::skip]
Expand Down Expand Up @@ -481,6 +486,26 @@ impl fmt::Display for Error {
f,
"Parameter `{name}` was declared with type `{declared}` but its assigned argument is of type `{assigned}`"
),
Error::InvalidJsonFormat(msg) => write!(
f,
"Invalid JSON format: {msg}"
),
Error::UndefinedWitness(name) => write!(
f,
"Witness `{name}` is not defined in the compiled program"
),
Error::UndefinedParameter(name) => write!(
f,
"Parameter `{name}` is not defined in the program parameters"
),
Error::WitnessMultipleAssignments(name) => write!(
f,
"Witness `{name}` is assigned multiple times"
),
Error::ArgumentMultipleAssignments(name) => write!(
f,
"Parameter `{name}` is assigned multiple times"
),
}
}
}
Expand Down
19 changes: 11 additions & 8 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,11 @@ impl CompiledProgram {
&self.debug_symbols
}

/// Access the witness types declared in the SimplicityHL program.
pub fn witness_types(&self) -> &WitnessTypes {
&self.witness_types
}

/// Access the Simplicity target code, without witness data.
pub fn commit(&self) -> Arc<CommitNode<Elements>> {
named::forget_names(&self.simplicity)
Expand Down Expand Up @@ -305,10 +310,9 @@ pub(crate) mod tests {
arguments_file_path: P,
) -> TestCase<CompiledProgram> {
let arguments_text = std::fs::read_to_string(arguments_file_path).unwrap();
let arguments = match serde_json::from_str::<Arguments>(&arguments_text) {
Ok(x) => x,
Err(error) => panic!("{error}"),
};
let arguments =
Arguments::from_file_with_types(&arguments_text, self.program.parameters())
.expect("Failed to parse arguments from JSON with types");
self.with_arguments(arguments)
}

Expand Down Expand Up @@ -343,10 +347,9 @@ pub(crate) mod tests {
witness_file_path: P,
) -> TestCase<SatisfiedProgram> {
let witness_text = std::fs::read_to_string(witness_file_path).unwrap();
let witness_values = match serde_json::from_str::<WitnessValues>(&witness_text) {
Ok(x) => x,
Err(error) => panic!("{error}"),
};
let witness_values =
WitnessValues::from_file_with_types(&witness_text, self.program.witness_types())
.expect("Failed to parse witness values from JSON with types");
self.with_witness_values(witness_values)
}

Expand Down
29 changes: 21 additions & 8 deletions src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
Arg::new("wit_file")
.value_name("WITNESS_FILE")
.action(ArgAction::Set)
.help("File containing the witness data"),
.help("File containing the witness data (YAML or JSON format)"),
)
.arg(
Arg::new("debug")
Expand All @@ -71,16 +71,27 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {

let compiled = CompiledProgram::new(prog_text, Arguments::default(), include_debug_symbols)?;

// Process witness file if provided
#[cfg(feature = "serde")]
let witness_opt = matches
.get_one::<String>("wit_file")
.map(|wit_file| -> Result<simplicityhl::WitnessValues, String> {
let wit_path = std::path::Path::new(wit_file);
let wit_text = std::fs::read_to_string(wit_path).map_err(|e| e.to_string())?;
let witness = serde_json::from_str::<simplicityhl::WitnessValues>(&wit_text).unwrap();
Ok(witness)

// Parse witness file - tries JSON first, then YAML as fallback
// Both formats use compiler-provided type information
#[allow(clippy::needless_borrow)] // clippy sends needless_borrow which is false
{
simplicityhl::WitnessValues::from_file_with_types(
&wit_text,
&compiled.witness_types(),
)
.map_err(|e| e.to_string())
}
})
.transpose()?;

#[cfg(not(feature = "serde"))]
let witness_opt = if matches.contains_id("wit_file") {
return Err(
Expand Down Expand Up @@ -109,12 +120,14 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
};

if output_json {
#[cfg(not(feature = "serde"))]
return Err(
"Program was compiled without the 'serde' feature and cannot output JSON.".into(),
);
#[cfg(feature = "serde")]
println!("{}", serde_json::to_string(&output)?);
{
println!("{}", serde_json::to_string_pretty(&output)?);
}
#[cfg(not(feature = "serde"))]
{
return Err("JSON output requires 'serde' feature".into());
}
} else {
println!("{}", output);
}
Expand Down
Loading