Skip to content
Merged
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
5 changes: 1 addition & 4 deletions examples/p2pk.args
Original file line number Diff line number Diff line change
@@ -1,6 +1,3 @@
{
"ALICE_PUBLIC_KEY": {
"value": "0x79be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798",
"type": "Pubkey"
}
"ALICE_PUBLIC_KEY": "0x79be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798"
}
5 changes: 1 addition & 4 deletions examples/p2pk.wit
Original file line number Diff line number Diff line change
@@ -1,6 +1,3 @@
{
"ALICE_SIGNATURE": {
"value": "0xf74b3ca574647f8595624b129324afa2f38b598a9c1c7cfc5f08a9c036ec5acd3c0fbb9ed3dae5ca23a0a65a34b5d6cccdd6ba248985d6041f7b21262b17af6f",
"type": "Signature"
}
"ALICE_SIGNATURE": "0xf74b3ca574647f8595624b129324afa2f38b598a9c1c7cfc5f08a9c036ec5acd3c0fbb9ed3dae5ca23a0a65a34b5d6cccdd6ba248985d6041f7b21262b17af6f"
}
8 changes: 4 additions & 4 deletions src/driver/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@
//!
//! SimplicityHL programs begin execution at a single entry point file,
//! which the driver registers internally as the root [`MAIN_MODULE`].
//! This is typically the file passed as the root to the [`DependencyGraph::new`] function.
//! This is typically the file passed as the root to the [`DependencyGraph::build_program`] function.
//!
//! External libraries are explicitly linked using the `--dep` flag. The driver
//! resolves and parses these external files relative to the entry point during
Expand Down Expand Up @@ -319,7 +319,7 @@ impl DependencyGraph {
///
/// Results are cached in `use_cache` to avoid redundant filesystem lookups during
/// later construction phases.
/// Note: This is a specialized helper designed exclusively for [`DependencyGraph::new`].
/// Note: This is a specialized helper designed exclusively for `DependencyGraph::build_graph`.
fn resolve_imports(
current_program: &parse::Program,
current_module: &CurrentModule,
Expand All @@ -342,7 +342,7 @@ impl DependencyGraph {
}

/// PHASE 2 OF GRAPH CONSTRUCTION: Loads, parses, and registers new dependencies.
/// Note: This is a specialized helper designed exclusively for [`DependencyGraph::new`].
/// Note: This is a specialized helper designed exclusively for `DependencyGraph::build_graph`.
fn load_and_parse_dependencies(
&mut self,
current: &CurrentModule,
Expand Down Expand Up @@ -497,7 +497,7 @@ impl<'a> ImportContext<'a> {
}

/// Shared mutable state threaded through dependency loading.
/// Lives only for the duration of [`DependencyGraph::new`].
/// Lives only for the duration of `DependencyGraph::discover_dependencies`.
struct LoadContext<'a> {
invalid_imports: &'a mut HashSet<CanonPath>,
diagnostics: &'a mut DiagnosticManager,
Expand Down
19 changes: 17 additions & 2 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,8 @@ use crate::source::CanonSourceFile;
pub use crate::types::ResolvedType;
pub use crate::unstable::{UnstableFeature, UnstableFeatures};
pub use crate::value::Value;
#[cfg(feature = "serde")]
pub use crate::witness::UnresolvedValues;
pub use crate::witness::{Arguments, Parameters, WitnessTypes, WitnessValues};

/// The template of a SimplicityHL program.
Expand Down Expand Up @@ -323,6 +325,11 @@ impl CompiledProgram {
version::SimcDirective::current_version()
}

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

/// Satisfy the SimplicityHL program with the given `witness_values`.
///
/// ## Errors
Expand Down Expand Up @@ -672,7 +679,11 @@ 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) {
let unresolved = match serde_json::from_str::<UnresolvedValues>(&arguments_text) {
Ok(x) => x,
Err(error) => panic!("{error}"),
};
let arguments = match unresolved.resolve(self.program.parameters()) {
Ok(x) => x,
Err(error) => panic!("{error}"),
};
Expand Down Expand Up @@ -734,7 +745,11 @@ 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) {
let unresolved = match serde_json::from_str::<UnresolvedValues>(&witness_text) {
Ok(x) => x,
Err(error) => panic!("{error}"),
};
let witness_values = match unresolved.resolve(self.program.witness_types()) {
Ok(x) => x,
Err(error) => panic!("{error}"),
};
Expand Down
54 changes: 36 additions & 18 deletions src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ use simplicityhl::ast::ElementsJetHinter;
use simplicityhl::version::SimcDirective;
use simplicityhl::{
resolution::DependencyMapBuilder, source::CanonPath, source::CanonSourceFile, AbiMeta,
CompiledProgram,
TemplateProgram,
};
use simplicityhl::{UnstableFeature, UnstableFeatures};
use std::path::Path;
Expand Down Expand Up @@ -132,24 +132,25 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
UnstableFeatures::new(features.copied())
});

// Argument values are resolved against the program's parameter types,
// so the file is parsed here and resolved once the template is built.
#[cfg(feature = "serde")]
let args_opt: simplicityhl::Arguments = match matches.get_one::<String>("args_file") {
None => simplicityhl::Arguments::default(),
Some(args_file) => {
let args_path = Path::new(&args_file);
let args_text = std::fs::read_to_string(args_path).map_err(|e| e.to_string())?;
serde_json::from_str::<simplicityhl::Arguments>(&args_text)?
}
};
let unresolved_args: Option<simplicityhl::UnresolvedValues> =
match matches.get_one::<String>("args_file") {
None => None,
Some(args_file) => {
let args_path = Path::new(&args_file);
let args_text = std::fs::read_to_string(args_path).map_err(|e| e.to_string())?;
Some(serde_json::from_str(&args_text)?)
}
};
#[cfg(not(feature = "serde"))]
let args_opt: simplicityhl::Arguments = if matches.contains_id("args_file") {
if matches.contains_id("args_file") {
return Err(
"Program was compiled without the 'serde' feature and cannot process .args files."
.into(),
);
} else {
simplicityhl::Arguments::default()
};
}

let dep_args = matches
.get_many::<String>("dependencies")
Expand Down Expand Up @@ -195,14 +196,30 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
};

