Skip to content

Commit 534fb8e

Browse files
authored
Merge pull request #3345 from o1-labs/florian/napi-prover-json
[DO NOT MERGE] native `circuit.rs` implementation
2 parents 9348803 + cf6b541 commit 534fb8e

File tree

6 files changed

+194
-5
lines changed

6 files changed

+194
-5
lines changed

Cargo.lock

Lines changed: 3 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

plonk-napi/Cargo.toml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,9 +23,11 @@ ark-serialize.workspace = true
2323
arkworks.workspace = true
2424

2525
# proof-systems
26+
kimchi.workspace = true
2627
mina-curves = { path = "../curves" }
2728
mina-poseidon = { path = "../poseidon" }
2829
o1-utils = { path = "../utils" }
30+
poly-commitment = { path = "../poly-commitment" }
2931

3032
getrandom.workspace = true
3133
libc.workspace = true
@@ -36,6 +38,7 @@ rand.workspace = true
3638
rayon.workspace = true
3739
rmp-serde.workspace = true
3840
serde.workspace = true
41+
serde_json.workspace = true
3942
serde_with.workspace = true
4043
wasm-types.workspace = true
4144

plonk-napi/src/circuit.rs

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
use ark_ff::PrimeField;
2+
use kimchi::circuits::{constraints::ConstraintSystem, gate::CircuitGate};
3+
use mina_curves::pasta::Fp;
4+
use napi::bindgen_prelude::*;
5+
use napi_derive::napi;
6+
use serde::Serialize;
7+
8+
use crate::types::WasmPastaFpPlonkIndex;
9+
10+
#[derive(Serialize)]
11+
struct Circuit<F>
12+
where
13+
F: PrimeField,
14+
{
15+
public_input_size: usize,
16+
#[serde(bound = "CircuitGate<F>: Serialize")]
17+
gates: Vec<CircuitGate<F>>,
18+
}
19+
20+
impl<F> From<&ConstraintSystem<F>> for Circuit<F>
21+
where
22+
F: PrimeField,
23+
{
24+
fn from(cs: &ConstraintSystem<F>) -> Self {
25+
Self {
26+
public_input_size: cs.public,
27+
gates: cs.gates.to_vec(),
28+
}
29+
}
30+
}
31+
32+
#[napi]
33+
pub fn prover_to_json(prover_index: External<WasmPastaFpPlonkIndex>) -> String {
34+
let circuit: Circuit<Fp> = prover_index.0.cs.as_ref().into();
35+
serde_json::to_string(&circuit).expect("couldn't serialize constraints")
36+
}

plonk-napi/src/lib.rs

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
1+
mod circuit;
12
mod poseidon;
3+
mod types;
24

3-
pub use poseidon::{
4-
caml_pasta_fp_poseidon_block_cipher,
5-
caml_pasta_fq_poseidon_block_cipher,
6-
};
5+
pub use poseidon::{caml_pasta_fp_poseidon_block_cipher, caml_pasta_fq_poseidon_block_cipher};
6+
7+
pub use circuit::prover_to_json;
8+
pub use types::{prover_index_from_bytes, prover_index_to_bytes, WasmPastaFpPlonkIndex};

plonk-napi/src/types.rs

Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
use kimchi::{linearization::expr_linearization, prover_index::ProverIndex};
2+
use mina_curves::pasta::{Vesta as GAffine, VestaParameters};
3+
use mina_poseidon::{constants::PlonkSpongeConstantsKimchi, sponge::DefaultFqSponge};
4+
use napi::bindgen_prelude::{Error, External, Result as NapiResult, Status, Uint8Array};
5+
use napi_derive::napi;
6+
use poly_commitment::ipa::{OpeningProof, SRS};
7+
use serde::{Deserialize, Serialize};
8+
use std::{io::Cursor, sync::Arc};
9+
10+
pub struct WasmPastaFpPlonkIndex(pub Box<ProverIndex<GAffine, OpeningProof<GAffine>>>);
11+
12+
// TOOD: remove incl all dependencies when no longer needed and we only pass napi objects around
13+
#[derive(Serialize, Deserialize)]
14+
struct SerializedProverIndex {
15+
prover_index: Vec<u8>,
16+
srs: Vec<u8>,
17+
}
18+
19+
// TOOD: remove incl all dependencies when no longer needed and we only pass napi objects around
20+
impl WasmPastaFpPlonkIndex {
21+
fn serialize_inner(&self) -> Result<Vec<u8>, String> {
22+
let prover_index = rmp_serde::to_vec(self.0.as_ref()).map_err(|e| e.to_string())?;
23+
24+
let mut srs = Vec::new();
25+
self.0
26+
.srs
27+
.serialize(&mut rmp_serde::Serializer::new(&mut srs))
28+
.map_err(|e| e.to_string())?;
29+
30+
let serialized = SerializedProverIndex { prover_index, srs };
31+
32+
rmp_serde::to_vec(&serialized).map_err(|e| e.to_string())
33+
}
34+
35+
fn deserialize_inner(bytes: &[u8]) -> Result<Self, String> {
36+
let serialized: SerializedProverIndex =
37+
rmp_serde::from_slice(bytes).map_err(|e| e.to_string())?;
38+
39+
let mut index: ProverIndex<GAffine, OpeningProof<GAffine>> = ProverIndex::deserialize(
40+
&mut rmp_serde::Deserializer::new(Cursor::new(serialized.prover_index)),
41+
)
42+
.map_err(|e| e.to_string())?;
43+
44+
let srs = SRS::<GAffine>::deserialize(&mut rmp_serde::Deserializer::new(Cursor::new(
45+
serialized.srs,
46+
)))
47+
.map_err(|e| e.to_string())?;
48+
49+
index.srs = Arc::new(srs);
50+
51+
let (linearization, powers_of_alpha) =
52+
expr_linearization(Some(&index.cs.feature_flags), true);
53+
index.linearization = linearization;
54+
index.powers_of_alpha = powers_of_alpha;
55+
56+
index.compute_verifier_index_digest::<
57+
DefaultFqSponge<VestaParameters, PlonkSpongeConstantsKimchi>,
58+
>();
59+
60+
Ok(WasmPastaFpPlonkIndex(Box::new(index)))
61+
}
62+
}
63+
64+
// TOOD: remove incl all dependencies when no longer needed and we only pass napi objects around
65+
#[napi]
66+
pub fn prover_index_from_bytes(bytes: Uint8Array) -> NapiResult<External<WasmPastaFpPlonkIndex>> {
67+
let index = WasmPastaFpPlonkIndex::deserialize_inner(bytes.as_ref())
68+
.map_err(|e| Error::new(Status::InvalidArg, e))?;
69+
Ok(External::new(index))
70+
}
71+
72+
// TOOD: remove incl all dependencies when no longer needed and we only pass napi objects around
73+
#[napi]
74+
pub fn prover_index_to_bytes(index: External<WasmPastaFpPlonkIndex>) -> NapiResult<Uint8Array> {
75+
let bytes = index
76+
.serialize_inner()
77+
.map_err(|e| Error::new(Status::GenericFailure, e))?;
78+
Ok(Uint8Array::from(bytes))
79+
}

