diff --git a/Cargo.lock b/Cargo.lock index 65c4a0562e..227438362e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3318,9 +3318,12 @@ dependencies = [ "ctor", "hex", "kimchi", + "kimchi-stubs", "mina-curves", "mina-poseidon", "o1-utils", + "ocaml", + "ocaml-gen", "once_cell", "poly-commitment", "proptest", @@ -3335,6 +3338,20 @@ dependencies = [ "tracing-subscriber", ] +[[package]] +name = "saffron-stubs" +version = "0.1.0" +dependencies = [ + "kimchi", + "kimchi-stubs", + "libc", + "ocaml", + "ocaml-gen", + "poly-commitment", + "rand", + "saffron", +] + [[package]] name = "same-file" version = "1.0.6" diff --git a/Cargo.toml b/Cargo.toml index 87a9dd71ae..c454d78e84 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -20,6 +20,7 @@ members = [ "poseidon", "poseidon/export_test_vectors", "saffron", + "saffron-stubs", "signer", "tools/kimchi-visu", "turshi", @@ -109,7 +110,7 @@ o1vm = { path = "./o1vm", version = "0.1.0" } optimism = { path = "./optimism", version = "0.1.0" } plonk_wasm = { path = "./plonk-wasm", version = "0.1.0" } poly-commitment = { path = "./poly-commitment", version = "0.1.0" } -saffron = { path = "./poly-commitment", version = "0.1.0" } +saffron = { path = "./saffron", version = "0.1.0" } signer = { path = "./signer", version = "0.1.0" } turshi = { path = "./turshi", version = "0.1.0" } utils = { path = "./utils", version = "0.1.0" } diff --git a/saffron-stubs/Cargo.toml b/saffron-stubs/Cargo.toml new file mode 100644 index 0000000000..ccf7886d74 --- /dev/null +++ b/saffron-stubs/Cargo.toml @@ -0,0 +1,25 @@ +[package] +name = "saffron-stubs" +version = "0.1.0" +authors = ["opensource@o1labs.org"] +description = "OCaml stubs for Saffron" +repository = "https://github.com/MinaProtocol/mina" +license = "MIT/Apache-2.0" +edition = "2021" + +[lib] +name = "saffron_stubs" +# Important: do not ask to build a dynamic library. +# On MacOS arm64, ocaml-rs.v0.2.2 causes build issues. +# On the Saffron side, a fake and empty dllsaffron_stubs.so file is used. +crate-type = ["lib", "staticlib"] + +[dependencies] +kimchi = { workspace = true, features = ["ocaml_types"] } +kimchi-stubs.workspace = true +libc.workspace = true +ocaml = { workspace = true, features = ["no-caml-startup"] } +ocaml-gen.workspace = true +poly-commitment.workspace = true +rand.workspace = true +saffron = { workspace = true, features = ["ocaml_types"] } diff --git a/saffron-stubs/README.md b/saffron-stubs/README.md new file mode 100644 index 0000000000..73e8c51793 --- /dev/null +++ b/saffron-stubs/README.md @@ -0,0 +1,29 @@ +# OCaml stubs for the Saffron codebase + +This library will be used to call the Rust code defined in the crate +[saffron](./saffron) in the Caml codebase of Saffron. + +The bindings uses ocaml-gen to facilite the bindings of structures like +`ReadProof` and ocaml-rs to generate the C and Caml boilerplate. + +The user can run `make release` (resp.`make build` to add debug symbols) from +the top-level of the workspace. A static library `libsaffron_stubs.a` will be +generated with all the symbols the Caml codebase of Saffron can used. The static +file will be available under `target/release/libsaffron_stubs.a` (resp +`target/debug/libsaffron_stubs.a`). The exposed symbols can be inspected using +`nm`. The `nm` tool can be useful to debug errors like `undefined references +[...]`. + +Using the build system `dune`, the user can ask to generate the static library using: +``` +(rule + (target libsaffron_stubs.a) + (action + (progn + (run + cargo + build + -p + saffron-stubs + --release)))) +``` diff --git a/saffron-stubs/src/lib.rs b/saffron-stubs/src/lib.rs new file mode 100644 index 0000000000..e2a4480e43 --- /dev/null +++ b/saffron-stubs/src/lib.rs @@ -0,0 +1,57 @@ +use kimchi::{circuits::domains::EvaluationDomains, groupmap::GroupMap}; +use kimchi_stubs::{ + arkworks::{CamlFp, CamlGVesta}, + srs::fp::CamlFpSrs, +}; +use poly_commitment::SRS; +use saffron::{ + read_proof::{self, caml::CamlReadProof, ReadProof}, + BaseField, Curve, ScalarField, +}; + +#[ocaml_gen::func] +#[ocaml::func] +pub fn caml_saffron_read_prove( + caml_srs: CamlFpSrs, + caml_data: Vec, + caml_query: Vec, + caml_answer: Vec, + caml_data_comm: CamlGVesta, +) -> CamlReadProof { + let srs = caml_srs.0; + let data: Vec = caml_data.into_iter().map(|x| x.into()).collect(); + let query: Vec = caml_query.into_iter().map(|x| x.into()).collect(); + let answer: Vec = caml_answer.into_iter().map(|x| x.into()).collect(); + let data_comm: Curve = caml_data_comm.into(); + + let srs_size = srs.max_poly_size(); + let domain = EvaluationDomains::::create(srs_size).unwrap(); + + let group_map = GroupMap::::setup(); + + let mut rng = rand::thread_rng(); + let read_proof = read_proof::prove( + &srs, domain, &group_map, &mut rng, &data, &query, &answer, &data_comm, + ); + read_proof.into() +} + +#[ocaml_gen::func] +#[ocaml::func] +pub fn caml_saffron_read_verify( + caml_srs: CamlFpSrs, + caml_data_comm: CamlGVesta, + caml_proof: CamlReadProof, +) -> bool { + let srs = caml_srs.0; + let data_comm: Curve = caml_data_comm.into(); + let proof: ReadProof = caml_proof.into(); + + let mut rng = rand::thread_rng(); + let srs_size = srs.max_poly_size(); + let domain = EvaluationDomains::::create(srs_size).unwrap(); + + let group_map = GroupMap::::setup(); + + read_proof::verify(&srs, domain, &group_map, &mut rng, &data_comm, &proof) +} diff --git a/saffron/Cargo.toml b/saffron/Cargo.toml index 8cf3d55215..099af2c0cd 100644 --- a/saffron/Cargo.toml +++ b/saffron/Cargo.toml @@ -9,8 +9,8 @@ readme = "README.md" edition = "2021" license = "Apache-2.0" -# [lib] -# path = "src/lib.rs" +[lib] +path = "src/lib.rs" [[bin]] name = "saffron" @@ -25,9 +25,12 @@ ark-serialize = { workspace = true, features = ["derive"] } clap = { workspace = true, features = ["derive"] } hex.workspace = true kimchi.workspace = true +kimchi-stubs = { workspace = true, optional = true } mina-curves.workspace = true mina-poseidon.workspace = true o1-utils.workspace = true +ocaml = { workspace = true, optional = true } +ocaml-gen = { workspace = true, optional = true } poly-commitment.workspace = true rand.workspace = true rayon.workspace = true @@ -65,3 +68,4 @@ harness = false [features] bench = [] +ocaml_types = ["kimchi-stubs", "ocaml", "ocaml-gen"] diff --git a/saffron/src/lib.rs b/saffron/src/lib.rs index a52d35a96a..960ab75237 100644 --- a/saffron/src/lib.rs +++ b/saffron/src/lib.rs @@ -24,5 +24,3 @@ pub type BaseField = Fq; pub type CurveFqSponge = DefaultFqSponge; pub type CurveFrSponge = DefaultFrSponge; - -//pub type ScalarSponge = DefaultFrSponge; diff --git a/saffron/src/read_proof.rs b/saffron/src/read_proof.rs index 845d782caa..acfd9cf7d1 100644 --- a/saffron/src/read_proof.rs +++ b/saffron/src/read_proof.rs @@ -370,3 +370,49 @@ mod tests { assert!(!res_2, "Soundness: Malformed proof #2 must NOT verify"); } } + +#[cfg(feature = "ocaml_types")] +pub mod caml { + use super::*; + use kimchi_stubs::arkworks::{group_affine::CamlGVesta, pasta_fp::CamlFp}; + use poly_commitment::ipa::caml::CamlOpeningProof; + + #[derive(ocaml::IntoValue, ocaml::FromValue, ocaml_gen::Struct)] + pub struct CamlReadProof { + pub query_comm: CamlGVesta, + pub answer_comm: CamlGVesta, + pub quotient_comm: CamlGVesta, + pub data_eval: CamlFp, + pub query_eval: CamlFp, + pub answer_eval: CamlFp, + pub opening_proof: CamlOpeningProof, + } + + impl From for CamlReadProof { + fn from(proof: ReadProof) -> Self { + Self { + query_comm: proof.query_comm.into(), + answer_comm: proof.answer_comm.into(), + quotient_comm: proof.quotient_comm.into(), + data_eval: proof.data_eval.into(), + query_eval: proof.query_eval.into(), + answer_eval: proof.answer_eval.into(), + opening_proof: proof.opening_proof.into(), + } + } + } + + impl From for ReadProof { + fn from(proof: CamlReadProof) -> Self { + Self { + query_comm: proof.query_comm.into(), + answer_comm: proof.answer_comm.into(), + quotient_comm: proof.quotient_comm.into(), + data_eval: proof.data_eval.into(), + query_eval: proof.query_eval.into(), + answer_eval: proof.answer_eval.into(), + opening_proof: proof.opening_proof.into(), + } + } + } +} diff --git a/saffron/src/storage_proof.rs b/saffron/src/storage_proof.rs index 0704648637..920d7dd271 100644 --- a/saffron/src/storage_proof.rs +++ b/saffron/src/storage_proof.rs @@ -295,3 +295,34 @@ mod tests { } } } + +#[cfg(feature = "ocaml_types")] +pub mod caml { + use super::*; + use kimchi_stubs::arkworks::{group_affine::CamlGVesta, pasta_fp::CamlFp}; + use poly_commitment::ipa::caml::CamlOpeningProof; + + #[derive(ocaml::IntoValue, ocaml::FromValue, ocaml_gen::Struct)] + pub struct CamlStorageProof { + pub combined_data_eval: CamlFp, + pub opening_proof: CamlOpeningProof, + } + + impl From for CamlStorageProof { + fn from(proof: StorageProof) -> Self { + Self { + combined_data_eval: proof.combined_data_eval.into(), + opening_proof: proof.opening_proof.into(), + } + } + } + + impl From for StorageProof { + fn from(proof: CamlStorageProof) -> Self { + Self { + combined_data_eval: proof.combined_data_eval.into(), + opening_proof: proof.opening_proof.into(), + } + } + } +}