let source = CanonSourceFile::new(main_path.clone(), std::sync::Arc::from(main_text));
let compiled = match CompiledProgram::new_with_dep(
let template = match TemplateProgram::new_with_dep(
source,
&dependencies,
&unstable_features,
args_opt,
include_debug_symbols,
Box::new(ElementsJetHinter::new()),
) {
Ok(program) => program,
Err(e) => {
// `Display` is message-only; render for source snippets with
// file and line pointers, as the single-file path does.
eprintln!("{}", e.render_to_string());
std::process::exit(1);
}
};

#[cfg(feature = "serde")]
let args_opt: simplicityhl::Arguments = match unresolved_args {
None => simplicityhl::Arguments::default(),
Some(unresolved) => unresolved.resolve(template.parameters())?,
};
#[cfg(not(feature = "serde"))]
let args_opt = simplicityhl::Arguments::default();

let compiled = match template.instantiate(args_opt, include_debug_symbols) {
Ok(program) => program,
Err(e) => {
eprintln!("{}", e);
Expand All @@ -216,8 +233,9 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
.map(|wit_file| -> Result<simplicityhl::WitnessValues, String> {
let wit_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)
let unresolved = serde_json::from_str::<simplicityhl::UnresolvedValues>(&wit_text)
.map_err(|e| e.to_string())?;
unresolved.resolve(compiled.witness_types())
})
.transpose()?;
#[cfg(not(feature = "serde"))]
Expand Down
69 changes: 61 additions & 8 deletions src/serde.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,25 +5,32 @@ use crate::parse::ParseFromStr;
use crate::str::WitnessName;
use crate::types::ResolvedType;
use crate::value::Value;
use crate::witness::{Arguments, WitnessValues};
use crate::witness::{Arguments, UnresolvedValue, UnresolvedValues, WitnessValues};
use crate::{AbiMeta, Parameters, WitnessTypes};
use serde::{de, ser::SerializeMap, Deserialize, Deserializer, Serialize, Serializer};

struct WitnessMapVisitor;
/// Visitor for a map from witness names to values of type `V`, rejecting duplicate names.
struct NamedMapVisitor<V>(std::marker::PhantomData<V>);

impl<'de> de::Visitor<'de> for WitnessMapVisitor {
type Value = HashMap<WitnessName, Value>;
impl<V> NamedMapVisitor<V> {
const fn new() -> Self {
Self(std::marker::PhantomData)
}
}

impl<'de, V: Deserialize<'de>> de::Visitor<'de> for NamedMapVisitor<V> {
type Value = HashMap<WitnessName, V>;

fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
formatter.write_str("a map with string keys and value-map values")
formatter.write_str("a map with string keys")
}

fn visit_map<M>(self, mut access: M) -> Result<Self::Value, M::Error>
where
M: de::MapAccess<'de>,
{
let mut map = HashMap::new();
while let Some((key, value)) = access.next_entry::<WitnessName, Value>()? {
while let Some((key, value)) = access.next_entry::<WitnessName, V>()? {
if map.insert(key.shallow_clone(), value).is_some() {
return Err(de::Error::custom(format!("Name `{key}` is assigned twice")));
}
Expand All @@ -38,11 +45,57 @@ impl<'de> Deserialize<'de> for WitnessValues {
D: Deserializer<'de>,
{
deserializer
.deserialize_map(WitnessMapVisitor)
.deserialize_map(NamedMapVisitor::<Value>::new())
.map(Self::from)
}
}

struct UnresolvedValueVisitor;

impl<'de> de::Visitor<'de> for UnresolvedValueVisitor {
type Value = UnresolvedValue;

fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
formatter.write_str("a value string or a map with \"value\" and \"type\" fields")
}

fn visit_str<E>(self, value: &str) -> Result<Self::Value, E>
where
E: de::Error,
{
Ok(UnresolvedValue::Untyped(value.to_owned()))
}

fn visit_map<M>(self, access: M) -> Result<Self::Value, M::Error>
where
M: de::MapAccess<'de>,
{
ValueMapVisitor
.visit_map(access)
.map(UnresolvedValue::Typed)
}
}

impl<'de> Deserialize<'de> for UnresolvedValue {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: Deserializer<'de>,
{
deserializer.deserialize_any(UnresolvedValueVisitor)
}
}

impl<'de> Deserialize<'de> for UnresolvedValues {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: Deserializer<'de>,
{
deserializer
.deserialize_map(NamedMapVisitor::<UnresolvedValue>::new())
.map(Self::from_map)
}
}

impl Serialize for ResolvedType {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
Expand Down Expand Up @@ -109,7 +162,7 @@ impl<'de> Deserialize<'de> for Arguments {
D: Deserializer<'de>,
{
deserializer
.deserialize_map(WitnessMapVisitor)
.deserialize_map(NamedMapVisitor::<Value>::new())
.map(Self::from)
}
}
Expand Down
Loading
Loading