plonk-wasm/src/pasta_fp_plonk_index.rs

Lines changed: 67 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,8 @@ use mina_poseidon::{constants::PlonkSpongeConstantsKimchi, sponge::DefaultFqSpon
2020
use serde::{Deserialize, Serialize};
2121
use std::{
2222
fs::{File, OpenOptions},
23-
io::{BufReader, BufWriter, Seek, SeekFrom::Start},
23+
io::{BufReader, BufWriter, Cursor, Seek, SeekFrom::Start},
24+
sync::Arc,
2425
};
2526
use wasm_bindgen::prelude::*;
2627
use wasm_types::FlatVector as WasmFlatVector;
@@ -35,6 +36,71 @@ pub struct WasmPastaFpPlonkIndex(
3536
#[wasm_bindgen(skip)] pub Box<ProverIndex<GAffine, OpeningProof<GAffine>>>,
3637
);
3738

39+
// TOOD: remove incl all dependencies when no longer needed and we only pass napi objects around
40+
#[derive(Serialize, Deserialize)]
41+
struct SerializedProverIndex {
42+
prover_index: Vec<u8>,
43+
srs: Vec<u8>,
44+
}
45+
46+
// TOOD: remove incl all dependencies when no longer needed and we only pass napi objects around
47+
#[wasm_bindgen]
48+
impl WasmPastaFpPlonkIndex {
49+
#[wasm_bindgen(js_name = "serialize")]
50+
pub fn serialize(&self) -> Result<Vec<u8>, JsError> {
51+
serialize_prover_index(self.0.as_ref())
52+
.map_err(|e| JsError::new(&format!("WasmPastaFpPlonkIndex::serialize: {e}")))
53+
}
54+
55+
#[wasm_bindgen(js_name = "deserialize")]
56+
pub fn deserialize(bytes: &[u8]) -> Result<WasmPastaFpPlonkIndex, JsError> {
57+
deserialize_prover_index(bytes)
58+
.map(WasmPastaFpPlonkIndex)
59+
.map_err(|e| JsError::new(&format!("WasmPastaFpPlonkIndex::deserialize: {e}")))
60+
}
61+
}
62+
63+
fn serialize_prover_index(
64+
index: &ProverIndex<GAffine, OpeningProof<GAffine>>,
65+
) -> Result<Vec<u8>, String> {
66+
let prover_index = rmp_serde::to_vec(index).map_err(|e| e.to_string())?;
67+
68+
let mut srs = Vec::new();
69+
index
70+
.srs
71+
.serialize(&mut rmp_serde::Serializer::new(&mut srs))
72+
.map_err(|e| e.to_string())?;
73+
74+
let serialized = SerializedProverIndex { prover_index, srs };
75+
76+
rmp_serde::to_vec(&serialized).map_err(|e| e.to_string())
77+
}
78+
79+
fn deserialize_prover_index(
80+
bytes: &[u8],
81+
) -> Result<Box<ProverIndex<GAffine, OpeningProof<GAffine>>>, String> {
82+
let serialized: SerializedProverIndex =
83+
rmp_serde::from_slice(bytes).map_err(|e| e.to_string())?;
84+
85+
let mut index: ProverIndex<GAffine, OpeningProof<GAffine>> = ProverIndex::deserialize(
86+
&mut rmp_serde::Deserializer::new(Cursor::new(serialized.prover_index)),
87+
)
88+
.map_err(|e| e.to_string())?;
89+
90+
let srs = poly_commitment::ipa::SRS::<GAffine>::deserialize(&mut rmp_serde::Deserializer::new(
91+
Cursor::new(serialized.srs),
92+
))
93+
.map_err(|e| e.to_string())?;
94+
95+
index.srs = Arc::new(srs);
96+
97+
let (linearization, powers_of_alpha) = expr_linearization(Some(&index.cs.feature_flags), true);
98+
index.linearization = linearization;
99+
index.powers_of_alpha = powers_of_alpha;
100+
101+
Ok(Box::new(index))
102+
}
103+
38104
// This should mimic LookupTable structure
39105
#[wasm_bindgen]
40106
pub struct WasmPastaFpLookupTable {

0 commit comments

Comments
 (0)