diff --git a/codex-plonky2-circuits/.gitignore b/codex-plonky2-circuits/.gitignore old mode 100644 new mode 100755 diff --git a/codex-plonky2-circuits/Cargo.toml b/codex-plonky2-circuits/Cargo.toml old mode 100644 new mode 100755 diff --git a/codex-plonky2-circuits/README.md b/codex-plonky2-circuits/README.md old mode 100644 new mode 100755 diff --git a/codex-plonky2-circuits/src/bn254_wrapper/bn254_fr.rs b/codex-plonky2-circuits/src/bn254_wrapper/bn254_fr.rs old mode 100644 new mode 100755 diff --git a/codex-plonky2-circuits/src/bn254_wrapper/config.rs b/codex-plonky2-circuits/src/bn254_wrapper/config.rs old mode 100644 new mode 100755 diff --git a/codex-plonky2-circuits/src/bn254_wrapper/mod.rs b/codex-plonky2-circuits/src/bn254_wrapper/mod.rs old mode 100644 new mode 100755 diff --git a/codex-plonky2-circuits/src/bn254_wrapper/poseidon_bn254.rs b/codex-plonky2-circuits/src/bn254_wrapper/poseidon_bn254.rs old mode 100644 new mode 100755 diff --git a/codex-plonky2-circuits/src/bn254_wrapper/poseidon_bn254_constants.rs b/codex-plonky2-circuits/src/bn254_wrapper/poseidon_bn254_constants.rs old mode 100644 new mode 100755 diff --git a/codex-plonky2-circuits/src/bn254_wrapper/wrap.rs b/codex-plonky2-circuits/src/bn254_wrapper/wrap.rs old mode 100644 new mode 100755 index c985cf6..335257f --- a/codex-plonky2-circuits/src/bn254_wrapper/wrap.rs +++ b/codex-plonky2-circuits/src/bn254_wrapper/wrap.rs @@ -130,16 +130,16 @@ impl< if !path.as_ref().exists() { fs::create_dir_all(&path)?; } - let common_data_file = File::create(path.as_ref().join("common_circuit_data.json"))?; + let common_data_file = File::create(path.as_ref().join("verifier_data/common_circuit_data.json"))?; serde_json::to_writer(&common_data_file, &self.common_data)?; println!("Succesfully wrote common circuit data to common_circuit_data.json"); let verifier_data_file = - File::create(path.as_ref().join("verifier_only_circuit_data.json"))?; + File::create(path.as_ref().join("verifier_data/verifier_only_circuit_data.json"))?; serde_json::to_writer(&verifier_data_file, &self.verifier_data)?; println!("Succesfully wrote verifier data to verifier_only_circuit_data.json"); - let proof_file = File::create(path.as_ref().join("proof_with_public_inputs.json"))?; + let proof_file = File::create(path.as_ref().join("verifier_data/proof_with_public_inputs.json"))?; serde_json::to_writer(&proof_file, &self.proof)?; println!("Succesfully wrote proof to proof_with_public_inputs.json"); @@ -172,10 +172,6 @@ mod tests { let conf = CircuitConfig::standard_recursion_config(); let mut builder = CircuitBuilder::::new(conf); - // let a = builder.add_virtual_public_input(); - // let b = builder.add_virtual_public_input(); - // let c = builder.add(a,b); - // builder.register_public_input(c); for _ in 0..(4096+10) { builder.add_gate(NoopGate, vec![]); } @@ -185,8 +181,6 @@ mod tests { // Set up the dummy circuit and wrapper. let dummy_circuit = builder.build::(); let mut pw = PartialWitness::new(); - // pw.set_target(a, GoldilocksField::from_canonical_u64(1))?; - // pw.set_target(b, GoldilocksField::from_canonical_u64(2))?; pw.set_target(t, F::ZERO).expect("faulty assign"); println!( "dummy circuit degree: {}", diff --git a/codex-plonky2-circuits/src/bundle/mod.rs b/codex-plonky2-circuits/src/bundle/mod.rs old mode 100644 new mode 100755 diff --git a/codex-plonky2-circuits/src/circuit_helper/mod.rs b/codex-plonky2-circuits/src/circuit_helper/mod.rs old mode 100644 new mode 100755 diff --git a/codex-plonky2-circuits/src/circuits/keyed_compress.rs b/codex-plonky2-circuits/src/circuits/keyed_compress.rs old mode 100644 new mode 100755 diff --git a/codex-plonky2-circuits/src/circuits/merkle_circuit.rs b/codex-plonky2-circuits/src/circuits/merkle_circuit.rs old mode 100644 new mode 100755 diff --git a/codex-plonky2-circuits/src/circuits/mod.rs b/codex-plonky2-circuits/src/circuits/mod.rs old mode 100644 new mode 100755 diff --git a/codex-plonky2-circuits/src/circuits/params.rs b/codex-plonky2-circuits/src/circuits/params.rs old mode 100644 new mode 100755 diff --git a/codex-plonky2-circuits/src/circuits/sample_cells.rs b/codex-plonky2-circuits/src/circuits/sample_cells.rs old mode 100644 new mode 100755 diff --git a/codex-plonky2-circuits/src/circuits/serialization.rs b/codex-plonky2-circuits/src/circuits/serialization.rs old mode 100644 new mode 100755 diff --git a/codex-plonky2-circuits/src/circuits/sponge.rs b/codex-plonky2-circuits/src/circuits/sponge.rs old mode 100644 new mode 100755 diff --git a/codex-plonky2-circuits/src/circuits/utils.rs b/codex-plonky2-circuits/src/circuits/utils.rs old mode 100644 new mode 100755 diff --git a/codex-plonky2-circuits/src/error.rs b/codex-plonky2-circuits/src/error.rs old mode 100644 new mode 100755 diff --git a/codex-plonky2-circuits/src/lib.rs b/codex-plonky2-circuits/src/lib.rs old mode 100644 new mode 100755 index 4994186..bc4c227 --- a/codex-plonky2-circuits/src/lib.rs +++ b/codex-plonky2-circuits/src/lib.rs @@ -4,5 +4,6 @@ pub mod error; pub mod circuit_helper; mod bundle; pub mod bn254_wrapper; +pub mod serialization; pub type Result = core::result::Result; diff --git a/codex-plonky2-circuits/src/recursion/compress.rs b/codex-plonky2-circuits/src/recursion/compress.rs old mode 100644 new mode 100755 diff --git a/codex-plonky2-circuits/src/recursion/dummy_gen.rs b/codex-plonky2-circuits/src/recursion/dummy_gen.rs old mode 100644 new mode 100755 diff --git a/codex-plonky2-circuits/src/recursion/leaf.rs b/codex-plonky2-circuits/src/recursion/leaf.rs old mode 100644 new mode 100755 diff --git a/codex-plonky2-circuits/src/recursion/mod.rs b/codex-plonky2-circuits/src/recursion/mod.rs old mode 100644 new mode 100755 diff --git a/codex-plonky2-circuits/src/recursion/node.rs b/codex-plonky2-circuits/src/recursion/node.rs old mode 100644 new mode 100755 diff --git a/codex-plonky2-circuits/src/recursion/pi_verifier.rs b/codex-plonky2-circuits/src/recursion/pi_verifier.rs old mode 100644 new mode 100755 diff --git a/codex-plonky2-circuits/src/recursion/utils.rs b/codex-plonky2-circuits/src/recursion/utils.rs old mode 100644 new mode 100755 diff --git a/codex-plonky2-circuits/src/serialization.rs b/codex-plonky2-circuits/src/serialization.rs new file mode 100755 index 0000000..548a5b1 --- /dev/null +++ b/codex-plonky2-circuits/src/serialization.rs @@ -0,0 +1,374 @@ +use serde::Serialize; +use std::{fs, io}; +use std::path::Path; +use anyhow::Context; +use plonky2::hash::hash_types::RichField; +use plonky2::plonk::circuit_data::{CircuitData, ProverCircuitData, VerifierCircuitData}; +use plonky2::plonk::config::{AlgebraicHasher, GenericConfig}; +use plonky2_field::extension::Extendable; +use plonky2_poseidon2::poseidon2_hash::poseidon2::Poseidon2; +use plonky2::plonk::proof::ProofWithPublicInputs; +use serde::de::DeserializeOwned; +use plonky2_poseidon2::serialization::{DefaultGateSerializer, DefaultGeneratorSerializer}; + +/// File constants paths - Prover +pub const PROVER_CIRC_DATA_JSON: &str = "prover_data/prover_circuit_data.bin"; +pub const TARGETS_JSON: &str = "prover_data/targets.json"; + +/// File constants paths - Verifier +pub const VERIFIER_CIRC_DATA_JSON: &str = "verifier_data/verifier_circuit_data.bin"; +pub const PROOF_JSON: &str = "verifier_data/proof_with_public_inputs.json"; + +// --------------------- helper fn -------------------------- + +/// Writes the provided bytes to the specified file path using `std::fs::write`. +pub fn write_bytes_to_file>(data: &[u8], path: P) -> io::Result<()> { + fs::write(path, data) +} + +/// Reads the contents of the specified file and returns them as a vector of bytes using `std::fs::read`. +pub fn read_bytes_from_file>(path: P) -> io::Result> { + fs::read(path) +} + +/// Ensures that the parent directory of the given file path exists. +/// If it does not exist, the function creates the entire directory path. +pub fn ensure_parent_directory_exists>(path: P) -> anyhow::Result<()> { + if let Some(parent) = path.as_ref().parent() { + fs::create_dir_all(parent) + .with_context(|| format!("Failed to create directory {:?}", parent))?; + } + Ok(()) +} + +//--------------------- EXPORT ----------------------------- + +pub fn export_prover_circuit_data< + F: RichField + Extendable + Poseidon2 + Serialize, + C: GenericConfig + Default + Serialize + 'static, + const D: usize, + P: AsRef, +>( + prover_data: ProverCircuitData, + base_path: P, +) -> anyhow::Result<()> + where + >::Hasher: AlgebraicHasher, +{ + let gate_serializer = DefaultGateSerializer; + let generator_serializer = DefaultGeneratorSerializer::::default(); + + // Serialize prover_data → Vec + let bytes = prover_data + .to_bytes(&gate_serializer, &generator_serializer) + .map_err(|e| anyhow::anyhow!("Failed to serialize prover data: {:?}", e))?; + + // Build output path: `{base_path}/prover_data/prover_circ_data.bin` + let out_path = base_path.as_ref().join(PROVER_CIRC_DATA_JSON); + + // Ensure parent directory exists + ensure_parent_directory_exists(&out_path) + .with_context(|| format!("Could not create directory for {:?}", out_path))?; + + // Write file + write_bytes_to_file(&bytes, &out_path) + .with_context(|| format!("Failed to write prover data to {:?}", out_path))?; + + Ok(()) +} + +/// Export only the VerifierCircuitData to `{base_path}/verifier_data/verifier_circ_data.bin`. +pub fn export_verifier_circuit_data< + F: RichField + Extendable + Poseidon2 + Serialize, + C: GenericConfig + Serialize, + const D: usize, + P: AsRef, +>( + verifier_data: VerifierCircuitData, + base_path: P, +) -> anyhow::Result<()> +{ + let gate_serializer = DefaultGateSerializer; + + // Serialize verifier_data → Vec + let bytes = verifier_data + .to_bytes(&gate_serializer) + .map_err(|e| anyhow::anyhow!("Failed to serialize verifier data: {:?}", e))?; + + // Build output path: `{base_path}/verifier_data/verifier_circ_data.bin` + let out_path = base_path.as_ref().join(VERIFIER_CIRC_DATA_JSON); + + // Ensure parent directory exists + ensure_parent_directory_exists(&out_path) + .with_context(|| format!("Could not create directory for {:?}", out_path))?; + + // Write file + write_bytes_to_file(&bytes, &out_path) + .with_context(|| format!("Failed to write verifier data to {:?}", out_path))?; + + Ok(()) +} + +/// Export only the “targets” (any `T: Serialize`) to `{base_path}/prover_data/targets.json`. +pub fn export_circuit_targets< + T: Serialize, + P: AsRef, +>( + targets: &T, + base_path: P, +) -> anyhow::Result<()> +{ + // Serialize `targets` → Vec (JSON) + let bytes = serde_json::to_vec(targets) + .context("Failed to serialize circuit targets to JSON")?; + + // Build output path: `{base_path}/prover_data/targets.json` + let out_path = base_path.as_ref().join(TARGETS_JSON); + + // Ensure parent directory exists + ensure_parent_directory_exists(&out_path) + .with_context(|| format!("Could not create directory for {:?}", out_path))?; + + // Write file + write_bytes_to_file(&bytes, &out_path) + .with_context(|| format!("Failed to write circuit targets to {:?}", out_path))?; + + Ok(()) +} + +/// Convenience function that calls all three exports in one shot. +/// ‣ Exports prover data, verifier data, and targets under `base_path`. +pub fn export_circuit_data< + F: RichField + Extendable + Poseidon2 + Serialize, + C: GenericConfig + Default + Serialize + 'static, + const D: usize, + P: AsRef, +>( + circ_data: CircuitData, + targets: &impl Serialize, + base_path: P, +) -> anyhow::Result<()> + where + >::Hasher: AlgebraicHasher, +{ + // 1. Split into prover_data + verifier_data + let verifier_data: VerifierCircuitData = circ_data.verifier_data(); + let prover_data = circ_data.prover_data(); + + + // 2. Export each separately + export_prover_circuit_data(prover_data, &base_path) + .context("export_prover_circuit_data failed")?; + export_verifier_circuit_data(verifier_data, &base_path) + .context("export_verifier_circuit_data failed")?; + export_circuit_targets(targets, &base_path) + .context("export_circuit_targets failed")?; + + Ok(()) +} + +/// Serialize `proof_with_pis` into JSON and write it under a base directory +pub fn export_proof_with_pi>( + proof_with_pis: &ProofWithPublicInputs, + base_path: P, +) -> anyhow::Result<()> + where + F: RichField + Extendable + Poseidon2 + Serialize, + C: GenericConfig + Serialize, +{ + // Serialize to JSON bytes + let proof_serialized = serde_json::to_vec(&proof_with_pis) + .map_err(|e| anyhow::anyhow!("Failed to serialize proof with public input: {:?}", e))?; + + // the full file path + let proof_file_path = base_path.as_ref().join(PROOF_JSON); + + // ensure parent directory exists + ensure_parent_directory_exists(&proof_file_path)?; + + // write it out + write_bytes_to_file(&proof_serialized, &proof_file_path) + .with_context(|| format!("Failed to write proof to {:?}", proof_file_path))?; + Ok(()) +} + +//------------------------- IMPORT -------------------------- + +/// Import `ProverCircuitData` from disk under the given `base_path`. +pub fn import_prover_circuit_data>( + base_path: P, +) -> anyhow::Result> + where + F: RichField + Extendable + Poseidon2 + Serialize, + C: GenericConfig + Default + Serialize + 'static, + >::Hasher: AlgebraicHasher, +{ + let gate_serializer = DefaultGateSerializer; + let generator_serializer = DefaultGeneratorSerializer::::default(); + + // the full path` + let full_path = base_path.as_ref().join(PROVER_CIRC_DATA_JSON); + + // Read raw bytes + let bytes = read_bytes_from_file(&full_path) + .with_context(|| format!("Failed to read prover circuit data from {:?}", full_path))?; + + // Deserialize + let prover_data = ProverCircuitData::::from_bytes( + &bytes, + &gate_serializer, + &generator_serializer, + ) + .map_err(|e| anyhow::anyhow!("Failed to deserialize prover data from {:?}: {:?}", full_path, e))?; + + Ok(prover_data) +} + +/// Import `VerifierCircuitData` from disk under `base_path`. +pub fn import_verifier_circuit_data>( + base_path: P, +) -> anyhow::Result> + where + F: RichField + Extendable + Poseidon2 + Serialize, + C: GenericConfig + Serialize, +{ + let gate_serializer = DefaultGateSerializer; + + let full_path = base_path.as_ref().join(VERIFIER_CIRC_DATA_JSON); + let bytes = read_bytes_from_file(&full_path) + .with_context(|| format!("Failed to read verifier circuit data from {:?}", full_path))?; + + let verifier_data = VerifierCircuitData::::from_bytes(bytes, &gate_serializer) + .map_err(|e| anyhow::anyhow!("Failed to deserialize verifier data from {:?}: {:?}", full_path, e))?; + + Ok(verifier_data) +} + +/// Import a `ProofWithPublicInputs` from JSON under `base_path`. +pub fn import_proof_with_pi>( + base_path: P, +) -> anyhow::Result> + where + F: RichField + Extendable + Poseidon2, + C: GenericConfig, +{ + // Build full path + let full_path = base_path.as_ref().join(PROOF_JSON); + + // Read JSON string + let proof_json_str = fs::read_to_string(&full_path) + .with_context(|| format!("Failed to read proof from {:?}", full_path))?; + + // Deserialize + let proof = serde_json::from_str(&proof_json_str) + .with_context(|| format!("Failed to deserialize proof at {:?}", full_path))?; + + Ok(proof) +} + +/// Import the circuit targets from the JSON file. +/// This function is generic over the type `T` that represents the targets and +/// must implement `DeserializeOwned` so that it can be deserialized. +pub fn import_targets>(base_path: P) -> anyhow::Result + where + T: DeserializeOwned, +{ + let full_path = base_path.as_ref().join(TARGETS_JSON); + let targets_str = fs::read_to_string(&full_path) + .with_context(|| format!("Failed to read targets from {:?}", full_path))?; + let targets = serde_json::from_str(&targets_str) + .with_context(|| format!("Failed to deserialize targets from {:?}", full_path))?; + Ok(targets) +} + +#[cfg(test)] +mod tests { + use super::*; + use std::path::Path; + use plonky2::gates::noop::NoopGate; + use plonky2::iop::target::Target; + use plonky2::iop::witness::{PartialWitness, WitnessWrite}; + use plonky2::plonk::circuit_builder::CircuitBuilder; + use plonky2::plonk::circuit_data::{CircuitConfig, ProverCircuitData, VerifierCircuitData}; + use plonky2::plonk::config::PoseidonGoldilocksConfig; + use plonky2::plonk::proof::ProofWithPublicInputs; + use plonky2_field::goldilocks_field::GoldilocksField; + use plonky2_field::types::Field; + + const D: usize = 2; + + type F = GoldilocksField; + type C = PoseidonGoldilocksConfig; + + #[test] + fn test_export_and_import_circuit_data_roundtrip() -> anyhow::Result<()> { + use serde::Serialize; + + #[derive(Clone, Debug, PartialEq, Serialize, serde::Deserialize)] + struct DummyTargets { + a: Target, + } + + let conf = CircuitConfig::standard_recursion_config(); + let mut builder = CircuitBuilder::::new(conf); + for _ in 0..128 { + builder.add_gate(NoopGate, vec![]); + } + let t = builder.add_virtual_public_input(); + + let dummy_circuit = builder.build::(); + let mut pw = PartialWitness::new(); + pw.set_target(t, F::ZERO).expect("faulty assign"); + let dummy_inner_proof = dummy_circuit.prove(pw).unwrap(); + assert!(dummy_circuit.verify(dummy_inner_proof.clone()).is_ok()); + + let dummy_t = DummyTargets{a: t}; + + let base_output = Path::new("../output/sampling_circ"); + export_circuit_data::(dummy_circuit, &dummy_t, base_output)?; + + let imported_prover: ProverCircuitData = + import_prover_circuit_data(base_output)?; + let imported_verifier: VerifierCircuitData = + import_verifier_circuit_data(base_output)?; + let imported_target: DummyTargets = import_targets(base_output)?; + + let mut pw = PartialWitness::new(); + pw.set_target(imported_target.a, F::ZERO).expect("faulty assign"); + let proof_with_pis = imported_prover.prove(pw).unwrap(); + assert!( + imported_verifier.verify(proof_with_pis).is_ok(), + "imported verifier failed to verify" + ); + + Ok(()) + } + + #[test] + fn test_export_and_import_proof_with_pi() -> anyhow::Result<()> { + let conf = CircuitConfig::standard_recursion_config(); + let mut builder = CircuitBuilder::::new(conf); + for _ in 0..128 { + builder.add_gate(NoopGate, vec![]); + } + let t = builder.add_virtual_public_input(); + + let dummy_circuit = builder.build::(); + let mut pw = PartialWitness::new(); + pw.set_target(t, F::ZERO).expect("faulty assign"); + let dummy_inner_proof = dummy_circuit.prove(pw).unwrap(); + assert!(dummy_circuit.verify(dummy_inner_proof.clone()).is_ok()); + + let base_output = Path::new("../output/sampling_circ"); + export_proof_with_pi(&dummy_inner_proof, base_output)?; + + let imported_proof: ProofWithPublicInputs = + import_proof_with_pi(base_output)?; + assert!( + dummy_circuit.verify(imported_proof).is_ok(), + "Imported proof failed verification" + ); + + Ok(()) + } +} \ No newline at end of file