Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
88 changes: 88 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 4 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ members = [
"mvpoly",
"o1vm",
"plonk-neon",
"plonk-napi",
"plonk-wasm",
"poly-commitment",
"poseidon",
Expand Down Expand Up @@ -61,6 +62,9 @@ libflate = "2"
log = "0.4.20"
num-bigint = { version = "0.4.4", features = ["rand", "serde"] }
num-integer = "0.1.45"
napi = { version = "2.16.8", default-features = false, features = ["napi7"] }
napi-derive = "2.16.8"
napi-build = "2.1.0"
ocaml = { version = "0.22.2" }
ocaml-gen = { version = "1.0.0" }
once_cell = "=1.21.3"
Expand Down
46 changes: 46 additions & 0 deletions plonk-napi/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
[package]
name = "plonk-napi"
version = "0.1.0"
authors = ["opensource@o1labs.org"]
description = "Node-API bindings for plonk proof systems"
repository = "https://github.com/MinaProtocol/mina"
license = "MIT/Apache-2.0"
edition = "2021"

[lib]
name = "plonk_napi"
crate-type = ["cdylib"] # to generate a dynamic library that is loadable by Node

[dependencies]
napi = { workspace = true, features = ["napi7"] }
napi-derive.workspace = true

# arkworks
ark-ec.workspace = true
ark-ff.workspace = true
ark-poly.workspace = true
ark-serialize.workspace = true
arkworks.workspace = true

# proof-systems
kimchi.workspace = true
mina-curves = { path = "../curves" }
mina-poseidon = { path = "../poseidon" }
o1-utils = { path = "../utils" }
poly-commitment = { path = "../poly-commitment" }

getrandom.workspace = true
libc.workspace = true
num-bigint.workspace = true
once_cell.workspace = true
paste.workspace = true
rand.workspace = true
rayon.workspace = true
rmp-serde.workspace = true
serde.workspace = true
serde_json.workspace = true
serde_with.workspace = true
wasm-types.workspace = true

[build-dependencies]
napi-build.workspace = true
3 changes: 3 additions & 0 deletions plonk-napi/build.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
fn main() {
napi_build::setup();
}
36 changes: 36 additions & 0 deletions plonk-napi/src/circuit.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
use ark_ff::PrimeField;
use kimchi::circuits::{constraints::ConstraintSystem, gate::CircuitGate};
use mina_curves::pasta::Fp;
use napi::bindgen_prelude::*;
use napi_derive::napi;
use serde::Serialize;

use crate::types::WasmPastaFpPlonkIndex;

#[derive(Serialize)]
struct Circuit<F>
where
F: PrimeField,
{
public_input_size: usize,
#[serde(bound = "CircuitGate<F>: Serialize")]
gates: Vec<CircuitGate<F>>,
}

impl<F> From<&ConstraintSystem<F>> for Circuit<F>
where
F: PrimeField,
{
fn from(cs: &ConstraintSystem<F>) -> Self {
Self {
public_input_size: cs.public,
gates: cs.gates.to_vec(),
}
}
}

#[napi]
pub fn prover_to_json(prover_index: External<WasmPastaFpPlonkIndex>) -> String {
let circuit: Circuit<Fp> = prover_index.0.cs.as_ref().into();
serde_json::to_string(&circuit).expect("couldn't serialize constraints")
}
8 changes: 8 additions & 0 deletions plonk-napi/src/lib.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
mod circuit;
mod poseidon;
mod types;

pub use poseidon::{caml_pasta_fp_poseidon_block_cipher, caml_pasta_fq_poseidon_block_cipher};

pub use circuit::prover_to_json;
pub use types::{prover_index_from_bytes, prover_index_to_bytes, WasmPastaFpPlonkIndex};
56 changes: 56 additions & 0 deletions plonk-napi/src/poseidon.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
use arkworks::{WasmPastaFp, WasmPastaFq};
use mina_curves::pasta::{Fp, Fq};
use mina_poseidon::{constants::PlonkSpongeConstantsKimchi, permutation::poseidon_block_cipher};
use napi::bindgen_prelude::*;
use napi_derive::napi;
use wasm_types::{FlatVector, FlatVectorElem};

// fp

#[napi]
pub fn caml_pasta_fp_poseidon_block_cipher(state: Uint8Array) -> Result<Uint8Array> {
println!("from native rust");

let mut state_vec: Vec<Fp> = FlatVector::<WasmPastaFp>::from_bytes(state.to_vec())
.into_iter()
.map(Into::into)
.collect();

poseidon_block_cipher::<Fp, PlonkSpongeConstantsKimchi>(
mina_poseidon::pasta::fp_kimchi::static_params(),
&mut state_vec,
);

let res: Vec<u8> = state_vec
.into_iter()
.map(WasmPastaFp)
.flat_map(FlatVectorElem::flatten)
.collect();

Ok(Uint8Array::from(res))
}

// fq

#[napi]
pub fn caml_pasta_fq_poseidon_block_cipher(state: Uint8Array) -> Result<Uint8Array> {
println!("from native rust");

let mut state_vec: Vec<Fq> = FlatVector::<WasmPastaFq>::from_bytes(state.to_vec())
.into_iter()
.map(Into::into)
.collect();

poseidon_block_cipher::<Fq, PlonkSpongeConstantsKimchi>(
mina_poseidon::pasta::fq_kimchi::static_params(),
&mut state_vec,
);

let res: Vec<u8> = state_vec
.into_iter()
.map(WasmPastaFq)
.flat_map(FlatVectorElem::flatten)
.collect();

Ok(Uint8Array::from(res))
}
79 changes: 79 additions & 0 deletions plonk-napi/src/types.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
use kimchi::{linearization::expr_linearization, prover_index::ProverIndex};
use mina_curves::pasta::{Vesta as GAffine, VestaParameters};
use mina_poseidon::{constants::PlonkSpongeConstantsKimchi, sponge::DefaultFqSponge};
use napi::bindgen_prelude::{Error, External, Result as NapiResult, Status, Uint8Array};
use napi_derive::napi;
use poly_commitment::ipa::{OpeningProof, SRS};
use serde::{Deserialize, Serialize};
use std::{io::Cursor, sync::Arc};

pub struct WasmPastaFpPlonkIndex(pub Box<ProverIndex<GAffine, OpeningProof<GAffine>>>);

// TOOD: remove incl all dependencies when no longer needed and we only pass napi objects around
#[derive(Serialize, Deserialize)]
struct SerializedProverIndex {
prover_index: Vec<u8>,
srs: Vec<u8>,
}

// TOOD: remove incl all dependencies when no longer needed and we only pass napi objects around
impl WasmPastaFpPlonkIndex {
fn serialize_inner(&self) -> Result<Vec<u8>, String> {
let prover_index = rmp_serde::to_vec(self.0.as_ref()).map_err(|e| e.to_string())?;

let mut srs = Vec::new();
self.0
.srs
.serialize(&mut rmp_serde::Serializer::new(&mut srs))
.map_err(|e| e.to_string())?;

let serialized = SerializedProverIndex { prover_index, srs };

rmp_serde::to_vec(&serialized).map_err(|e| e.to_string())
}

fn deserialize_inner(bytes: &[u8]) -> Result<Self, String> {
let serialized: SerializedProverIndex =
rmp_serde::from_slice(bytes).map_err(|e| e.to_string())?;

let mut index: ProverIndex<GAffine, OpeningProof<GAffine>> = ProverIndex::deserialize(
&mut rmp_serde::Deserializer::new(Cursor::new(serialized.prover_index)),
)
.map_err(|e| e.to_string())?;

let srs = SRS::<GAffine>::deserialize(&mut rmp_serde::Deserializer::new(Cursor::new(
serialized.srs,
)))
.map_err(|e| e.to_string())?;

index.srs = Arc::new(srs);

let (linearization, powers_of_alpha) =
expr_linearization(Some(&index.cs.feature_flags), true);
index.linearization = linearization;
index.powers_of_alpha = powers_of_alpha;

index.compute_verifier_index_digest::<
DefaultFqSponge<VestaParameters, PlonkSpongeConstantsKimchi>,
>();

Ok(WasmPastaFpPlonkIndex(Box::new(index)))
}
}

// TOOD: remove incl all dependencies when no longer needed and we only pass napi objects around
#[napi]
pub fn prover_index_from_bytes(bytes: Uint8Array) -> NapiResult<External<WasmPastaFpPlonkIndex>> {
let index = WasmPastaFpPlonkIndex::deserialize_inner(bytes.as_ref())
.map_err(|e| Error::new(Status::InvalidArg, e))?;
Ok(External::new(index))
}

// TOOD: remove incl all dependencies when no longer needed and we only pass napi objects around
#[napi]
pub fn prover_index_to_bytes(index: External<WasmPastaFpPlonkIndex>) -> NapiResult<Uint8Array> {
let bytes = index
.serialize_inner()
.map_err(|e| Error::new(Status::GenericFailure, e))?;
Ok(Uint8Array::from(bytes))
}
Loading
Loading