diff --git a/crates/zkvm-interface/Cargo.toml b/crates/zkvm-interface/Cargo.toml index ac35507..84798fd 100644 --- a/crates/zkvm-interface/Cargo.toml +++ b/crates/zkvm-interface/Cargo.toml @@ -12,6 +12,10 @@ bincode = "1.3" indexmap = { version = "2.9.0", features = ["serde"] } thiserror = "2" auto_impl = "1.0" +erased-serde = "0.4.6" + +[dev-dependencies] +serde_json = "1" [lints] workspace = true diff --git a/crates/zkvm-interface/src/input.rs b/crates/zkvm-interface/src/input.rs index 34aa59d..3ba386b 100644 --- a/crates/zkvm-interface/src/input.rs +++ b/crates/zkvm-interface/src/input.rs @@ -1,5 +1,5 @@ +use erased_serde::Serialize as ErasedSerialize; use serde::Serialize; - /// Represents a builder for input data to be passed to a ZKVM guest program. /// Values are serialized sequentially into an internal byte buffer. #[derive(Debug, Default)] @@ -8,6 +8,24 @@ pub struct Input { ranges: Vec<(usize, usize)>, } +pub struct InputErased { + buf: Vec>, +} + +impl InputErased { + /// Create an empty input buffer. + pub fn new() -> Self { + Self { + buf: Default::default(), + } + } + + pub fn write(&mut self, value: T) -> Result<(), bincode::Error> { + self.buf.push(Box::new(value)); + Ok(()) + } +} + impl Input { /// Create an empty input buffer. pub fn new() -> Self { @@ -127,3 +145,177 @@ mod tests { assert_eq!(input.iter().count(), slice1.len() + slice2.len()); } } + +#[cfg(test)] +mod input_erased_tests { + use super::*; + use serde::{Deserialize, Serialize}; + + #[derive(Debug, Serialize, Deserialize, PartialEq)] + struct Person { + name: String, + age: u32, + } + + #[derive(Debug, Serialize, Deserialize, PartialEq)] + struct Product { + id: u64, + name: String, + price: f64, + } + + #[test] + fn test_new_creates_empty_buffer() { + let input = InputErased::new(); + assert_eq!(input.buf.len(), 0); + } + + #[test] + fn test_write_primitive_types() { + let mut input = InputErased::new(); + + assert!(input.write(42i32).is_ok()); + assert!(input.write(3.14f64).is_ok()); + assert!(input.write(true).is_ok()); + assert!(input.write("hello".to_string()).is_ok()); + + assert_eq!(input.buf.len(), 4); + } + + #[test] + fn test_write_custom_structs() { + let mut input = InputErased::new(); + + let person = Person { + name: "Alice".to_string(), + age: 30, + }; + + let product = Product { + id: 123, + name: "Widget".to_string(), + price: 9.99, + }; + + assert!(input.write(person).is_ok()); + assert!(input.write(product).is_ok()); + + assert_eq!(input.buf.len(), 2); + } + + #[test] + fn test_write_collections() { + let mut input = InputErased::new(); + + let vec_data = vec![1, 2, 3, 4, 5]; + let array_data = [10, 20, 30]; + + assert!(input.write(vec_data).is_ok()); + assert!(input.write(array_data).is_ok()); + + assert_eq!(input.buf.len(), 2); + } + + #[test] + fn test_write_mixed_types() { + let mut input = InputErased::new(); + + // Write different types to the same buffer + assert!(input.write(42).is_ok()); + assert!(input.write("test".to_string()).is_ok()); + assert!(input.write(vec![1, 2, 3]).is_ok()); + assert!( + input + .write(Person { + name: "Bob".to_string(), + age: 25, + }) + .is_ok() + ); + + assert_eq!(input.buf.len(), 4); + } + + #[test] + fn test_serialization_with_erased_serde() { + let mut input = InputErased::new(); + + input.write(42i32).unwrap(); + input.write("hello".to_string()).unwrap(); + + // Test that we can serialize the stored items to a buffer + for item in &input.buf { + let mut buf = Vec::new(); + let mut serializer = serde_json::Serializer::new(&mut buf); + let json_result = erased_serde::serialize(item.as_ref(), &mut serializer); + // Just testing that serialization works without error + assert!(json_result.is_ok()); + } + } + + #[test] + fn test_write_returns_ok() { + let mut input = InputErased::new(); + + // All these should return Ok(()) + let results = vec![ + input.write(1), + input.write("test".to_string()), + input.write(vec![1, 2, 3]), + ]; + + for result in results { + assert!(result.is_ok()); + assert_eq!(result.unwrap(), ()); + } + } + + #[test] + fn test_multiple_writes_increase_buffer_size() { + let mut input = InputErased::new(); + + assert_eq!(input.buf.len(), 0); + + input.write(1).unwrap(); + assert_eq!(input.buf.len(), 1); + + input.write(2).unwrap(); + assert_eq!(input.buf.len(), 2); + + input.write(3).unwrap(); + assert_eq!(input.buf.len(), 3); + } + + // Helper function to demonstrate actual serialization to bytes + // (since the current implementation doesn't expose this) + fn serialize_buffer_to_json( + input: &InputErased, + ) -> Result, Box> { + let mut results = Vec::new(); + + for item in &input.buf { + let mut buf = Vec::new(); + let mut serializer = serde_json::Serializer::new(&mut buf); + erased_serde::serialize(item.as_ref(), &mut serializer)?; + results.push(String::from_utf8(buf)?); + } + + Ok(results) + } + + #[test] + fn test_actual_serialization_output() { + let mut input = InputErased::new(); + + input.write(42).unwrap(); + input.write("hello".to_string()).unwrap(); + input.write(vec![1, 2, 3]).unwrap(); + + let serialized = serialize_buffer_to_json(&input).unwrap(); + + assert_eq!(serialized.len(), 3); + assert_eq!(serialized[0], "42"); + assert_eq!(serialized[1], "\"hello\""); + assert_eq!(serialized[2], "[1,2,3]"); + } +}