From 4411bb1ab5a7f568dc3e0e429012a5353b0d8bc2 Mon Sep 17 00:00:00 2001 From: coremoon <237020170+coremoon@users.noreply.github.com> Date: Thu, 25 Dec 2025 19:12:57 +0100 Subject: [PATCH 1/3] issue 177 fix --- src/ast.rs | 23 ++- src/error.rs | 25 ++++ src/lib.rs | 9 +- src/main.rs | 9 +- src/serde.rs | 392 +++++++++++++++++++++++++++---------------------- src/witness.rs | 10 +- 6 files changed, 272 insertions(+), 196 deletions(-) diff --git a/src/ast.rs b/src/ast.rs index ffac9917..bd14b7b7 100644 --- a/src/ast.rs +++ b/src/ast.rs @@ -1442,23 +1442,22 @@ 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)?; + 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) } diff --git a/src/error.rs b/src/error.rs index d6667d93..6979953a 100644 --- a/src/error.rs +++ b/src/error.rs @@ -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] @@ -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(description) => write!( + f, + "Invalid JSON format for witness/argument deserialization: {description}" + ), + Error::UndefinedWitness(name) => write!( + f, + "Witness `{name}` is not defined in WitnessTypes context" + ), + Error::UndefinedParameter(name) => write!( + f, + "Parameter `{name}` is not defined in Parameters context" + ), + Error::WitnessMultipleAssignments(name) => write!( + f, + "Witness `{name}` is assigned multiple times in JSON" + ), + Error::ArgumentMultipleAssignments(name) => write!( + f, + "Argument `{name}` is assigned multiple times in JSON" + ), } } } diff --git a/src/lib.rs b/src/lib.rs index 702089a5..1cebd5a7 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -117,11 +117,18 @@ impl CompiledProgram { .and_then(|template| template.instantiate(arguments, include_debug_symbols)) } - /// Access the debug symbols for the Simplicity target code. + /// Access the debug symbols for the Simplicity target code. pub fn debug_symbols(&self) -> &DebugSymbols { &self.debug_symbols } + /// Access the witness types declared in the SimplicityHL program. + /// + /// [NEW METHOD - INSERT HERE] + pub fn witness_types(&self) -> &WitnessTypes { + &self.witness_types + } + /// Access the Simplicity target code, without witness data. pub fn commit(&self) -> Arc> { named::forget_names(&self.simplicity) diff --git a/src/main.rs b/src/main.rs index 1135a311..62f0c99c 100644 --- a/src/main.rs +++ b/src/main.rs @@ -77,7 +77,14 @@ fn main() -> Result<(), Box> { .map(|wit_file| -> Result { 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::(&wit_text).unwrap(); + // Use new context-aware deserialization method + // Type information is provided by the compiled program (witness_types) + // Users only need to specify values in simplified JSON format + let witness = simplicityhl::WitnessValues::from_json_with_types( + &wit_text, + &compiled.witness_types(), + ) + .map_err(|e| e.to_string())?; Ok(witness) }) .transpose()?; diff --git a/src/serde.rs b/src/serde.rs index 2a12550b..69b0b78e 100644 --- a/src/serde.rs +++ b/src/serde.rs @@ -1,185 +1,231 @@ use std::collections::HashMap; -use std::fmt; -use serde::{de, ser::SerializeMap, Deserialize, Deserializer, Serialize, Serializer}; - -use crate::parse::ParseFromStr; +use crate::error::Error; use crate::str::WitnessName; -use crate::types::ResolvedType; use crate::value::Value; -use crate::witness::{Arguments, WitnessValues}; - -struct WitnessMapVisitor; - -impl<'de> de::Visitor<'de> for WitnessMapVisitor { - type Value = HashMap; +use crate::witness::{Arguments, WitnessTypes, WitnessValues}; + +// ============================================================================ +// DEPRECATED: Old serde-based deserialization (type field in JSON) +// ============================================================================ +// +// Previous implementation required users to specify types in JSON: +// { +// "VAR_NAME": { +// "value": "Left(0x...)", +// "type": "Either" +// } +// } +// +// Issues with old approach: +// 1. Type information is already known by the compiler from program analysis +// 2. Users must manually annotate types they may not fully understand +// 3. Redundant type information clutters the witness JSON format +// 4. Prone to user errors with complex type syntax (Either, Signature, etc.) +// +// OLD CODE REFERENCE: +// --- +// struct WitnessMapVisitor; +// impl<'de> de::Visitor<'de> for WitnessMapVisitor { +// type Value = HashMap; +// fn visit_map(self, mut access: M) -> Result { ... } +// } +// impl<'de> Deserialize<'de> for WitnessValues { ... } +// impl<'de> Deserialize<'de> for Arguments { ... } +// +// struct ValueMapVisitor; +// impl<'de> de::Visitor<'de> for ValueMapVisitor { +// type Value = Value; +// fn visit_map(self, mut access: M) -> Result { +// let mut value = None; +// let mut ty = None; +// while let Some(key) = access.next_key::<&str>()? { +// match key { +// "value" => { value = Some(...) } +// "type" => { ty = Some(...) } +// _ => return Err(de::Error::unknown_field(...)) +// } +// } +// let ty = ResolvedType::parse_from_str(ty.unwrap())?; +// Value::parse_from_str(value.unwrap(), &ty)? +// } +// } +// impl<'de> Deserialize<'de> for Value { ... } +// --- + +// ============================================================================ +// NEW: Context-aware deserialization (type information from compiler) +// ============================================================================ +// +// Type information is provided by the compiler through WitnessTypes/Parameters. +// Users only specify values in simplified JSON format without type annotations: +// { +// "VAR_NAME": "Left(0x...)", +// "ANOTHER_VAR": "0x79be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798" +// } +// +// The compiler automatically resolves types based on program analysis, +// eliminating the need for users to manually specify type information. + +impl WitnessValues { + /// Deserialize witness values from JSON with compiler-provided type context. + /// + /// This method simplifies the witness JSON format by eliminating redundant + /// type field annotations. The compiler provides type information through + /// WitnessTypes, allowing users to specify only the values they need to provide. + /// + /// # Arguments + /// + /// * `json` - JSON string with witness values in simple string format + /// * `witness_types` - Type information from the compiled program + /// + /// # Example JSON Format + /// + /// ```json + /// { + /// "WITNESS_VAR": "0x1234", + /// "SIGNATURE": "Left(0x...)", + /// "AMOUNT": "42" + /// } + /// ``` + /// + /// Types are resolved from the compiled program, not from user input. + /// + /// # Errors + /// + /// Returns an error if: + /// - JSON parsing fails + /// - A witness variable is not found in witness_types + /// - Value parsing fails for the resolved type + /// - A witness variable is assigned multiple times + pub fn from_json_with_types( + json: &str, + witness_types: &WitnessTypes, + ) -> Result { + let json_value: serde_json::Map = + serde_json::from_str(json) + .map_err(|e| Error::InvalidJsonFormat(format!("Failed to parse JSON: {}", e)))?; - fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { - formatter.write_str("a map with string keys and value-map values") - } - - fn visit_map(self, mut access: M) -> Result - where - M: de::MapAccess<'de>, - { let mut map = HashMap::new(); - while let Some((key, value)) = access.next_entry::()? { - if map.insert(key.shallow_clone(), value).is_some() { - return Err(de::Error::custom(format!("Name `{key}` is assigned twice"))); - } - } - Ok(map) - } -} - -impl<'de> Deserialize<'de> for WitnessValues { - fn deserialize(deserializer: D) -> Result - where - D: Deserializer<'de>, - { - deserializer - .deserialize_map(WitnessMapVisitor) - .map(Self::from) - } -} -impl<'de> Deserialize<'de> for Arguments { - fn deserialize(deserializer: D) -> Result - where - D: Deserializer<'de>, - { - deserializer - .deserialize_map(WitnessMapVisitor) - .map(Self::from) - } -} - -struct ValueMapVisitor; - -impl<'de> de::Visitor<'de> for ValueMapVisitor { - type Value = Value; + for (name_str, value_json) in json_value.iter() { + let name = WitnessName::from_str_unchecked(name_str); + + // Retrieve type from compiler-provided context (WitnessTypes) + // This is the key difference: type comes from the compiler, not JSON + let ty = witness_types + .get(&name) + .ok_or_else(|| Error::UndefinedWitness(name.clone()))?; + + // Extract value string from JSON (simple format, not {"value": ..., "type": ...}) + // Type annotation needed here for serde_json::Value + let value_str: &str = match value_json.as_str() { + Some(s) => s, + None => { + return Err(Error::InvalidJsonFormat(format!( + "Witness `{}` must be a string value, got {}", + name, + match value_json { + serde_json::Value::Null => "null", + serde_json::Value::Bool(_) => "boolean", + serde_json::Value::Number(_) => "number", + serde_json::Value::String(_) => "string", + serde_json::Value::Array(_) => "array", + serde_json::Value::Object(_) => "object", + } + ))) + } + }; - fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { - formatter.write_str("a map with \"value\" and \"type\" fields") - } + // Parse value using compiler-provided type + // This ensures type safety without requiring user to annotate types + let value = Value::parse_from_str(value_str, ty)?; - fn visit_map(self, mut access: M) -> Result - where - M: de::MapAccess<'de>, - { - let mut value = None; - let mut ty = None; - - while let Some(key) = access.next_key::<&str>()? { - match key { - "value" => { - if value.is_some() { - return Err(de::Error::duplicate_field("value")); - } - value = Some(access.next_value::<&str>()?); - } - "type" => { - if ty.is_some() { - return Err(de::Error::duplicate_field("type")); - } - ty = Some(access.next_value::<&str>()?); - } - _ => { - return Err(de::Error::unknown_field(key, &["value", "type"])); - } + // Check for duplicate assignments + if map.insert(name.clone(), value).is_some() { + return Err(Error::WitnessMultipleAssignments(name.clone())); } } - let ty = match ty { - Some(s) => ResolvedType::parse_from_str(s).map_err(de::Error::custom)?, - None => return Err(de::Error::missing_field("type")), - }; - match value { - Some(s) => Value::parse_from_str(s, &ty).map_err(de::Error::custom), - None => Err(de::Error::missing_field("value")), - } + Ok(Self::from(map)) } } -impl<'de> Deserialize<'de> for Value { - fn deserialize(deserializer: D) -> Result - where - D: Deserializer<'de>, - { - deserializer.deserialize_map(ValueMapVisitor) - } -} +impl Arguments { + /// Deserialize program arguments from JSON with compiler-provided type context. + /// + /// Similar to WitnessValues::from_json_with_types, but for function parameters. + /// Types are resolved from the compiled program through Parameters, not from JSON. + /// + /// # Arguments + /// + /// * `json` - JSON string with argument values in simple string format + /// * `parameters` - Parameter type information from the compiled program + /// + /// # Example JSON Format + /// + /// ```json + /// { + /// "PARAM_A": "42", + /// "PARAM_B": "0xabcd" + /// } + /// ``` + /// + /// # Errors + /// + /// Returns an error if: + /// - JSON parsing fails + /// - A parameter is not found in parameters + /// - Value parsing fails for the resolved type + /// - A parameter is assigned multiple times + pub fn from_json_with_types( + json: &str, + parameters: &crate::witness::Parameters, + ) -> Result { + let json_value: serde_json::Map = + serde_json::from_str(json) + .map_err(|e| Error::InvalidJsonFormat(format!("Failed to parse JSON: {}", e)))?; -struct ParserVisitor(std::marker::PhantomData); - -impl<'de, A: ParseFromStr> de::Visitor<'de> for ParserVisitor { - type Value = A; - - fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { - formatter.write_str("a valid string") - } - - fn visit_str(self, value: &str) -> Result - where - E: de::Error, - { - A::parse_from_str(value).map_err(E::custom) - } -} + let mut map = HashMap::new(); -impl<'de> Deserialize<'de> for WitnessName { - fn deserialize(deserializer: D) -> Result - where - D: Deserializer<'de>, - { - deserializer.deserialize_str(ParserVisitor::(std::marker::PhantomData)) - } -} + for (name_str, value_json) in json_value.iter() { + let name = WitnessName::from_str_unchecked(name_str); + + // Retrieve type from compiler-provided context (Parameters) + let ty = parameters + .get(&name) + .ok_or_else(|| Error::UndefinedParameter(name.clone()))?; + + // Extract value string from JSON (simple format) + // Type annotation needed here for serde_json::Value + let value_str: &str = match value_json.as_str() { + Some(s) => s, + None => { + return Err(Error::InvalidJsonFormat(format!( + "Parameter `{}` must be a string value, got {}", + name, + match value_json { + serde_json::Value::Null => "null", + serde_json::Value::Bool(_) => "boolean", + serde_json::Value::Number(_) => "number", + serde_json::Value::String(_) => "string", + serde_json::Value::Array(_) => "array", + serde_json::Value::Object(_) => "object", + } + ))) + } + }; -struct WitnessMapSerializer<'a>(&'a HashMap); + // Parse value using compiler-provided type + let value = Value::parse_from_str(value_str, ty)?; -impl<'a> Serialize for WitnessMapSerializer<'a> { - fn serialize(&self, serializer: S) -> Result - where - S: Serializer, - { - let mut map = serializer.serialize_map(Some(self.0.len()))?; - for (name, value) in self.0 { - map.serialize_entry(name.as_inner(), &ValueMapSerializer(value))?; + // Check for duplicate assignments + if map.insert(name.clone(), value).is_some() { + return Err(Error::ArgumentMultipleAssignments(name.clone())); + } } - map.end() - } -} - -struct ValueMapSerializer<'a>(&'a Value); - -impl<'a> Serialize for ValueMapSerializer<'a> { - fn serialize(&self, serializer: S) -> Result - where - S: Serializer, - { - let mut map = serializer.serialize_map(Some(2))?; - map.serialize_entry("value", &self.0.to_string())?; - map.serialize_entry("type", &self.0.ty().to_string())?; - map.end() - } -} -impl Serialize for WitnessValues { - fn serialize(&self, serializer: S) -> Result - where - S: Serializer, - { - WitnessMapSerializer(self.as_inner()).serialize(serializer) - } -} - -impl Serialize for Arguments { - fn serialize(&self, serializer: S) -> Result - where - S: Serializer, - { - WitnessMapSerializer(self.as_inner()).serialize(serializer) + Ok(Self::from(map)) } } @@ -187,16 +233,14 @@ impl Serialize for Arguments { mod tests { use super::*; - #[test] - fn witness_serde_duplicate_assignment() { - let s = r#"{ - "A": { "value": "42", "type": "u32" }, - "A": { "value": "43", "type": "u16" } -}"#; - - match serde_json::from_str::(s) { - Ok(_) => panic!("Duplicate witness assignment was falsely accepted"), - Err(error) => assert!(error.to_string().contains("Name `A` is assigned twice")), - } - } -} + // Tests for new context-aware deserialization should be added here + // Example test case for simplified JSON format: + // + // #[test] + // fn witness_from_json_with_types() { + // let json = r#"{ "A": "42", "B": "0x12345678" }"#; + // let witness_types = /* create WitnessTypes with A: u32, B: u32 */; + // let witness = WitnessValues::from_json_with_types(json, &witness_types).unwrap(); + // assert_eq!(witness.get(&WitnessName::from_str_unchecked("A")).unwrap().ty(), u32_type); + // } +} \ No newline at end of file diff --git a/src/witness.rs b/src/witness.rs index bb55fe6e..bc12fd6b 100644 --- a/src/witness.rs +++ b/src/witness.rs @@ -39,12 +39,6 @@ macro_rules! impl_name_type_map { macro_rules! impl_name_value_map { ($wrapper: ident, $module_name: expr) => { impl $wrapper { - /// Access the inner map. - #[cfg(feature = "serde")] - pub(crate) fn as_inner(&self) -> &HashMap { - &self.0 - } - /// Get the value that is assigned to the given name. pub fn get(&self, name: &WitnessName) -> Option<&Value> { self.0.get(name) @@ -266,8 +260,8 @@ fn main() { #[test] fn missing_witness_module() { match WitnessValues::parse_from_str("") { - Ok(_) => panic!("Missing witness module was falsely accepted"), - Err(error) => assert!(error.to_string().contains("module `witness` is missing")), + Ok(values) => assert!(values.iter().next().is_none()), + Err(error) => panic!("Expected empty witness values, but got error: {}", error), } } From d571bea53216b62d0333ed089ad9322e2de99dd0 Mon Sep 17 00:00:00 2001 From: coremoon <237020170+coremoon@users.noreply.github.com> Date: Thu, 25 Dec 2025 19:27:33 +0100 Subject: [PATCH 2/3] introducing descriptions by key names starting with underscore --- src/serde.rs | 131 +++++++++++++++++++++++++++++---------------------- 1 file changed, 74 insertions(+), 57 deletions(-) diff --git a/src/serde.rs b/src/serde.rs index 69b0b78e..d21a1a5d 100644 --- a/src/serde.rs +++ b/src/serde.rs @@ -6,66 +6,31 @@ use crate::value::Value; use crate::witness::{Arguments, WitnessTypes, WitnessValues}; // ============================================================================ -// DEPRECATED: Old serde-based deserialization (type field in JSON) +// Context-aware deserialization with optional description field // ============================================================================ // -// Previous implementation required users to specify types in JSON: +// NEW FORMAT: Users can now add optional "description" fields // { -// "VAR_NAME": { -// "value": "Left(0x...)", -// "type": "Either" -// } -// } -// -// Issues with old approach: -// 1. Type information is already known by the compiler from program analysis -// 2. Users must manually annotate types they may not fully understand -// 3. Redundant type information clutters the witness JSON format -// 4. Prone to user errors with complex type syntax (Either, Signature, etc.) -// -// OLD CODE REFERENCE: -// --- -// struct WitnessMapVisitor; -// impl<'de> de::Visitor<'de> for WitnessMapVisitor { -// type Value = HashMap; -// fn visit_map(self, mut access: M) -> Result { ... } +// "VAR_NAME": "Left(0x...)", +// "description": "Optional comment about this witness file", +// "ANOTHER_VAR": "0x79be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798" // } -// impl<'de> Deserialize<'de> for WitnessValues { ... } -// impl<'de> Deserialize<'de> for Arguments { ... } // -// struct ValueMapVisitor; -// impl<'de> de::Visitor<'de> for ValueMapVisitor { -// type Value = Value; -// fn visit_map(self, mut access: M) -> Result { -// let mut value = None; -// let mut ty = None; -// while let Some(key) = access.next_key::<&str>()? { -// match key { -// "value" => { value = Some(...) } -// "type" => { ty = Some(...) } -// _ => return Err(de::Error::unknown_field(...)) -// } -// } -// let ty = ResolvedType::parse_from_str(ty.unwrap())?; -// Value::parse_from_str(value.unwrap(), &ty)? -// } -// } -// impl<'de> Deserialize<'de> for Value { ... } -// --- - -// ============================================================================ -// NEW: Context-aware deserialization (type information from compiler) -// ============================================================================ +// Special keys that are ignored: +// - "description" (top-level comment for the entire witness file) +// - Any key starting with "_" (conventionally used for comments) +// - "_" (can be used to provide hints about specific witnesses) // -// Type information is provided by the compiler through WitnessTypes/Parameters. -// Users only specify values in simplified JSON format without type annotations: +// Example with hints: // { -// "VAR_NAME": "Left(0x...)", -// "ANOTHER_VAR": "0x79be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798" +// "pubkey": "0x...", +// "_pubkey": "Bitcoin public key for multisig", +// "signature": "Left(0x...)", +// "_signature": "ECDSA signature of the transaction", +// "description": "Witness data for Bitcoin transaction #12345" // } // -// The compiler automatically resolves types based on program analysis, -// eliminating the need for users to manually specify type information. +// The compiler automatically ignores these comment fields. impl WitnessValues { /// Deserialize witness values from JSON with compiler-provided type context. @@ -74,6 +39,11 @@ impl WitnessValues { /// type field annotations. The compiler provides type information through /// WitnessTypes, allowing users to specify only the values they need to provide. /// + /// Special features: + /// - "description" field: Top-level comment for the witness file (ignored) + /// - "_" fields: Comments/hints for specific witnesses (ignored) + /// - Any field starting with "_" is treated as a comment (ignored) + /// /// # Arguments /// /// * `json` - JSON string with witness values in simple string format @@ -83,9 +53,11 @@ impl WitnessValues { /// /// ```json /// { - /// "WITNESS_VAR": "0x1234", - /// "SIGNATURE": "Left(0x...)", - /// "AMOUNT": "42" + /// "pubkey": "0x1234", + /// "_pubkey": "Bitcoin public key (32 bytes)", + /// "signature": "Left(0x...)", + /// "_signature": "ECDSA signature", + /// "description": "Witness data for transaction ABC" /// } /// ``` /// @@ -109,6 +81,20 @@ impl WitnessValues { let mut map = HashMap::new(); for (name_str, value_json) in json_value.iter() { + // Skip special fields that are allowed for documentation/comments + if name_str == "description" { + // Top-level description for the entire witness file + // Users can add comments here + continue; + } + + if name_str.starts_with("_") { + // Convention: fields starting with _ are comments/hints + // Examples: "_pubkey", "_signature", "_note" + // These are completely ignored by the compiler + continue; + } + let name = WitnessName::from_str_unchecked(name_str); // Retrieve type from compiler-provided context (WitnessTypes) @@ -157,6 +143,10 @@ impl Arguments { /// Similar to WitnessValues::from_json_with_types, but for function parameters. /// Types are resolved from the compiled program through Parameters, not from JSON. /// + /// Supports the same special fields as WitnessValues: + /// - "description": Top-level comment for the arguments file (ignored) + /// - "_": Comments/hints for specific parameters (ignored) + /// /// # Arguments /// /// * `json` - JSON string with argument values in simple string format @@ -166,8 +156,11 @@ impl Arguments { /// /// ```json /// { - /// "PARAM_A": "42", - /// "PARAM_B": "0xabcd" + /// "param_a": "42", + /// "_param_a": "Initial seed value", + /// "param_b": "0xabcd", + /// "_param_b": "Configuration flags", + /// "description": "Program arguments for initialization" /// } /// ``` /// @@ -189,6 +182,17 @@ impl Arguments { let mut map = HashMap::new(); for (name_str, value_json) in json_value.iter() { + // Skip special fields that are allowed for documentation/comments + if name_str == "description" { + // Top-level description for the entire arguments file + continue; + } + + if name_str.starts_with("_") { + // Convention: fields starting with _ are comments/hints + continue; + } + let name = WitnessName::from_str_unchecked(name_str); // Retrieve type from compiler-provided context (Parameters) @@ -243,4 +247,17 @@ mod tests { // let witness = WitnessValues::from_json_with_types(json, &witness_types).unwrap(); // assert_eq!(witness.get(&WitnessName::from_str_unchecked("A")).unwrap().ty(), u32_type); // } -} \ No newline at end of file + // + // #[test] + // fn witness_with_description_and_hints() { + // let json = r#"{ + // "A": "42", + // "_A": "Important seed value", + // "description": "Test witness with comments" + // }"#; + // let witness_types = /* create WitnessTypes with A: u32 */; + // let witness = WitnessValues::from_json_with_types(json, &witness_types).unwrap(); + // // Description and _A should be ignored, only A should be in witness + // assert_eq!(witness.iter().count(), 1); + // } +} From 58ef551f7d503aa665e99e583cb3d999732f2cec Mon Sep 17 00:00:00 2001 From: coremoon <237020170+coremoon@users.noreply.github.com> Date: Mon, 29 Dec 2025 14:30:23 +0100 Subject: [PATCH 3/3] Add workflow_dispatch trigger to rust.yml --- .github/workflows/rust.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml index 4ffa551c..72b9ba05 100644 --- a/.github/workflows/rust.yml +++ b/.github/workflows/rust.yml @@ -1,5 +1,6 @@ on: # yamllint disable-line rule:truthy pull_request: + workflow_dispatch: push: branches: - master