From 53fef4ffe076cb7f6f10c957fba066453d2fa04b Mon Sep 17 00:00:00 2001 From: querolita Date: Wed, 12 Nov 2025 15:09:21 +0100 Subject: [PATCH 01/23] conversions: attempt proof conversions --- src/bindings/crypto/napi-conversion-proof.ts | 176 ++++++++++++++++++- 1 file changed, 173 insertions(+), 3 deletions(-) diff --git a/src/bindings/crypto/napi-conversion-proof.ts b/src/bindings/crypto/napi-conversion-proof.ts index 830ad61f8b..b09f17fa20 100644 --- a/src/bindings/crypto/napi-conversion-proof.ts +++ b/src/bindings/crypto/napi-conversion-proof.ts @@ -7,29 +7,62 @@ import type { WasmPastaFqRuntimeTableCfg, WasmVecVecFp, WasmVecVecFq, + WasmFpProverCommitments, + WasmFqProverCommitments, + WasmFpOpeningProof, + WasmFqOpeningProof, + WasmFpProverProof, + WasmFqProverProof, + WasmFpLookupCommitments, + WasmFqLookupCommitments, } from '../compiled/node_bindings/plonk_wasm.cjs'; import type * as napiNamespace from '../compiled/node_bindings/plonk_wasm.cjs'; import type { RuntimeTable, RuntimeTableCfg, LookupTable, + ProofWithPublic, + ProverProof, + LookupCommitments, + PolyComm, PointEvaluations, ProofEvaluations, + RecursionChallenge, + ProverCommitments, + OpeningProof, + OrInfinity, } from './bindings/kimchi-types.js'; -import { MlArray} from '../../lib/ml/base.js'; +import { MlArray, MlTuple, MlOption} from '../../lib/ml/base.js'; import { fieldsToRustFlat, + fieldToRust, + fieldsFromRustFlat, + fieldFromRust, } from './bindings/conversion-base.js'; import { ConversionCore, ConversionCores } from './napi-conversion-core.js'; export { napiProofConversion }; + +type NapiProofEvaluations = [ + 0, + MlOption>, + ...RemoveLeadingZero>, +]; type napi = typeof napiNamespace; +type NapiProverCommitments = WasmFpProverCommitments | WasmFqProverCommitments; +type NapiOpeningProof = WasmFpOpeningProof | WasmFqOpeningProof; +type NapiProverProof = WasmFpProverProof | WasmFqProverProof; +type NapiLookupCommitments = WasmFpLookupCommitments | WasmFqLookupCommitments; type NapiRuntimeTable = WasmFpRuntimeTable | WasmFqRuntimeTable; type NapiRuntimeTableCfg = WasmPastaFpRuntimeTableCfg | WasmPastaFqRuntimeTableCfg; type NapiLookupTable = WasmPastaFpLookupTable | WasmPastaFqLookupTable; type NapiClasses = { + ProverCommitments: typeof WasmFpProverCommitments | typeof WasmFqProverCommitments; + OpeningProof: typeof WasmFpOpeningProof | typeof WasmFqOpeningProof; VecVec: typeof WasmVecVecFp | typeof WasmVecVecFq; + ProverProof: typeof WasmFpProverProof | typeof WasmFqProverProof; + LookupCommitments: typeof WasmFpLookupCommitments | typeof WasmFqLookupCommitments; RuntimeTable: typeof WasmFpRuntimeTable | typeof WasmFqRuntimeTable; RuntimeTableCfg: typeof WasmPastaFpRuntimeTableCfg | typeof WasmPastaFqRuntimeTableCfg; LookupTable: typeof WasmPastaFpLookupTable | typeof WasmPastaFqLookupTable; @@ -38,13 +71,21 @@ type NapiClasses = { function napiProofConversion(napi: napi, core: ConversionCores) { return { fp: proofConversionPerField(core.fp, { + ProverCommitments: napi.WasmFpProverCommitments, + OpeningProof: napi.WasmFpOpeningProof, VecVec: napi.WasmVecVecFp, + ProverProof: napi.WasmFpProverProof, + LookupCommitments: napi.WasmFpLookupCommitments, RuntimeTable: napi.WasmFpRuntimeTable, RuntimeTableCfg: napi.WasmPastaFpRuntimeTableCfg, LookupTable: napi.WasmPastaFpLookupTable, }), fq: proofConversionPerField(core.fq, { + ProverCommitments: napi.WasmFqProverCommitments, + OpeningProof: napi.WasmFqOpeningProof, VecVec: napi.WasmVecVecFq, + ProverProof: napi.WasmFqProverProof, + LookupCommitments: napi.WasmFqLookupCommitments, RuntimeTable: napi.WasmFqRuntimeTable, RuntimeTableCfg: napi.WasmPastaFqRuntimeTableCfg, LookupTable: napi.WasmPastaFqLookupTable, @@ -55,13 +96,78 @@ function napiProofConversion(napi: napi, core: ConversionCores) { function proofConversionPerField( core: ConversionCore, { + ProverCommitments, + OpeningProof, VecVec, + ProverProof, + LookupCommitments, RuntimeTable, RuntimeTableCfg, LookupTable, }: NapiClasses ) { - + function commitmentsToRust(commitments: ProverCommitments): NapiProverCommitments { + let wComm = core.polyCommsToRust(commitments[1]); + let zComm = core.polyCommToRust(commitments[2]); + let tComm = core.polyCommToRust(commitments[3]); + let lookup = MlOption.mapFrom(commitments[4], lookupCommitmentsToRust); + return new ProverCommitments(wComm, zComm, tComm, lookup); + } + function commitmentsFromRust(commitments: NapiProverCommitments): ProverCommitments { + let wComm = core.polyCommsFromRust(commitments.w_comm); + let zComm = core.polyCommFromRust(commitments.z_comm); + let tComm = core.polyCommFromRust(commitments.t_comm); + let lookup = MlOption.mapTo(commitments.lookup, lookupCommitmentsFromRust); + commitments.free(); + return [0, wComm as MlTuple, zComm, tComm, lookup]; + } + + function lookupCommitmentsToRust(lookup: LookupCommitments): NapiLookupCommitments { + let sorted = core.polyCommsToRust(lookup[1]); + let aggreg = core.polyCommToRust(lookup[2]); + let runtime = MlOption.mapFrom(lookup[3], core.polyCommToRust); + return new LookupCommitments(sorted, aggreg, runtime); + } + function lookupCommitmentsFromRust(lookup: NapiLookupCommitments): LookupCommitments { + let sorted = core.polyCommsFromRust(lookup.sorted); + let aggreg = core.polyCommFromRust(lookup.aggreg); + let runtime = MlOption.mapTo(lookup.runtime, core.polyCommFromRust); + lookup.free(); + return [0, sorted, aggreg, runtime]; + } + + function openingProofToRust(proof: OpeningProof): NapiOpeningProof { + let [_, [, ...lr], delta, z1, z2, sg] = proof; + // We pass l and r as separate vectors over the FFI + let l: MlArray = [0]; + let r: MlArray = [0]; + for (let [, li, ri] of lr) { + l.push(li); + r.push(ri); + } + return new OpeningProof( + core.pointsToRust(l), + core.pointsToRust(r), + core.pointToRust(delta), + fieldToRust(z1), + fieldToRust(z2), + core.pointToRust(sg) + ); + } + function openingProofFromRust(proof: NapiOpeningProof): OpeningProof { + let [, ...l] = core.pointsFromRust(proof.lr_0); + let [, ...r] = core.pointsFromRust(proof.lr_1); + let n = l.length; + if (n !== r.length) throw Error('openingProofFromRust: l and r length mismatch.'); + let lr = l.map<[0, OrInfinity, OrInfinity]>((li, i) => [0, li, r[i]]); + let delta = core.pointFromRust(proof.delta); + let z1 = fieldFromRust(proof.z1); + let z2 = fieldFromRust(proof.z2); + let sg = core.pointFromRust(proof.sg); + proof.free(); + return [0, [0, ...lr], delta, z1, z2, sg]; + } + function runtimeTableToRust([, id, data]: RuntimeTable): NapiRuntimeTable { return new RuntimeTable(id, core.vectorToRust(data)); } @@ -78,8 +184,68 @@ function proofConversionPerField( } return new LookupTable(id, wasmData); } + + return { + proofToRust([, public_evals, proof]: ProofWithPublic): NapiProverProof { + let commitments = commitmentsToRust(proof[1]); + let openingProof = openingProofToRust(proof[2]); + let [, ...evals] = proofEvaluationsToRust(proof[3]); + let publicEvals = pointEvalsOptionToRust(public_evals); + // TODO typed as `any` in wasm-bindgen, this has the correct type + let evalsActual: NapiProofEvaluations = [0, publicEvals, ...evals]; + + let ftEval1 = fieldToRust(proof[4]); + let public_ = fieldsToRustFlat(proof[5]); + let [, ...prevChallenges] = proof[6]; + let n = prevChallenges.length; + let prevChallengeScalars = new VecVec(n); + let prevChallengeCommsMl: MlArray = [0]; + for (let [, scalars, comms] of prevChallenges) { + prevChallengeScalars.push(fieldsToRustFlat(scalars)); + prevChallengeCommsMl.push(comms); + } + let prevChallengeComms = core.polyCommsToRust(prevChallengeCommsMl); + return new ProverProof( + commitments, + openingProof, + evalsActual, + ftEval1, + public_, + prevChallengeScalars, + prevChallengeComms + ); + }, + proofFromRust(wasmProof: NapiProverProof): ProofWithPublic { + let commitments = commitmentsFromRust(wasmProof.commitments); + let openingProof = openingProofFromRust(wasmProof.proof); + // TODO typed as `any` in wasm-bindgen, this is the correct type + let [, wasmPublicEvals, ...wasmEvals]: NapiProofEvaluations = wasmProof.evals; + let publicEvals = pointEvalsOptionFromRust(wasmPublicEvals); + let evals = proofEvaluationsFromRust([0, ...wasmEvals]); + + let ftEval1 = fieldFromRust(wasmProof.ft_eval1); + let public_ = fieldsFromRustFlat(wasmProof.public_); + let prevChallengeScalars = wasmProof.prev_challenges_scalars; + let [, ...prevChallengeComms] = core.polyCommsFromRust(wasmProof.prev_challenges_comms); + let prevChallenges = prevChallengeComms.map((comms, i) => { + let scalars = fieldsFromRustFlat(prevChallengeScalars.get(i)); + return [0, scalars, comms]; + }); + wasmProof.free(); + let proof: ProverProof = [ + 0, + commitments, + openingProof, + evals, + ftEval1, + public_, + [0, ...prevChallenges], + ]; + return [0, publicEvals, proof]; + }, + runtimeTablesToRust([, ...tables]: MlArray): NapiRuntimeTable[] { return tables.map(runtimeTableToRust); }, @@ -92,4 +258,8 @@ function proofConversionPerField( return tables.map(lookupTableToRust); }, }; -} \ No newline at end of file +} + +// helper + +type RemoveLeadingZero = T extends [0, ...infer U] ? U : never; From f4528083226a6d878da9856d537dc7a71f9b6b58 Mon Sep 17 00:00:00 2001 From: querolita Date: Thu, 13 Nov 2025 12:45:32 +0100 Subject: [PATCH 02/23] bindings: remove types conversions for proof --- src/bindings/crypto/napi-conversion-proof.ts | 194 +------------------ 1 file changed, 6 insertions(+), 188 deletions(-) diff --git a/src/bindings/crypto/napi-conversion-proof.ts b/src/bindings/crypto/napi-conversion-proof.ts index b09f17fa20..adf08d7a10 100644 --- a/src/bindings/crypto/napi-conversion-proof.ts +++ b/src/bindings/crypto/napi-conversion-proof.ts @@ -1,68 +1,29 @@ import type { - WasmPastaFpLookupTable, WasmFpRuntimeTable, WasmPastaFpRuntimeTableCfg, - WasmPastaFqLookupTable, + WasmPastaFpLookupTable, WasmFqRuntimeTable, WasmPastaFqRuntimeTableCfg, + WasmPastaFqLookupTable, WasmVecVecFp, WasmVecVecFq, - WasmFpProverCommitments, - WasmFqProverCommitments, - WasmFpOpeningProof, - WasmFqOpeningProof, - WasmFpProverProof, - WasmFqProverProof, - WasmFpLookupCommitments, - WasmFqLookupCommitments, } from '../compiled/node_bindings/plonk_wasm.cjs'; import type * as napiNamespace from '../compiled/node_bindings/plonk_wasm.cjs'; -import type { - RuntimeTable, - RuntimeTableCfg, - LookupTable, - ProofWithPublic, - ProverProof, - LookupCommitments, - PolyComm, PointEvaluations, ProofEvaluations, - RecursionChallenge, - ProverCommitments, - OpeningProof, - OrInfinity, -} from './bindings/kimchi-types.js'; -import { MlArray, MlTuple, MlOption} from '../../lib/ml/base.js'; -import { - fieldsToRustFlat, - fieldToRust, - fieldsFromRustFlat, - fieldFromRust, -} from './bindings/conversion-base.js'; +import type { RuntimeTable, RuntimeTableCfg, LookupTable } from './bindings/kimchi-types.js'; +import { MlArray } from '../../lib/ml/base.js'; +import { fieldsToRustFlat } from './bindings/conversion-base.js'; import { ConversionCore, ConversionCores } from './napi-conversion-core.js'; export { napiProofConversion }; - -type NapiProofEvaluations = [ - 0, - MlOption>, - ...RemoveLeadingZero>, -]; type napi = typeof napiNamespace; -type NapiProverCommitments = WasmFpProverCommitments | WasmFqProverCommitments; -type NapiOpeningProof = WasmFpOpeningProof | WasmFqOpeningProof; -type NapiProverProof = WasmFpProverProof | WasmFqProverProof; -type NapiLookupCommitments = WasmFpLookupCommitments | WasmFqLookupCommitments; type NapiRuntimeTable = WasmFpRuntimeTable | WasmFqRuntimeTable; type NapiRuntimeTableCfg = WasmPastaFpRuntimeTableCfg | WasmPastaFqRuntimeTableCfg; type NapiLookupTable = WasmPastaFpLookupTable | WasmPastaFqLookupTable; type NapiClasses = { - ProverCommitments: typeof WasmFpProverCommitments | typeof WasmFqProverCommitments; - OpeningProof: typeof WasmFpOpeningProof | typeof WasmFqOpeningProof; VecVec: typeof WasmVecVecFp | typeof WasmVecVecFq; - ProverProof: typeof WasmFpProverProof | typeof WasmFqProverProof; - LookupCommitments: typeof WasmFpLookupCommitments | typeof WasmFqLookupCommitments; RuntimeTable: typeof WasmFpRuntimeTable | typeof WasmFqRuntimeTable; RuntimeTableCfg: typeof WasmPastaFpRuntimeTableCfg | typeof WasmPastaFqRuntimeTableCfg; LookupTable: typeof WasmPastaFpLookupTable | typeof WasmPastaFqLookupTable; @@ -71,21 +32,13 @@ type NapiClasses = { function napiProofConversion(napi: napi, core: ConversionCores) { return { fp: proofConversionPerField(core.fp, { - ProverCommitments: napi.WasmFpProverCommitments, - OpeningProof: napi.WasmFpOpeningProof, VecVec: napi.WasmVecVecFp, - ProverProof: napi.WasmFpProverProof, - LookupCommitments: napi.WasmFpLookupCommitments, RuntimeTable: napi.WasmFpRuntimeTable, RuntimeTableCfg: napi.WasmPastaFpRuntimeTableCfg, LookupTable: napi.WasmPastaFpLookupTable, }), fq: proofConversionPerField(core.fq, { - ProverCommitments: napi.WasmFqProverCommitments, - OpeningProof: napi.WasmFqOpeningProof, VecVec: napi.WasmVecVecFq, - ProverProof: napi.WasmFqProverProof, - LookupCommitments: napi.WasmFqLookupCommitments, RuntimeTable: napi.WasmFqRuntimeTable, RuntimeTableCfg: napi.WasmPastaFqRuntimeTableCfg, LookupTable: napi.WasmPastaFqLookupTable, @@ -95,79 +48,8 @@ function napiProofConversion(napi: napi, core: ConversionCores) { function proofConversionPerField( core: ConversionCore, - { - ProverCommitments, - OpeningProof, - VecVec, - ProverProof, - LookupCommitments, - RuntimeTable, - RuntimeTableCfg, - LookupTable, - }: NapiClasses + { VecVec, RuntimeTable, RuntimeTableCfg, LookupTable }: NapiClasses ) { - function commitmentsToRust(commitments: ProverCommitments): NapiProverCommitments { - let wComm = core.polyCommsToRust(commitments[1]); - let zComm = core.polyCommToRust(commitments[2]); - let tComm = core.polyCommToRust(commitments[3]); - let lookup = MlOption.mapFrom(commitments[4], lookupCommitmentsToRust); - return new ProverCommitments(wComm, zComm, tComm, lookup); - } - function commitmentsFromRust(commitments: NapiProverCommitments): ProverCommitments { - let wComm = core.polyCommsFromRust(commitments.w_comm); - let zComm = core.polyCommFromRust(commitments.z_comm); - let tComm = core.polyCommFromRust(commitments.t_comm); - let lookup = MlOption.mapTo(commitments.lookup, lookupCommitmentsFromRust); - commitments.free(); - return [0, wComm as MlTuple, zComm, tComm, lookup]; - } - - function lookupCommitmentsToRust(lookup: LookupCommitments): NapiLookupCommitments { - let sorted = core.polyCommsToRust(lookup[1]); - let aggreg = core.polyCommToRust(lookup[2]); - let runtime = MlOption.mapFrom(lookup[3], core.polyCommToRust); - return new LookupCommitments(sorted, aggreg, runtime); - } - function lookupCommitmentsFromRust(lookup: NapiLookupCommitments): LookupCommitments { - let sorted = core.polyCommsFromRust(lookup.sorted); - let aggreg = core.polyCommFromRust(lookup.aggreg); - let runtime = MlOption.mapTo(lookup.runtime, core.polyCommFromRust); - lookup.free(); - return [0, sorted, aggreg, runtime]; - } - - function openingProofToRust(proof: OpeningProof): NapiOpeningProof { - let [_, [, ...lr], delta, z1, z2, sg] = proof; - // We pass l and r as separate vectors over the FFI - let l: MlArray = [0]; - let r: MlArray = [0]; - for (let [, li, ri] of lr) { - l.push(li); - r.push(ri); - } - return new OpeningProof( - core.pointsToRust(l), - core.pointsToRust(r), - core.pointToRust(delta), - fieldToRust(z1), - fieldToRust(z2), - core.pointToRust(sg) - ); - } - function openingProofFromRust(proof: NapiOpeningProof): OpeningProof { - let [, ...l] = core.pointsFromRust(proof.lr_0); - let [, ...r] = core.pointsFromRust(proof.lr_1); - let n = l.length; - if (n !== r.length) throw Error('openingProofFromRust: l and r length mismatch.'); - let lr = l.map<[0, OrInfinity, OrInfinity]>((li, i) => [0, li, r[i]]); - let delta = core.pointFromRust(proof.delta); - let z1 = fieldFromRust(proof.z1); - let z2 = fieldFromRust(proof.z2); - let sg = core.pointFromRust(proof.sg); - proof.free(); - return [0, [0, ...lr], delta, z1, z2, sg]; - } - function runtimeTableToRust([, id, data]: RuntimeTable): NapiRuntimeTable { return new RuntimeTable(id, core.vectorToRust(data)); } @@ -184,68 +66,8 @@ function proofConversionPerField( } return new LookupTable(id, wasmData); } - - return { - proofToRust([, public_evals, proof]: ProofWithPublic): NapiProverProof { - let commitments = commitmentsToRust(proof[1]); - let openingProof = openingProofToRust(proof[2]); - let [, ...evals] = proofEvaluationsToRust(proof[3]); - let publicEvals = pointEvalsOptionToRust(public_evals); - // TODO typed as `any` in wasm-bindgen, this has the correct type - let evalsActual: NapiProofEvaluations = [0, publicEvals, ...evals]; - - let ftEval1 = fieldToRust(proof[4]); - let public_ = fieldsToRustFlat(proof[5]); - let [, ...prevChallenges] = proof[6]; - let n = prevChallenges.length; - let prevChallengeScalars = new VecVec(n); - let prevChallengeCommsMl: MlArray = [0]; - for (let [, scalars, comms] of prevChallenges) { - prevChallengeScalars.push(fieldsToRustFlat(scalars)); - prevChallengeCommsMl.push(comms); - } - let prevChallengeComms = core.polyCommsToRust(prevChallengeCommsMl); - return new ProverProof( - commitments, - openingProof, - evalsActual, - ftEval1, - public_, - prevChallengeScalars, - prevChallengeComms - ); - }, - proofFromRust(wasmProof: NapiProverProof): ProofWithPublic { - let commitments = commitmentsFromRust(wasmProof.commitments); - let openingProof = openingProofFromRust(wasmProof.proof); - // TODO typed as `any` in wasm-bindgen, this is the correct type - let [, wasmPublicEvals, ...wasmEvals]: NapiProofEvaluations = wasmProof.evals; - let publicEvals = pointEvalsOptionFromRust(wasmPublicEvals); - let evals = proofEvaluationsFromRust([0, ...wasmEvals]); - - let ftEval1 = fieldFromRust(wasmProof.ft_eval1); - let public_ = fieldsFromRustFlat(wasmProof.public_); - let prevChallengeScalars = wasmProof.prev_challenges_scalars; - let [, ...prevChallengeComms] = core.polyCommsFromRust(wasmProof.prev_challenges_comms); - let prevChallenges = prevChallengeComms.map((comms, i) => { - let scalars = fieldsFromRustFlat(prevChallengeScalars.get(i)); - return [0, scalars, comms]; - }); - wasmProof.free(); - let proof: ProverProof = [ - 0, - commitments, - openingProof, - evals, - ftEval1, - public_, - [0, ...prevChallenges], - ]; - return [0, publicEvals, proof]; - }, - runtimeTablesToRust([, ...tables]: MlArray): NapiRuntimeTable[] { return tables.map(runtimeTableToRust); }, @@ -259,7 +81,3 @@ function proofConversionPerField( }, }; } - -// helper - -type RemoveLeadingZero = T extends [0, ...infer U] ? U : never; From 0c0f8eba24bb51c7498ab9e00e3d42815e4f55aa Mon Sep 17 00:00:00 2001 From: querolita Date: Thu, 13 Nov 2025 12:45:39 +0100 Subject: [PATCH 03/23] Revert "bindings: remove types conversions for proof" This reverts commit f4528083226a6d878da9856d537dc7a71f9b6b58. Because we need access to the proof structure we cannot avoid the conversions. --- src/bindings/crypto/napi-conversion-proof.ts | 194 ++++++++++++++++++- 1 file changed, 188 insertions(+), 6 deletions(-) diff --git a/src/bindings/crypto/napi-conversion-proof.ts b/src/bindings/crypto/napi-conversion-proof.ts index adf08d7a10..b09f17fa20 100644 --- a/src/bindings/crypto/napi-conversion-proof.ts +++ b/src/bindings/crypto/napi-conversion-proof.ts @@ -1,29 +1,68 @@ import type { + WasmPastaFpLookupTable, WasmFpRuntimeTable, WasmPastaFpRuntimeTableCfg, - WasmPastaFpLookupTable, + WasmPastaFqLookupTable, WasmFqRuntimeTable, WasmPastaFqRuntimeTableCfg, - WasmPastaFqLookupTable, WasmVecVecFp, WasmVecVecFq, + WasmFpProverCommitments, + WasmFqProverCommitments, + WasmFpOpeningProof, + WasmFqOpeningProof, + WasmFpProverProof, + WasmFqProverProof, + WasmFpLookupCommitments, + WasmFqLookupCommitments, } from '../compiled/node_bindings/plonk_wasm.cjs'; import type * as napiNamespace from '../compiled/node_bindings/plonk_wasm.cjs'; -import type { RuntimeTable, RuntimeTableCfg, LookupTable } from './bindings/kimchi-types.js'; -import { MlArray } from '../../lib/ml/base.js'; -import { fieldsToRustFlat } from './bindings/conversion-base.js'; +import type { + RuntimeTable, + RuntimeTableCfg, + LookupTable, + ProofWithPublic, + ProverProof, + LookupCommitments, + PolyComm, PointEvaluations, ProofEvaluations, + RecursionChallenge, + ProverCommitments, + OpeningProof, + OrInfinity, +} from './bindings/kimchi-types.js'; +import { MlArray, MlTuple, MlOption} from '../../lib/ml/base.js'; +import { + fieldsToRustFlat, + fieldToRust, + fieldsFromRustFlat, + fieldFromRust, +} from './bindings/conversion-base.js'; import { ConversionCore, ConversionCores } from './napi-conversion-core.js'; export { napiProofConversion }; + +type NapiProofEvaluations = [ + 0, + MlOption>, + ...RemoveLeadingZero>, +]; type napi = typeof napiNamespace; +type NapiProverCommitments = WasmFpProverCommitments | WasmFqProverCommitments; +type NapiOpeningProof = WasmFpOpeningProof | WasmFqOpeningProof; +type NapiProverProof = WasmFpProverProof | WasmFqProverProof; +type NapiLookupCommitments = WasmFpLookupCommitments | WasmFqLookupCommitments; type NapiRuntimeTable = WasmFpRuntimeTable | WasmFqRuntimeTable; type NapiRuntimeTableCfg = WasmPastaFpRuntimeTableCfg | WasmPastaFqRuntimeTableCfg; type NapiLookupTable = WasmPastaFpLookupTable | WasmPastaFqLookupTable; type NapiClasses = { + ProverCommitments: typeof WasmFpProverCommitments | typeof WasmFqProverCommitments; + OpeningProof: typeof WasmFpOpeningProof | typeof WasmFqOpeningProof; VecVec: typeof WasmVecVecFp | typeof WasmVecVecFq; + ProverProof: typeof WasmFpProverProof | typeof WasmFqProverProof; + LookupCommitments: typeof WasmFpLookupCommitments | typeof WasmFqLookupCommitments; RuntimeTable: typeof WasmFpRuntimeTable | typeof WasmFqRuntimeTable; RuntimeTableCfg: typeof WasmPastaFpRuntimeTableCfg | typeof WasmPastaFqRuntimeTableCfg; LookupTable: typeof WasmPastaFpLookupTable | typeof WasmPastaFqLookupTable; @@ -32,13 +71,21 @@ type NapiClasses = { function napiProofConversion(napi: napi, core: ConversionCores) { return { fp: proofConversionPerField(core.fp, { + ProverCommitments: napi.WasmFpProverCommitments, + OpeningProof: napi.WasmFpOpeningProof, VecVec: napi.WasmVecVecFp, + ProverProof: napi.WasmFpProverProof, + LookupCommitments: napi.WasmFpLookupCommitments, RuntimeTable: napi.WasmFpRuntimeTable, RuntimeTableCfg: napi.WasmPastaFpRuntimeTableCfg, LookupTable: napi.WasmPastaFpLookupTable, }), fq: proofConversionPerField(core.fq, { + ProverCommitments: napi.WasmFqProverCommitments, + OpeningProof: napi.WasmFqOpeningProof, VecVec: napi.WasmVecVecFq, + ProverProof: napi.WasmFqProverProof, + LookupCommitments: napi.WasmFqLookupCommitments, RuntimeTable: napi.WasmFqRuntimeTable, RuntimeTableCfg: napi.WasmPastaFqRuntimeTableCfg, LookupTable: napi.WasmPastaFqLookupTable, @@ -48,8 +95,79 @@ function napiProofConversion(napi: napi, core: ConversionCores) { function proofConversionPerField( core: ConversionCore, - { VecVec, RuntimeTable, RuntimeTableCfg, LookupTable }: NapiClasses + { + ProverCommitments, + OpeningProof, + VecVec, + ProverProof, + LookupCommitments, + RuntimeTable, + RuntimeTableCfg, + LookupTable, + }: NapiClasses ) { + function commitmentsToRust(commitments: ProverCommitments): NapiProverCommitments { + let wComm = core.polyCommsToRust(commitments[1]); + let zComm = core.polyCommToRust(commitments[2]); + let tComm = core.polyCommToRust(commitments[3]); + let lookup = MlOption.mapFrom(commitments[4], lookupCommitmentsToRust); + return new ProverCommitments(wComm, zComm, tComm, lookup); + } + function commitmentsFromRust(commitments: NapiProverCommitments): ProverCommitments { + let wComm = core.polyCommsFromRust(commitments.w_comm); + let zComm = core.polyCommFromRust(commitments.z_comm); + let tComm = core.polyCommFromRust(commitments.t_comm); + let lookup = MlOption.mapTo(commitments.lookup, lookupCommitmentsFromRust); + commitments.free(); + return [0, wComm as MlTuple, zComm, tComm, lookup]; + } + + function lookupCommitmentsToRust(lookup: LookupCommitments): NapiLookupCommitments { + let sorted = core.polyCommsToRust(lookup[1]); + let aggreg = core.polyCommToRust(lookup[2]); + let runtime = MlOption.mapFrom(lookup[3], core.polyCommToRust); + return new LookupCommitments(sorted, aggreg, runtime); + } + function lookupCommitmentsFromRust(lookup: NapiLookupCommitments): LookupCommitments { + let sorted = core.polyCommsFromRust(lookup.sorted); + let aggreg = core.polyCommFromRust(lookup.aggreg); + let runtime = MlOption.mapTo(lookup.runtime, core.polyCommFromRust); + lookup.free(); + return [0, sorted, aggreg, runtime]; + } + + function openingProofToRust(proof: OpeningProof): NapiOpeningProof { + let [_, [, ...lr], delta, z1, z2, sg] = proof; + // We pass l and r as separate vectors over the FFI + let l: MlArray = [0]; + let r: MlArray = [0]; + for (let [, li, ri] of lr) { + l.push(li); + r.push(ri); + } + return new OpeningProof( + core.pointsToRust(l), + core.pointsToRust(r), + core.pointToRust(delta), + fieldToRust(z1), + fieldToRust(z2), + core.pointToRust(sg) + ); + } + function openingProofFromRust(proof: NapiOpeningProof): OpeningProof { + let [, ...l] = core.pointsFromRust(proof.lr_0); + let [, ...r] = core.pointsFromRust(proof.lr_1); + let n = l.length; + if (n !== r.length) throw Error('openingProofFromRust: l and r length mismatch.'); + let lr = l.map<[0, OrInfinity, OrInfinity]>((li, i) => [0, li, r[i]]); + let delta = core.pointFromRust(proof.delta); + let z1 = fieldFromRust(proof.z1); + let z2 = fieldFromRust(proof.z2); + let sg = core.pointFromRust(proof.sg); + proof.free(); + return [0, [0, ...lr], delta, z1, z2, sg]; + } + function runtimeTableToRust([, id, data]: RuntimeTable): NapiRuntimeTable { return new RuntimeTable(id, core.vectorToRust(data)); } @@ -66,8 +184,68 @@ function proofConversionPerField( } return new LookupTable(id, wasmData); } + + return { + proofToRust([, public_evals, proof]: ProofWithPublic): NapiProverProof { + let commitments = commitmentsToRust(proof[1]); + let openingProof = openingProofToRust(proof[2]); + let [, ...evals] = proofEvaluationsToRust(proof[3]); + let publicEvals = pointEvalsOptionToRust(public_evals); + // TODO typed as `any` in wasm-bindgen, this has the correct type + let evalsActual: NapiProofEvaluations = [0, publicEvals, ...evals]; + + let ftEval1 = fieldToRust(proof[4]); + let public_ = fieldsToRustFlat(proof[5]); + let [, ...prevChallenges] = proof[6]; + let n = prevChallenges.length; + let prevChallengeScalars = new VecVec(n); + let prevChallengeCommsMl: MlArray = [0]; + for (let [, scalars, comms] of prevChallenges) { + prevChallengeScalars.push(fieldsToRustFlat(scalars)); + prevChallengeCommsMl.push(comms); + } + let prevChallengeComms = core.polyCommsToRust(prevChallengeCommsMl); + return new ProverProof( + commitments, + openingProof, + evalsActual, + ftEval1, + public_, + prevChallengeScalars, + prevChallengeComms + ); + }, + proofFromRust(wasmProof: NapiProverProof): ProofWithPublic { + let commitments = commitmentsFromRust(wasmProof.commitments); + let openingProof = openingProofFromRust(wasmProof.proof); + // TODO typed as `any` in wasm-bindgen, this is the correct type + let [, wasmPublicEvals, ...wasmEvals]: NapiProofEvaluations = wasmProof.evals; + let publicEvals = pointEvalsOptionFromRust(wasmPublicEvals); + let evals = proofEvaluationsFromRust([0, ...wasmEvals]); + + let ftEval1 = fieldFromRust(wasmProof.ft_eval1); + let public_ = fieldsFromRustFlat(wasmProof.public_); + let prevChallengeScalars = wasmProof.prev_challenges_scalars; + let [, ...prevChallengeComms] = core.polyCommsFromRust(wasmProof.prev_challenges_comms); + let prevChallenges = prevChallengeComms.map((comms, i) => { + let scalars = fieldsFromRustFlat(prevChallengeScalars.get(i)); + return [0, scalars, comms]; + }); + wasmProof.free(); + let proof: ProverProof = [ + 0, + commitments, + openingProof, + evals, + ftEval1, + public_, + [0, ...prevChallenges], + ]; + return [0, publicEvals, proof]; + }, + runtimeTablesToRust([, ...tables]: MlArray): NapiRuntimeTable[] { return tables.map(runtimeTableToRust); }, @@ -81,3 +259,7 @@ function proofConversionPerField( }, }; } + +// helper + +type RemoveLeadingZero = T extends [0, ...infer U] ? U : never; From b59aba1b25ea9fa8cc5dee0d48659c5017ea6d5f Mon Sep 17 00:00:00 2001 From: querolita Date: Fri, 14 Nov 2025 12:37:56 +0100 Subject: [PATCH 04/23] submodule: update mina --- src/mina | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/mina b/src/mina index a7b1ba0a1e..ef9d64fbce 160000 --- a/src/mina +++ b/src/mina @@ -1 +1 @@ -Subproject commit a7b1ba0a1e64f9f36ed8df51998d4b53dd63cef1 +Subproject commit ef9d64fbce56e86c6a8b000813324cee3d646fbc From d741ec68d619a831beb90bd85a3eab3f9c47cb2e Mon Sep 17 00:00:00 2001 From: Shigoto-dev19 Date: Fri, 14 Nov 2025 19:57:31 +0300 Subject: [PATCH 05/23] Native Rust conversion for oracles --- src/bindings/crypto/bindings.ts | 6 +- .../bindings/napi-conversion-oracles.ts | 123 ++++++++++++++++++ 2 files changed, 127 insertions(+), 2 deletions(-) create mode 100644 src/bindings/crypto/bindings/napi-conversion-oracles.ts diff --git a/src/bindings/crypto/bindings.ts b/src/bindings/crypto/bindings.ts index ee231d6261..a1c5e8a202 100644 --- a/src/bindings/crypto/bindings.ts +++ b/src/bindings/crypto/bindings.ts @@ -14,6 +14,7 @@ import { verifierIndexConversion } from './bindings/conversion-verifier-index.js import { PallasBindings, VestaBindings } from './bindings/curve.js'; import { jsEnvironment } from './bindings/env.js'; import { FpBindings, FqBindings } from './bindings/field.js'; +import { napiOraclesConversion } from './bindings/napi-conversion-oracles.js'; import { srs } from './bindings/srs.js'; import { FpVectorBindings, FqVectorBindings } from './bindings/vector.js'; import { napiConversionCore } from './napi-conversion-core.js'; @@ -85,9 +86,10 @@ function buildWasmConversion(wasm: Wasm) { function createNativeRustConversion(napi: any) { let core = napiConversionCore(napi); let proof = napiProofConversion(napi, core); + let oracles = napiOraclesConversion(napi); return { - fp: { ...core.fp, ...proof.fp }, - fq: { ...core.fq, ...proof.fq }, + fp: { ...core.fp, ...proof.fp, ...oracles.fp }, + fq: { ...core.fq, ...proof.fq, ...oracles.fq }, }; } diff --git a/src/bindings/crypto/bindings/napi-conversion-oracles.ts b/src/bindings/crypto/bindings/napi-conversion-oracles.ts new file mode 100644 index 0000000000..13bcb42f96 --- /dev/null +++ b/src/bindings/crypto/bindings/napi-conversion-oracles.ts @@ -0,0 +1,123 @@ +import { MlOption } from '../../../lib/ml/base.js'; +import type * as napiNamespace from '../../compiled/node_bindings/plonk_wasm.cjs'; +import type { + WasmFpOracles, + WasmFpRandomOracles, + WasmFqOracles, + WasmFqRandomOracles, +} from '../../compiled/node_bindings/plonk_wasm.cjs'; +import { + fieldFromRust, + fieldToRust, + fieldsFromRustFlat, + fieldsToRustFlat, + maybeFieldToRust, +} from './conversion-base.js'; +import { Field, Oracles, RandomOracles, ScalarChallenge } from './kimchi-types.js'; + +export { napiOraclesConversion }; + +type napi = typeof napiNamespace; + +type NapiRandomOracles = WasmFpRandomOracles | WasmFqRandomOracles; +type NapiOracles = WasmFpOracles | WasmFqOracles; + +type NapiClasses = { + RandomOracles: typeof WasmFpRandomOracles | typeof WasmFqRandomOracles; + Oracles: typeof WasmFpOracles | typeof WasmFqOracles; +}; + +function napiOraclesConversion(napi: napi) { + return { + fp: oraclesConversionPerField({ + RandomOracles: napi.WasmFpRandomOracles, + Oracles: napi.WasmFpOracles, + }), + fq: oraclesConversionPerField({ + RandomOracles: napi.WasmFqRandomOracles, + Oracles: napi.WasmFqOracles, + }), + }; +} + +function oraclesConversionPerField({ RandomOracles, Oracles }: NapiClasses) { + function randomOraclesToRust(ro: RandomOracles): NapiRandomOracles { + let jointCombinerMl = MlOption.from(ro[1]); + let jointCombinerChal = maybeFieldToRust(jointCombinerMl?.[1][1]); + let jointCombiner = maybeFieldToRust(jointCombinerMl?.[2]); + let beta = fieldToRust(ro[2]); + let gamma = fieldToRust(ro[3]); + let alphaChal = fieldToRust(ro[4][1]); + let alpha = fieldToRust(ro[5]); + let zeta = fieldToRust(ro[6]); + let v = fieldToRust(ro[7]); + let u = fieldToRust(ro[8]); + let zetaChal = fieldToRust(ro[9][1]); + let vChal = fieldToRust(ro[10][1]); + let uChal = fieldToRust(ro[11][1]); + return new RandomOracles( + jointCombinerChal, + jointCombiner, + beta, + gamma, + alphaChal, + alpha, + zeta, + v, + u, + zetaChal, + vChal, + uChal + ); + } + function randomOraclesFromRust(ro: NapiRandomOracles): RandomOracles { + let jointCombinerChal = ro.joint_combiner_chal; + let jointCombiner = ro.joint_combiner; + let jointCombinerOption = MlOption<[0, ScalarChallenge, Field]>( + jointCombinerChal && + jointCombiner && [0, [0, fieldFromRust(jointCombinerChal)], fieldFromRust(jointCombiner)] + ); + let mlRo: RandomOracles = [ + 0, + jointCombinerOption, + fieldFromRust(ro.beta), + fieldFromRust(ro.gamma), + [0, fieldFromRust(ro.alpha_chal)], + fieldFromRust(ro.alpha), + fieldFromRust(ro.zeta), + fieldFromRust(ro.v), + fieldFromRust(ro.u), + [0, fieldFromRust(ro.zeta_chal)], + [0, fieldFromRust(ro.v_chal)], + [0, fieldFromRust(ro.u_chal)], + ]; + // TODO: do we not want to free? + // ro.free(); + return mlRo; + } + + return { + oraclesToRust(oracles: Oracles): NapiOracles { + let [, o, pEval, openingPrechallenges, digestBeforeEvaluations] = oracles; + return new Oracles( + randomOraclesToRust(o), + fieldToRust(pEval[1]), + fieldToRust(pEval[2]), + fieldsToRustFlat(openingPrechallenges), + fieldToRust(digestBeforeEvaluations) + ); + }, + oraclesFromRust(oracles: NapiOracles): Oracles { + let mlOracles: Oracles = [ + 0, + randomOraclesFromRust(oracles.o), + [0, fieldFromRust(oracles.p_eval0), fieldFromRust(oracles.p_eval1)], + fieldsFromRustFlat(oracles.opening_prechallenges), + fieldFromRust(oracles.digest_before_evaluations), + ]; + // TODO: do we not want to free? + // oracles.free(); + return mlOracles; + }, + }; +} From cba0e65cadce66294c95609dcb88b072b368115d Mon Sep 17 00:00:00 2001 From: querolita Date: Fri, 14 Nov 2025 23:09:56 +0100 Subject: [PATCH 06/23] bindings: silent compiler using any in napi proof conversion --- src/bindings/crypto/napi-conversion-proof.ts | 99 ++++++++++++++++++-- 1 file changed, 93 insertions(+), 6 deletions(-) diff --git a/src/bindings/crypto/napi-conversion-proof.ts b/src/bindings/crypto/napi-conversion-proof.ts index b09f17fa20..690b416783 100644 --- a/src/bindings/crypto/napi-conversion-proof.ts +++ b/src/bindings/crypto/napi-conversion-proof.ts @@ -38,9 +38,17 @@ import { fieldFromRust, } from './bindings/conversion-base.js'; import { ConversionCore, ConversionCores } from './napi-conversion-core.js'; +import type { Field } from './bindings/field.js'; export { napiProofConversion }; +const fieldToRust_ = (x: Field) => fieldToRust(x); +const proofEvaluationsToRust = mapProofEvaluations(fieldToRust_); +const proofEvaluationsFromRust = mapProofEvaluations(fieldFromRust); +const pointEvalsOptionToRust = mapPointEvalsOption(fieldToRust_); +const pointEvalsOptionFromRust = mapPointEvalsOption(fieldFromRust); + + type NapiProofEvaluations = [ 0, MlOption>, @@ -111,7 +119,7 @@ function proofConversionPerField( let zComm = core.polyCommToRust(commitments[2]); let tComm = core.polyCommToRust(commitments[3]); let lookup = MlOption.mapFrom(commitments[4], lookupCommitmentsToRust); - return new ProverCommitments(wComm, zComm, tComm, lookup); + return new ProverCommitments(wComm as any, zComm as any, tComm as any, lookup as any); } function commitmentsFromRust(commitments: NapiProverCommitments): ProverCommitments { let wComm = core.polyCommsFromRust(commitments.w_comm); @@ -126,7 +134,7 @@ function proofConversionPerField( let sorted = core.polyCommsToRust(lookup[1]); let aggreg = core.polyCommToRust(lookup[2]); let runtime = MlOption.mapFrom(lookup[3], core.polyCommToRust); - return new LookupCommitments(sorted, aggreg, runtime); + return new LookupCommitments(sorted as any, aggreg as any, runtime as any); } function lookupCommitmentsFromRust(lookup: NapiLookupCommitments): LookupCommitments { let sorted = core.polyCommsFromRust(lookup.sorted); @@ -146,15 +154,15 @@ function proofConversionPerField( r.push(ri); } return new OpeningProof( - core.pointsToRust(l), - core.pointsToRust(r), + core.pointsToRust(l) as any, + core.pointsToRust(r) as any, core.pointToRust(delta), fieldToRust(z1), fieldToRust(z2), core.pointToRust(sg) ); } - function openingProofFromRust(proof: NapiOpeningProof): OpeningProof { + function openingProofFromRust(proof: any): OpeningProof { let [, ...l] = core.pointsFromRust(proof.lr_0); let [, ...r] = core.pointsFromRust(proof.lr_1); let n = l.length; @@ -214,7 +222,7 @@ function proofConversionPerField( ftEval1, public_, prevChallengeScalars, - prevChallengeComms + prevChallengeComms as any ); }, proofFromRust(wasmProof: NapiProverProof): ProofWithPublic { @@ -260,6 +268,85 @@ function proofConversionPerField( }; } +function createMapPointEvals(map: (x: Field1) => Field2) { + return (evals: PointEvaluations): PointEvaluations => { + let [, zeta, zeta_omega] = evals; + return [0, MlArray.map(zeta, map), MlArray.map(zeta_omega, map)]; + }; +} + +function mapPointEvalsOption(map: (x: Field1) => Field2) { + return (evals: MlOption>) => + MlOption.map(evals, createMapPointEvals(map)); +} + +function mapProofEvaluations(map: (x: Field1) => Field2) { + const mapPointEvals = createMapPointEvals(map); + + const mapPointEvalsOption = ( + evals: MlOption> + ): MlOption> => MlOption.map(evals, mapPointEvals); + + return function mapProofEvaluations(evals: ProofEvaluations): ProofEvaluations { + let [ + , + w, + z, + s, + coeffs, + genericSelector, + poseidonSelector, + completeAddSelector, + mulSelector, + emulSelector, + endomulScalarSelector, + rangeCheck0Selector, + rangeCheck1Selector, + foreignFieldAddSelector, + foreignFieldMulSelector, + xorSelector, + rotSelector, + lookupAggregation, + lookupTable, + lookupSorted, + runtimeLookupTable, + runtimeLookupTableSelector, + xorLookupSelector, + lookupGateLookupSelector, + rangeCheckLookupSelector, + foreignFieldMulLookupSelector, + ] = evals; + return [ + 0, + MlTuple.map(w, mapPointEvals), + mapPointEvals(z), + MlTuple.map(s, mapPointEvals), + MlTuple.map(coeffs, mapPointEvals), + mapPointEvals(genericSelector), + mapPointEvals(poseidonSelector), + mapPointEvals(completeAddSelector), + mapPointEvals(mulSelector), + mapPointEvals(emulSelector), + mapPointEvals(endomulScalarSelector), + mapPointEvalsOption(rangeCheck0Selector), + mapPointEvalsOption(rangeCheck1Selector), + mapPointEvalsOption(foreignFieldAddSelector), + mapPointEvalsOption(foreignFieldMulSelector), + mapPointEvalsOption(xorSelector), + mapPointEvalsOption(rotSelector), + mapPointEvalsOption(lookupAggregation), + mapPointEvalsOption(lookupTable), + MlArray.map(lookupSorted, mapPointEvalsOption), + mapPointEvalsOption(runtimeLookupTable), + mapPointEvalsOption(runtimeLookupTableSelector), + mapPointEvalsOption(xorLookupSelector), + mapPointEvalsOption(lookupGateLookupSelector), + mapPointEvalsOption(rangeCheckLookupSelector), + mapPointEvalsOption(foreignFieldMulLookupSelector), + ]; + }; +} + // helper type RemoveLeadingZero = T extends [0, ...infer U] ? U : never; From 0e5f958f574b7b6bfe807b2cba1850d7eb3a7a60 Mon Sep 17 00:00:00 2001 From: querolita Date: Wed, 19 Nov 2025 00:49:09 +0100 Subject: [PATCH 07/23] bindings: create napi srs bindings --- src/bindings/crypto/bindings.ts | 7 +- src/bindings/crypto/napi-srs.ts | 343 ++++++++++++++++++++++++++++++++ tests/native/native.ts | 2 + 3 files changed, 350 insertions(+), 2 deletions(-) create mode 100644 src/bindings/crypto/napi-srs.ts diff --git a/src/bindings/crypto/bindings.ts b/src/bindings/crypto/bindings.ts index 23b133cdc7..97b65cd470 100644 --- a/src/bindings/crypto/bindings.ts +++ b/src/bindings/crypto/bindings.ts @@ -15,11 +15,13 @@ import { PallasBindings, VestaBindings } from './bindings/curve.js'; import { jsEnvironment } from './bindings/env.js'; import { FpBindings, FqBindings } from './bindings/field.js'; import { srs } from './bindings/srs.js'; +import { srs as napiSrs } from './napi-srs.js'; import { FpVectorBindings, FqVectorBindings } from './bindings/vector.js'; import { napiConversionCore } from './napi-conversion-core.js'; import { napiProofConversion } from './napi-conversion-proof.js'; +import type * as napiNamespace from '../compiled/node_bindings/plonk_wasm.cjs'; -export { RustConversion, Wasm, createNativeRustConversion, getRustConversion }; +export { RustConversion, Wasm, Napi, createNativeRustConversion, getRustConversion }; /* TODO: Uncomment in phase 2 of conversion layer import { conversionCore as conversionCoreNative } from './native/conversion-core.js'; @@ -49,13 +51,14 @@ const tsBindings = { return bundle.srsFactory(wasm, bundle.conversion); },*/ srs: (wasm: Wasm) => srs(wasm, getRustConversion(wasm)), - srsNative: (napi: Wasm) => srs(napi, createNativeRustConversion(napi) as any), + srsNative: (napi: Napi) => napiSrs(napi, createNativeRustConversion(napi) as any), }; // this is put in a global variable so that mina/src/lib/crypto/kimchi_bindings/js/bindings.js finds it (globalThis as any).__snarkyTsBindings = tsBindings; type Wasm = typeof wasmNamespace; +type Napi = typeof napiNamespace; type RustConversion = ReturnType; diff --git a/src/bindings/crypto/napi-srs.ts b/src/bindings/crypto/napi-srs.ts new file mode 100644 index 0000000000..fe61e75b4b --- /dev/null +++ b/src/bindings/crypto/napi-srs.ts @@ -0,0 +1,343 @@ +import { MlArray } from '../../lib/ml/base.js'; +import { + readCache, + withVersion, + writeCache, + type Cache, + type CacheHeader, +} from '../../lib/proof-system/cache.js'; +import { assert } from '../../lib/util/errors.js'; +import { type WasmFpSrs, type WasmFqSrs } from '../compiled/node_bindings/plonk_wasm.cjs'; +import type { RustConversion, Napi } from './bindings.js'; +import { OrInfinity, OrInfinityJson } from './bindings/curve.js'; +import { PolyComm } from './bindings/kimchi-types.js'; + +export { setSrsCache, srs, unsetSrsCache }; + +type NapiSrs = WasmFpSrs | WasmFqSrs; + +type SrsStore = Record; + +function empty(): SrsStore { + return {}; +} + +const srsStore = { fp: empty(), fq: empty() }; + +const CacheReadRegister = new Map(); + +let cache: Cache | undefined; + +function setSrsCache(c: Cache) { + cache = c; +} +function unsetSrsCache() { + cache = undefined; +} + +const srsVersion = 1; + +function cacheHeaderLagrange(f: 'fp' | 'fq', domainSize: number): CacheHeader { + let id = `lagrange-basis-${f}-${domainSize}`; + return withVersion( + { + kind: 'lagrange-basis', + persistentId: id, + uniqueId: id, + dataType: 'string', + }, + srsVersion + ); +} +function cacheHeaderSrs(f: 'fp' | 'fq', domainSize: number): CacheHeader { + let id = `srs-${f}-${domainSize}`; + return withVersion( + { + kind: 'srs', + persistentId: id, + uniqueId: id, + dataType: 'string', + }, + srsVersion + ); +} + +function srs(napi: Napi , conversion: RustConversion) { + return { + fp: srsPerField('fp', napi, conversion), + fq: srsPerField('fq', napi, conversion), + }; +} + +function srsPerField(f: 'fp' | 'fq', napi: Napi, conversion: RustConversion) { + // note: these functions are properly typed, thanks to TS template literal types + let createSrs = (size: number) => { + try { + console.log(0); + return napi[`caml_${f}_srs_create_parallel`](size); + } catch (error) { + console.error(`Error in SRS get for field ${f}`); + throw error; + } + }; + let getSrs = (srs: NapiSrs) => { + try { + console.log(1); + let v = napi[`caml_${f}_srs_get`](srs); + console.log(2); + return v; + } catch (error) { + console.error(`Error in SRS get for field ${f}`); + throw error; + } + }; + let setSrs = (bytes: any) => { + try { + console.log(2); + return napi[`caml_${f}_srs_set`](bytes); + } catch (error) { + console.error(`Error in SRS set for field ${f} args ${bytes}`); + throw error; + } + }; + + let maybeLagrangeCommitment = (srs: NapiSrs, domain_size: number, i: number) => { + try { + return napi[`caml_${f}_srs_maybe_lagrange_commitment`](srs, domain_size, i); + } catch (error) { + console.error(`Error in SRS maybe lagrange commitment for field ${f}`); + throw error; + } + }; + let lagrangeCommitment = (srs: NapiSrs, domain_size: number, i: number) => { + try { + return napi[`caml_${f}_srs_lagrange_commitment`](srs, domain_size, i); + } catch (error) { + console.error(`Error in SRS lagrange commitment for field ${f}`); + throw error; + } + }; + let setLagrangeBasis = (srs: NapiSrs, domain_size: number, input: any) => { + try { + console.log(6); + return napi[`caml_${f}_srs_set_lagrange_basis`](srs, domain_size, input); + } catch (error) { + console.error(`Error in SRS set lagrange basis for field ${f}`); + throw error; + } + }; + let getLagrangeBasis = (srs: NapiSrs, n: number) => { + try { + return napi[`caml_${f}_srs_get_lagrange_basis`](srs, n); + } catch (error) { + console.error(`Error in SRS get lagrange basis for field ${f}`); + throw error; + } + }; + return { + /** + * returns existing stored SRS or falls back to creating a new one + */ + create(size: number): NapiSrs { + let srs = srsStore[f][size] satisfies NapiSrs as NapiSrs | undefined; + + if (srs === undefined) { + if (cache === undefined) { + // if there is no cache, create SRS in memory + console.log('Creating SRS without cache'); + srs = createSrs(size); + console.log('SRS created without cache:', srs); + } else { + let header = cacheHeaderSrs(f, size); + + // try to read SRS from cache / recompute and write if not found + console.log('Reading SRS from cache'); + srs = readCache(cache, header, (bytes) => { + // TODO: this takes a bit too long, about 300ms for 2^16 + // `pointsToRust` is the clear bottleneck + let jsonSrs: OrInfinityJson[] = JSON.parse(new TextDecoder().decode(bytes)); + let mlSrs = MlArray.mapTo(jsonSrs, OrInfinity.fromJSON); + let wasmSrs = conversion[f].pointsToRust(mlSrs); + return setSrs(wasmSrs); + }); + console.log('SRS read from cache:', srs); + if (srs === undefined) { + // not in cache + console.log(1); + srs = createSrs(size); + console.log('Writing SRS to cache', srs); + + if (cache.canWrite) { + console.log(2); + let wasmSrs = getSrs(srs); + console.log(3); + let mlSrs = conversion[f].pointsFromRust(wasmSrs); + let jsonSrs = MlArray.mapFrom(mlSrs, OrInfinity.toJSON); + let bytes = new TextEncoder().encode(JSON.stringify(jsonSrs)); + + writeCache(cache, header, bytes); + } + } + } + console.log('Storing SRS in memory'); + srsStore[f][size] = srs; + console.log('SRS stored in memory:', srs); + } + + // TODO should we call freeOnFinalize() and expose a function to clean the SRS cache? + console.trace('Returning SRS:', srs); + return srsStore[f][size]; + }, + + /** + * returns ith Lagrange basis commitment for a given domain size + */ + lagrangeCommitment(srs: NapiSrs, domainSize: number, i: number): PolyComm { + console.log('lagrangeCommitment'); + // happy, fast case: if basis is already stored on the srs, return the ith commitment + let commitment = maybeLagrangeCommitment(srs, domainSize, i); + + if (commitment === undefined) { + if (cache === undefined) { + // if there is no cache, recompute and store basis in memory + commitment = lagrangeCommitment(srs, domainSize, i); + } else { + // try to read lagrange basis from cache / recompute and write if not found + let header = cacheHeaderLagrange(f, domainSize); + let didRead = readCacheLazy( + cache, + header, + conversion, + f, + srs, + domainSize, + setLagrangeBasis + ); + if (didRead !== true) { + // not in cache + if (cache.canWrite) { + // TODO: this code path will throw on the web since `caml_${f}_srs_get_lagrange_basis` is not properly implemented + // using a writable cache in the browser seems to be fairly uncommon though, so it's at least an 80/20 solution + let napiComms = getLagrangeBasis(srs, domainSize); + console.log('napiComms', napiComms); + let mlComms = conversion[f].polyCommsFromRust(napiComms); + console.log('mlComms', mlComms); + let comms = polyCommsToJSON(mlComms); + let bytes = new TextEncoder().encode(JSON.stringify(comms)); + writeCache(cache, header, bytes); + } else { + lagrangeCommitment(srs, domainSize, i); + } + } + // here, basis is definitely stored on the srs + let c = maybeLagrangeCommitment(srs, domainSize, i); + assert(c !== undefined, 'commitment exists after setting'); + commitment = c; + } + } + + // edge case for when we have a writeable cache and the basis was already stored on the srs + // but we didn't store it in the cache separately yet + if (commitment && cache && cache.canWrite) { + let header = cacheHeaderLagrange(f, domainSize); + let didRead = readCacheLazy( + cache, + header, + conversion, + f, + srs, + domainSize, + setLagrangeBasis + ); + // only proceed for entries we haven't written to the cache yet + if (didRead !== true) { + // same code as above - write the lagrange basis to the cache if it wasn't there already + // currently we re-generate the basis via `getLagrangeBasis` - we could derive this from the + // already existing `commitment` instead, but this is simpler and the performance impact is negligible + let napiComms = getLagrangeBasis(srs, domainSize); + let mlComms = conversion[f].polyCommsFromRust(napiComms); + let comms = polyCommsToJSON(mlComms); + let bytes = new TextEncoder().encode(JSON.stringify(comms)); + + writeCache(cache, header, bytes); + } + } + return conversion[f].polyCommFromRust(commitment); + }, + + /** + * Returns the Lagrange basis commitments for the whole domain + */ + lagrangeCommitmentsWholeDomain(srs: NapiSrs, domainSize: number) { + console.log('lagrangeCommitmentsWholeDomain'); + try { + let napiComms = napi[`caml_${f}_srs_lagrange_commitments_whole_domain_ptr`](srs, domainSize); + let mlComms = conversion[f].polyCommsFromRust(napiComms as any); + return mlComms; + } catch (error) { + console.error(`Error in SRS lagrange commitments whole domain ptr for field ${f}`); + throw error; + } + }, + + /** + * adds Lagrange basis for a given domain size + */ + addLagrangeBasis(srs: NapiSrs, logSize: number) { + console.log('addLagrangeBasis'); + // this ensures that basis is stored on the srs, no need to duplicate caching logic + this.lagrangeCommitment(srs, 1 << logSize, 0); + }, + }; +} + +type PolyCommJson = { + shifted: OrInfinityJson[]; + unshifted: OrInfinityJson | undefined; +}; + +function polyCommsToJSON(comms: MlArray): PolyCommJson[] { + return MlArray.mapFrom(comms, ([, elems]) => { + return { + shifted: MlArray.mapFrom(elems, OrInfinity.toJSON), + unshifted: undefined, + }; + }); +} + +function polyCommsFromJSON(json: PolyCommJson[]): MlArray { + return MlArray.mapTo(json, ({ shifted, unshifted }) => { + return [0, MlArray.mapTo(shifted, OrInfinity.fromJSON)]; + }); +} + +function readCacheLazy( + cache: Cache, + header: CacheHeader, + conversion: RustConversion, + f: 'fp' | 'fq', + srs: NapiSrs, + domainSize: number, + setLagrangeBasis: (srs: NapiSrs, domainSize: number, comms: Uint32Array) => void +) { + if (CacheReadRegister.get(header.uniqueId) === true) return true; + return readCache(cache, header, (bytes) => { + let comms: PolyCommJson[] = JSON.parse(new TextDecoder().decode(bytes)); + let mlComms = polyCommsFromJSON(comms); + let napiComms = conversion[f].polyCommsToRust(mlComms); + + setLagrangeBasis(srs, domainSize, napiComms); + CacheReadRegister.set(header.uniqueId, true); + return true; + }); +} +function runInTryCatch any>(fn: T): T { + return function (...args: Parameters): ReturnType { + try { + return fn(...args); + } catch (e) { + console.error(`Error in SRS function ${fn.name} with args:`, args); + throw e; + } + } as T; +} diff --git a/tests/native/native.ts b/tests/native/native.ts index 9216ddbb00..1b6da18c17 100644 --- a/tests/native/native.ts +++ b/tests/native/native.ts @@ -1,6 +1,8 @@ import assert from 'node:assert'; import native from '../../src/native/native'; +// run with `./run tests/native/native.ts --bundle` + console.log(native); assert(native.getNativeCalls() == 0n, 'native module starts with no calls'); From ce58b3fdc97d806cff3b3bc4cde1c7d4cfb4087b Mon Sep 17 00:00:00 2001 From: querolita Date: Wed, 19 Nov 2025 18:12:33 +0100 Subject: [PATCH 08/23] submodule: update mina --- src/mina | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/mina b/src/mina index 55730353ad..6189fa7b99 160000 --- a/src/mina +++ b/src/mina @@ -1 +1 @@ -Subproject commit 55730353ad13944294ebd9fb457ec33fbcf99239 +Subproject commit 6189fa7b99e90a34673ac19519cb0271816b306a From ca1f429e6e89d43c1a37a2af4f9ba6af16463a49 Mon Sep 17 00:00:00 2001 From: querolita Date: Thu, 20 Nov 2025 00:29:14 +0100 Subject: [PATCH 09/23] bindings: fix gate vector unit test notation --- .../bindings/gate-vector-napi.unit-test.ts | 39 +++++++++++++------ 1 file changed, 28 insertions(+), 11 deletions(-) diff --git a/src/bindings/crypto/bindings/gate-vector-napi.unit-test.ts b/src/bindings/crypto/bindings/gate-vector-napi.unit-test.ts index 8f6939db1f..9b85604c4e 100644 --- a/src/bindings/crypto/bindings/gate-vector-napi.unit-test.ts +++ b/src/bindings/crypto/bindings/gate-vector-napi.unit-test.ts @@ -6,9 +6,11 @@ import type { Field, Gate, Wire } from './kimchi-types.js'; const require = createRequire(import.meta.url); function loadNative() { + const slug = `${process.platform}-${process.arch}`; const candidates = [ - '../../compiled/_node_bindings/plonk_napi.node', + `../../../../../native/${slug}/plonk_napi.node`, '../../compiled/node_bindings/plonk_napi.node', + '../../compiled/_node_bindings/plonk_napi.node', ]; for (const path of candidates) { try { @@ -23,6 +25,21 @@ function loadNative() { const native: any = loadNative(); +const gateVectorCreate = + native.caml_pasta_fp_plonk_gate_vector_create ?? native.camlPastaFpPlonkGateVectorCreate; +const gateVectorLen = + native.caml_pasta_fp_plonk_gate_vector_len ?? native.camlPastaFpPlonkGateVectorLen; +const gateVectorAdd = + native.caml_pasta_fp_plonk_gate_vector_add ?? native.camlPastaFpPlonkGateVectorAdd; +const gateVectorGet = + native.caml_pasta_fp_plonk_gate_vector_get ?? native.camlPastaFpPlonkGateVectorGet; +const gateVectorWrap = + native.caml_pasta_fp_plonk_gate_vector_wrap ?? native.camlPastaFpPlonkGateVectorWrap; +const gateVectorDigest = + native.caml_pasta_fp_plonk_gate_vector_digest ?? native.camlPastaFpPlonkGateVectorDigest; +const circuitSerialize = + native.caml_pasta_fp_plonk_circuit_serialize ?? native.camlPastaFpPlonkCircuitSerialize; + const { fp } = napiConversionCore(native); const zeroField: Field = [0, 0n]; @@ -44,24 +61,24 @@ const sampleGate: Gate = [ [0, zeroField, zeroField, zeroField, zeroField, zeroField, zeroField, zeroField], ]; -const vector = native.camlPastaFpPlonkGateVectorCreate(); -expect(native.camlPastaFpPlonkGateVectorLen(vector)).toBe(0); +const vector = gateVectorCreate(); +expect(gateVectorLen(vector)).toBe(0); -native.camlPastaFpPlonkGateVectorAdd(vector, fp.gateToRust(sampleGate)); -expect(native.camlPastaFpPlonkGateVectorLen(vector)).toBe(1); +gateVectorAdd(vector, fp.gateToRust(sampleGate)); +expect(gateVectorLen(vector)).toBe(1); -const gate0 = native.camlPastaFpPlonkGateVectorGet(vector, 0); +const gate0 = gateVectorGet(vector, 0); expect(gate0.typ).toBe(sampleGate[1]); const rustTarget = fp.wireToRust(mlWire(0, 0)); const rustHead = fp.wireToRust(mlWire(1, 2)); -native.camlPastaFpPlonkGateVectorWrap(vector, rustTarget, rustHead); -const wrapped = native.camlPastaFpPlonkGateVectorGet(vector, 0); +gateVectorWrap(vector, rustTarget, rustHead); +const wrapped = gateVectorGet(vector, 0); expect(wrapped.wires.w0).toEqual({ row: 1, col: 2 }); -native.camlPastaFpPlonkGateVectorDigest(0, vector); -native.camlPastaFpPlonkCircuitSerialize(0, vector); +gateVectorDigest(0, vector); +circuitSerialize(0, vector); -console.log('{}', native.camlPastaFpPlonkGateVectorDigest(0, vector)); +console.log('{}', gateVectorDigest(0, vector)); console.log('gate vector napi bindings (fp) are working ✔️'); From e75b3f364bae9c305f98cc51523885e6ff9adea2 Mon Sep 17 00:00:00 2001 From: querolita Date: Thu, 20 Nov 2025 21:19:06 +0100 Subject: [PATCH 10/23] submodule: update mina --- src/mina | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/mina b/src/mina index 6189fa7b99..6d9b2f6fe2 160000 --- a/src/mina +++ b/src/mina @@ -1 +1 @@ -Subproject commit 6189fa7b99e90a34673ac19519cb0271816b306a +Subproject commit 6d9b2f6fe2d7f686897062637eb036f17dc41ba8 From e7c5292b92a9572434d8c0eec0f9c0beb92d5a3e Mon Sep 17 00:00:00 2001 From: querolita Date: Thu, 20 Nov 2025 21:24:12 +0100 Subject: [PATCH 11/23] submodule: update mina --- src/mina | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/mina b/src/mina index 6d9b2f6fe2..3116ffcdb3 160000 --- a/src/mina +++ b/src/mina @@ -1 +1 @@ -Subproject commit 6d9b2f6fe2d7f686897062637eb036f17dc41ba8 +Subproject commit 3116ffcdb35038a04624112305d9ab618990c7d2 From c5a5da4be17d320abe840ec8e96928fcd6687063 Mon Sep 17 00:00:00 2001 From: querolita Date: Wed, 26 Nov 2025 17:09:30 +0100 Subject: [PATCH 12/23] bindings: first version of verifier index conversion --- src/bindings/crypto/bindings.ts | 6 +- src/bindings/crypto/napi-conversion-core.ts | 12 +- src/bindings/crypto/napi-conversion-proof.ts | 2 - .../crypto/napi-conversion-verifier-index.ts | 284 ++++++++++++++++++ 4 files changed, 293 insertions(+), 11 deletions(-) create mode 100644 src/bindings/crypto/napi-conversion-verifier-index.ts diff --git a/src/bindings/crypto/bindings.ts b/src/bindings/crypto/bindings.ts index f3d4817c29..645abf9b34 100644 --- a/src/bindings/crypto/bindings.ts +++ b/src/bindings/crypto/bindings.ts @@ -20,6 +20,7 @@ import { srs as napiSrs } from './napi-srs.js'; import { FpVectorBindings, FqVectorBindings } from './bindings/vector.js'; import { napiConversionCore } from './napi-conversion-core.js'; import { napiProofConversion } from './napi-conversion-proof.js'; +import { napiVerifierIndexConversion } from './napi-conversion-verifier-index.js'; import type * as napiNamespace from '../compiled/node_bindings/plonk_wasm.cjs'; export { RustConversion, Wasm, Napi, createNativeRustConversion, getRustConversion }; @@ -90,10 +91,11 @@ function buildWasmConversion(wasm: Wasm) { function createNativeRustConversion(napi: any) { let core = napiConversionCore(napi); let proof = napiProofConversion(napi, core); + let verif = napiVerifierIndexConversion(napi, core); let oracles = napiOraclesConversion(napi); return { - fp: { ...core.fp, ...proof.fp, ...oracles.fp }, - fq: { ...core.fq, ...proof.fq, ...oracles.fq }, + fp: { ...core.fp, ...proof.fp, ...verif.fp, ...oracles.fp }, + fq: { ...core.fq, ...proof.fq, ...verif.fq, ...oracles.fq }, }; } diff --git a/src/bindings/crypto/napi-conversion-core.ts b/src/bindings/crypto/napi-conversion-core.ts index 7409d16939..5c1a403ce6 100644 --- a/src/bindings/crypto/napi-conversion-core.ts +++ b/src/bindings/crypto/napi-conversion-core.ts @@ -70,15 +70,13 @@ function napiConversionCore(napi: any) { }; return { - fp: { ...fpCore }, + fp: { + ...fpCore, + }, fq: { ...fqCore, - shiftsFromRust: (s: any) => { - let shifts = [s.s0, s.s1, s.s2, s.s3, s.s4, s.s5, s.s6]; - return [0, ...shifts.map(fieldFromRust)]; - }, - }, - ...shared, + }, + ...shared, }; } diff --git a/src/bindings/crypto/napi-conversion-proof.ts b/src/bindings/crypto/napi-conversion-proof.ts index 690b416783..5df1610ca3 100644 --- a/src/bindings/crypto/napi-conversion-proof.ts +++ b/src/bindings/crypto/napi-conversion-proof.ts @@ -192,8 +192,6 @@ function proofConversionPerField( } return new LookupTable(id, wasmData); } - - return { proofToRust([, public_evals, proof]: ProofWithPublic): NapiProverProof { diff --git a/src/bindings/crypto/napi-conversion-verifier-index.ts b/src/bindings/crypto/napi-conversion-verifier-index.ts new file mode 100644 index 0000000000..23705bb808 --- /dev/null +++ b/src/bindings/crypto/napi-conversion-verifier-index.ts @@ -0,0 +1,284 @@ +import { MlArray, MlBool, MlOption } from '../../lib/ml/base.js'; +import type * as wasmNamespace from '../compiled/node_bindings/plonk_wasm.cjs'; +import type { + WasmFpDomain, + WasmFpLookupSelectors, + WasmFpLookupVerifierIndex, + WasmFpPlonkVerificationEvals, + WasmFpPlonkVerifierIndex, + WasmFpShifts, + WasmFqDomain, + WasmFqLookupSelectors, + WasmFqLookupVerifierIndex, + WasmFqPlonkVerificationEvals, + WasmFqPlonkVerifierIndex, + WasmFqShifts, + LookupInfo as WasmLookupInfo, +} from '../compiled/node_bindings/plonk_wasm.cjs'; +import { fieldFromRust, fieldToRust } from './bindings/conversion-base.js'; +import { ConversionCore, ConversionCores } from './napi-conversion-core.js'; +import { Domain, Field, PolyComm, VerificationEvals, VerifierIndex } from './bindings/kimchi-types.js'; +import { Lookup, LookupInfo, LookupSelectors } from './bindings/lookup.js'; + +export { napiVerifierIndexConversion }; + +type wasm = typeof wasmNamespace; + +type WasmDomain = WasmFpDomain | WasmFqDomain; +type WasmVerificationEvals = WasmFpPlonkVerificationEvals | WasmFqPlonkVerificationEvals; +type WasmShifts = WasmFpShifts | WasmFqShifts; +type WasmVerifierIndex = WasmFpPlonkVerifierIndex | WasmFqPlonkVerifierIndex; + +type WasmLookupVerifierIndex = WasmFpLookupVerifierIndex | WasmFqLookupVerifierIndex; +type WasmLookupSelector = WasmFpLookupSelectors | WasmFqLookupSelectors; + +type WasmClasses = { + Domain: typeof WasmFpDomain | typeof WasmFqDomain; + VerificationEvals: typeof WasmFpPlonkVerificationEvals | typeof WasmFqPlonkVerificationEvals; + Shifts: typeof WasmFpShifts | typeof WasmFqShifts; + VerifierIndex: typeof WasmFpPlonkVerifierIndex | typeof WasmFqPlonkVerifierIndex; + LookupVerifierIndex: typeof WasmFpLookupVerifierIndex | typeof WasmFqLookupVerifierIndex; + LookupSelector: typeof WasmFpLookupSelectors | typeof WasmFqLookupSelectors; +}; + +function napiVerifierIndexConversion(napi: any, core: ConversionCores) { + return { + fp: verifierIndexConversionPerField(napi, core.fp, { + Domain: napi.WasmFpDomain, + VerificationEvals: napi.WasmFpPlonkVerificationEvals, + Shifts: napi.WasmFpShifts, + VerifierIndex: napi.WasmFpPlonkVerifierIndex, + LookupVerifierIndex: napi.WasmFpLookupVerifierIndex, + LookupSelector: napi.WasmFpLookupSelectors, + }), + fq: verifierIndexConversionPerField(napi, core.fq, { + Domain: napi.WasmFqDomain, + VerificationEvals: napi.WasmFqPlonkVerificationEvals, + Shifts: napi.WasmFqShifts, + VerifierIndex: napi.WasmFqPlonkVerifierIndex, + LookupVerifierIndex: napi.WasmFqLookupVerifierIndex, + LookupSelector: napi.WasmFqLookupSelectors, + }), + }; +} + +function verifierIndexConversionPerField( + wasm: wasm, + core: ConversionCore, + { + Domain, + VerificationEvals, + Shifts, + VerifierIndex, + LookupVerifierIndex, + LookupSelector, + }: WasmClasses +) { + function domainToRust([, logSizeOfGroup, groupGen]: Domain): WasmDomain { + return new Domain(logSizeOfGroup, fieldToRust(groupGen)); + } + function domainFromRust(domain: WasmDomain): Domain { + let logSizeOfGroup = domain.log_size_of_group; + let groupGen = fieldFromRust(domain.group_gen); + return [0, logSizeOfGroup, groupGen]; + } + + function verificationEvalsToRust(evals: VerificationEvals): WasmVerificationEvals { + let sigmaComm = core.polyCommsToRust(evals[1]); + let coefficientsComm = core.polyCommsToRust(evals[2]); + let genericComm = core.polyCommToRust(evals[3]); + let psmComm = core.polyCommToRust(evals[4]); + let completeAddComm = core.polyCommToRust(evals[5]); + let mulComm = core.polyCommToRust(evals[6]); + let emulComm = core.polyCommToRust(evals[7]); + let endomulScalarComm = core.polyCommToRust(evals[8]); + let xorComm = MlOption.mapFrom(evals[9], core.polyCommToRust); + let rangeCheck0Comm = MlOption.mapFrom(evals[10], core.polyCommToRust); + let rangeCheck1Comm = MlOption.mapFrom(evals[11], core.polyCommToRust); + let foreignFieldAddComm = MlOption.mapFrom(evals[12], core.polyCommToRust); + let foreignFieldMulComm = MlOption.mapFrom(evals[13], core.polyCommToRust); + let rotComm = MlOption.mapFrom(evals[14], core.polyCommToRust); + return new VerificationEvals( + sigmaComm as any, + coefficientsComm as any, + genericComm as any, + psmComm as any, + completeAddComm as any, + mulComm as any, + emulComm as any, + endomulScalarComm as any, + xorComm as any, + rangeCheck0Comm as any, + rangeCheck1Comm as any, + foreignFieldAddComm as any, + foreignFieldMulComm as any, + rotComm as any + ); + } + function verificationEvalsFromRust(evals: WasmVerificationEvals): VerificationEvals { + console.log('evals', evals.coefficients_comm); + + let mlEvals: VerificationEvals = [ + 0, + core.polyCommsFromRust(evals.sigma_comm), + core.polyCommsFromRust(evals.coefficients_comm), + core.polyCommFromRust(evals.generic_comm), + core.polyCommFromRust(evals.psm_comm), + core.polyCommFromRust(evals.complete_add_comm), + core.polyCommFromRust(evals.mul_comm), + core.polyCommFromRust(evals.emul_comm), + core.polyCommFromRust(evals.endomul_scalar_comm), + MlOption.mapTo(evals.xor_comm, core.polyCommFromRust), + MlOption.mapTo(evals.range_check0_comm, core.polyCommFromRust), + MlOption.mapTo(evals.range_check1_comm, core.polyCommFromRust), + MlOption.mapTo(evals.foreign_field_add_comm, core.polyCommFromRust), + MlOption.mapTo(evals.foreign_field_mul_comm, core.polyCommFromRust), + MlOption.mapTo(evals.rot_comm, core.polyCommFromRust), + ]; + return mlEvals; + } + + function lookupVerifierIndexToRust(lookup: Lookup): WasmLookupVerifierIndex { + let [ + , + joint_lookup_used, + lookup_table, + selectors, + table_ids, + lookup_info, + runtime_tables_selector, + ] = lookup; + return new LookupVerifierIndex( + MlBool.from(joint_lookup_used), + core.polyCommsToRust(lookup_table) as any, + lookupSelectorsToRust(selectors), + MlOption.mapFrom(table_ids, core.polyCommToRust) as any, + lookupInfoToRust(lookup_info), + MlOption.mapFrom(runtime_tables_selector, core.polyCommToRust) as any + ); + } + function lookupVerifierIndexFromRust(lookup: WasmLookupVerifierIndex): Lookup { + let mlLookup: Lookup = [ + 0, + MlBool(lookup.joint_lookup_used), + core.polyCommsFromRust(lookup.lookup_table), + lookupSelectorsFromRust(lookup.lookup_selectors), + MlOption.mapTo(lookup.table_ids, core.polyCommFromRust), + lookupInfoFromRust(lookup.lookup_info), + MlOption.mapTo(lookup.runtime_tables_selector, core.polyCommFromRust), + ]; + return mlLookup; + } + + function lookupSelectorsToRust([ + , + lookup, + xor, + range_check, + ffmul, + ]: LookupSelectors): WasmLookupSelector { + return new LookupSelector( + MlOption.mapFrom(xor, core.polyCommToRust) as any, + MlOption.mapFrom(lookup, core.polyCommToRust) as any, + MlOption.mapFrom(range_check, core.polyCommToRust) as any, + MlOption.mapFrom(ffmul, core.polyCommToRust) as any + ); + } + function lookupSelectorsFromRust(selector: WasmLookupSelector): LookupSelectors { + let lookup = MlOption.mapTo(selector.lookup, core.polyCommFromRust); + let xor = MlOption.mapTo(selector.xor, core.polyCommFromRust); + let range_check = MlOption.mapTo(selector.range_check, core.polyCommFromRust); + let ffmul = MlOption.mapTo(selector.ffmul, core.polyCommFromRust); + return [0, lookup, xor, range_check, ffmul]; + } + + function lookupInfoToRust([, maxPerRow, maxJointSize, features]: LookupInfo): WasmLookupInfo { + let [, patterns, joint_lookup_used, uses_runtime_tables] = features; + let [, xor, lookup, range_check, foreign_field_mul] = patterns; + let wasmPatterns = new wasm.LookupPatterns( + MlBool.from(xor), + MlBool.from(lookup), + MlBool.from(range_check), + MlBool.from(foreign_field_mul) + ); + let wasmFeatures = new wasm.LookupFeatures( + wasmPatterns, + MlBool.from(joint_lookup_used), + MlBool.from(uses_runtime_tables) + ); + return new wasm.LookupInfo(maxPerRow, maxJointSize, wasmFeatures); + } + function lookupInfoFromRust(info: WasmLookupInfo): LookupInfo { + let features = info.features; + let patterns = features.patterns; + let mlInfo: LookupInfo = [ + 0, + info.max_per_row, + info.max_joint_size, + [ + 0, + [ + 0, + MlBool(patterns.xor), + MlBool(patterns.lookup), + MlBool(patterns.range_check), + MlBool(patterns.foreign_field_mul), + ], + MlBool(features.joint_lookup_used), + MlBool(features.uses_runtime_tables), + ], + ]; + return mlInfo; + } + + let self = { + shiftsToRust([, ...shifts]: MlArray): WasmShifts { + let s = shifts.map((s) => fieldToRust(s)); + return new Shifts(s[0], s[1], s[2], s[3], s[4], s[5], s[6]); + }, + shiftsFromRust(s: WasmShifts): MlArray { + let shifts = [s.s0, s.s1, s.s2, s.s3, s.s4, s.s5, s.s6]; + return [0, ...shifts.map(fieldFromRust)]; + }, + + verifierIndexToRust(vk: VerifierIndex): WasmVerifierIndex { + let domain = domainToRust(vk[1]); + let maxPolySize = vk[2]; + let nPublic = vk[3]; + let prevChallenges = vk[4]; + let srs = vk[5]; + let evals = verificationEvalsToRust(vk[6]); + let shifts = self.shiftsToRust(vk[7]); + let lookupIndex = MlOption.mapFrom(vk[8], lookupVerifierIndexToRust); + let zkRows = vk[9]; + return new VerifierIndex( + domain, + maxPolySize, + nPublic, + prevChallenges, + srs, + evals, + shifts, + lookupIndex, + zkRows + ); + }, + verifierIndexFromRust(vk: WasmVerifierIndex): VerifierIndex { + let mlVk: VerifierIndex = [ + 0, + domainFromRust(vk.domain), + vk.max_poly_size, + vk.public_, + vk.prev_challenges, + vk.srs, + verificationEvalsFromRust(vk.evals), + self.shiftsFromRust(vk.shifts), + MlOption.mapTo(vk.lookup_index, lookupVerifierIndexFromRust), + vk.zk_rows, + ]; + return mlVk; + }, + }; + + return self; +} From e25896e195be4fe9b72a817f6f5d2b83da91122e Mon Sep 17 00:00:00 2001 From: querolita Date: Wed, 26 Nov 2025 17:12:59 +0100 Subject: [PATCH 13/23] submodule: update mina --- src/mina | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/mina b/src/mina index 3116ffcdb3..8437493a87 160000 --- a/src/mina +++ b/src/mina @@ -1 +1 @@ -Subproject commit 3116ffcdb35038a04624112305d9ab618990c7d2 +Subproject commit 8437493a87dcc0db0065a19b8d74d34c567b4c53 From 550855a547184e227fe78898461766731ae95169 Mon Sep 17 00:00:00 2001 From: querolita Date: Wed, 3 Dec 2025 20:09:41 +0100 Subject: [PATCH 14/23] bindings: use native conversion for prover keys --- src/bindings/crypto/bindings.ts | 2 +- src/lib/proof-system/prover-keys.ts | 11 ++++++----- 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/src/bindings/crypto/bindings.ts b/src/bindings/crypto/bindings.ts index 645abf9b34..8e1b7923d5 100644 --- a/src/bindings/crypto/bindings.ts +++ b/src/bindings/crypto/bindings.ts @@ -14,12 +14,12 @@ import { verifierIndexConversion } from './bindings/conversion-verifier-index.js import { PallasBindings, VestaBindings } from './bindings/curve.js'; import { jsEnvironment } from './bindings/env.js'; import { FpBindings, FqBindings } from './bindings/field.js'; -import { napiOraclesConversion } from './bindings/napi-conversion-oracles.js'; import { srs } from './bindings/srs.js'; import { srs as napiSrs } from './napi-srs.js'; import { FpVectorBindings, FqVectorBindings } from './bindings/vector.js'; import { napiConversionCore } from './napi-conversion-core.js'; import { napiProofConversion } from './napi-conversion-proof.js'; +import { napiOraclesConversion } from './bindings/napi-conversion-oracles.js'; import { napiVerifierIndexConversion } from './napi-conversion-verifier-index.js'; import type * as napiNamespace from '../compiled/node_bindings/plonk_wasm.cjs'; diff --git a/src/lib/proof-system/prover-keys.ts b/src/lib/proof-system/prover-keys.ts index e9fd8de7d2..69055df1c0 100644 --- a/src/lib/proof-system/prover-keys.ts +++ b/src/lib/proof-system/prover-keys.ts @@ -10,7 +10,8 @@ import { WasmPastaFpPlonkIndex, WasmPastaFqPlonkIndex, } from '../../bindings/compiled/node_bindings/plonk_wasm.cjs'; -import { getRustConversion } from '../../bindings/crypto/bindings.js'; +// TODO: include conversion bundle to decide between wasm and napi conversion +import { createNativeRustConversion, getRustConversion } from '../../bindings/crypto/bindings.js'; import { VerifierIndex } from '../../bindings/crypto/bindings/kimchi-types.js'; import { MlString } from '../ml/base.js'; import { CacheHeader, cacheHeaderVersion } from './cache.js'; @@ -95,13 +96,13 @@ function encodeProverKey(value: SnarkKey): Uint8Array { case KeyType.StepProvingKey: { let index = value[1][1]; let encoded = wasm.caml_pasta_fp_plonk_index_encode( - (wasm as any).prover_index_fp_from_bytes(index.serialize()) + (wasm as any).prover_index_fp_deserialize((wasm as any).prover_index_fp_serialize(index)) ); return encoded; } case KeyType.StepVerificationKey: { let vkMl = value[1]; - const rustConversion = getRustConversion(wasm); + const rustConversion = createNativeRustConversion(wasm); let vkWasm = rustConversion.fp.verifierIndexToRust(vkMl); let string = wasm.caml_pasta_fp_plonk_verifier_index_serialize(vkWasm); return new TextEncoder().encode(string); @@ -109,7 +110,7 @@ function encodeProverKey(value: SnarkKey): Uint8Array { case KeyType.WrapProvingKey: { let index = value[1][1]; let encoded = wasm.caml_pasta_fq_plonk_index_encode( - (wasm as any).prover_index_fq_from_bytes(index.serialize()) + (wasm as any).prover_index_fq_deserialize((wasm as any).prover_index_fq_serialize(index)) ); return encoded; } @@ -139,7 +140,7 @@ function decodeProverKey(header: SnarkKeyHeader, bytes: Uint8Array): SnarkKey { let srs = Pickles.loadSrsFp(); let string = new TextDecoder().decode(bytes); let vkWasm = wasm.caml_pasta_fp_plonk_verifier_index_deserialize(srs, string); - const rustConversion = getRustConversion(wasm); + const rustConversion = createNativeRustConversion(wasm); let vkMl = rustConversion.fp.verifierIndexFromRust(vkWasm); return [KeyType.StepVerificationKey, vkMl]; } From 199e872affe06f11da37847baad69b9ee82e9a80 Mon Sep 17 00:00:00 2001 From: querolita Date: Wed, 3 Dec 2025 20:11:49 +0100 Subject: [PATCH 15/23] bindings: logs for srs functions for debugging --- src/bindings/crypto/bindings/srs.ts | 2 +- src/bindings/crypto/napi-srs.ts | 17 +++++++++++++++-- 2 files changed, 16 insertions(+), 3 deletions(-) diff --git a/src/bindings/crypto/bindings/srs.ts b/src/bindings/crypto/bindings/srs.ts index cc3d440bac..3c8e9fef4b 100644 --- a/src/bindings/crypto/bindings/srs.ts +++ b/src/bindings/crypto/bindings/srs.ts @@ -104,7 +104,7 @@ function srsPerField(f: 'fp' | 'fq', wasm: Wasm, conversion: RustConversion) { let maybeLagrangeCommitment = (srs: WasmSrs, domain_size: number, i: number) => { try { console.log(3); - console.log('srs', srs); + console.log('srs wasm', srs); let bytes = (wasm as any)[`caml_${f}_srs_to_bytes_external`](srs); console.log('bytes', bytes); let wasmSrs = undefined; diff --git a/src/bindings/crypto/napi-srs.ts b/src/bindings/crypto/napi-srs.ts index fe61e75b4b..c12047c45a 100644 --- a/src/bindings/crypto/napi-srs.ts +++ b/src/bindings/crypto/napi-srs.ts @@ -103,7 +103,18 @@ function srsPerField(f: 'fp' | 'fq', napi: Napi, conversion: RustConversion) { let maybeLagrangeCommitment = (srs: NapiSrs, domain_size: number, i: number) => { try { - return napi[`caml_${f}_srs_maybe_lagrange_commitment`](srs, domain_size, i); + console.log(3); + console.log('srs napi', srs); + + /*let bytes = (napi as any)[`caml_${f}_srs_to_bytes`](srs); + console.log('bytes', bytes); + let wasmSrs = undefined; + if (f === 'fp') wasmSrs = (napi as any)[`caml_${f}_srs_from_bytes`](bytes); + else wasmSrs = (napi as any)[`caml_fq_srs_from_bytes`](bytes); + */ + let s = napi[`caml_${f}_srs_maybe_lagrange_commitment`](srs, domain_size, i); + console.log('S', s); + return s; } catch (error) { console.error(`Error in SRS maybe lagrange commitment for field ${f}`); throw error; @@ -197,7 +208,8 @@ function srsPerField(f: 'fp' | 'fq', napi: Napi, conversion: RustConversion) { // happy, fast case: if basis is already stored on the srs, return the ith commitment let commitment = maybeLagrangeCommitment(srs, domainSize, i); - if (commitment === undefined) { + if (commitment === undefined || commitment === null) { + console.log("comm was undefined"); if (cache === undefined) { // if there is no cache, recompute and store basis in memory commitment = lagrangeCommitment(srs, domainSize, i); @@ -235,6 +247,7 @@ function srsPerField(f: 'fp' | 'fq', napi: Napi, conversion: RustConversion) { commitment = c; } } + console.log("commitment was not undefined"); // edge case for when we have a writeable cache and the basis was already stored on the srs // but we didn't store it in the cache separately yet From a987a9c2ef3ab8c2d83ce05b576daeb6d477bbf5 Mon Sep 17 00:00:00 2001 From: querolita Date: Wed, 3 Dec 2025 20:12:10 +0100 Subject: [PATCH 16/23] bindings: update polyCommFromRust --- src/bindings/crypto/napi-conversion-core.ts | 13 ++----------- 1 file changed, 2 insertions(+), 11 deletions(-) diff --git a/src/bindings/crypto/napi-conversion-core.ts b/src/bindings/crypto/napi-conversion-core.ts index 5c1a403ce6..3c849d7f00 100644 --- a/src/bindings/crypto/napi-conversion-core.ts +++ b/src/bindings/crypto/napi-conversion-core.ts @@ -180,22 +180,13 @@ function conversionCorePerField({ makeAffine, PolyComm }: NapiClasses) { return new PolyCommClass(unshifted as unknown, undefined); }; - /* const polyCommFromRust = (polyComm: NapiPolyComm): PolyComm => { + const polyCommFromRust = (polyComm: any): any => { + if (polyComm == null) return undefined; console.log('polyComm', polyComm); const rustUnshifted = asArrayLike(polyComm.unshifted, 'polyComm.unshifted'); console.log('rustUnshifted', rustUnshifted); const mlUnshifted = rustUnshifted.map(affineFromRust); return [0, [0, ...mlUnshifted]]; - }; */ - const polyCommFromRust = (polyComm: any): any => { - let rustUnshifted = polyComm.unshifted; - console.log('rustUnshifted', rustUnshifted); - let mlUnshifted = mapFromUintArray(rustUnshifted, (ptr) => { - console.log('ptr', ptr); - /* return affineFromRust(wrap(ptr, CommitmentCurve)); - */ - }); - return [0, [0, ...mlUnshifted]]; }; const polyCommsToRust = ([, ...comms]: MlArray): NapiPolyComm[] => From aeefc4d74d43fac0bb31316a39c3544c98fbfe74 Mon Sep 17 00:00:00 2001 From: querolita Date: Wed, 3 Dec 2025 20:13:04 +0100 Subject: [PATCH 17/23] bindings: refactor verifier index --- .../crypto/napi-conversion-verifier-index.ts | 62 +++++++++---------- 1 file changed, 29 insertions(+), 33 deletions(-) diff --git a/src/bindings/crypto/napi-conversion-verifier-index.ts b/src/bindings/crypto/napi-conversion-verifier-index.ts index 23705bb808..60c5cf4a5c 100644 --- a/src/bindings/crypto/napi-conversion-verifier-index.ts +++ b/src/bindings/crypto/napi-conversion-verifier-index.ts @@ -1,5 +1,5 @@ import { MlArray, MlBool, MlOption } from '../../lib/ml/base.js'; -import type * as wasmNamespace from '../compiled/node_bindings/plonk_wasm.cjs'; +import type * as napiNamespace from '../compiled/node_bindings/plonk_wasm.cjs'; import type { WasmFpDomain, WasmFpLookupSelectors, @@ -22,17 +22,16 @@ import { Lookup, LookupInfo, LookupSelectors } from './bindings/lookup.js'; export { napiVerifierIndexConversion }; -type wasm = typeof wasmNamespace; +type napi = typeof napiNamespace; -type WasmDomain = WasmFpDomain | WasmFqDomain; -type WasmVerificationEvals = WasmFpPlonkVerificationEvals | WasmFqPlonkVerificationEvals; -type WasmShifts = WasmFpShifts | WasmFqShifts; -type WasmVerifierIndex = WasmFpPlonkVerifierIndex | WasmFqPlonkVerifierIndex; +type NapiDomain = WasmFpDomain | WasmFqDomain; +type NapiVerificationEvals = WasmFpPlonkVerificationEvals | WasmFqPlonkVerificationEvals; +type NapiShifts = WasmFpShifts | WasmFqShifts; +type NapiVerifierIndex = WasmFpPlonkVerifierIndex | WasmFqPlonkVerifierIndex; +type NapiLookupVerifierIndex = WasmFpLookupVerifierIndex | WasmFqLookupVerifierIndex; +type NapiLookupSelector = WasmFpLookupSelectors | WasmFqLookupSelectors; -type WasmLookupVerifierIndex = WasmFpLookupVerifierIndex | WasmFqLookupVerifierIndex; -type WasmLookupSelector = WasmFpLookupSelectors | WasmFqLookupSelectors; - -type WasmClasses = { +type NapiClasses = { Domain: typeof WasmFpDomain | typeof WasmFqDomain; VerificationEvals: typeof WasmFpPlonkVerificationEvals | typeof WasmFqPlonkVerificationEvals; Shifts: typeof WasmFpShifts | typeof WasmFqShifts; @@ -63,7 +62,7 @@ function napiVerifierIndexConversion(napi: any, core: ConversionCores) { } function verifierIndexConversionPerField( - wasm: wasm, + napi: any, core: ConversionCore, { Domain, @@ -72,18 +71,16 @@ function verifierIndexConversionPerField( VerifierIndex, LookupVerifierIndex, LookupSelector, - }: WasmClasses + }: NapiClasses ) { - function domainToRust([, logSizeOfGroup, groupGen]: Domain): WasmDomain { + function domainToRust([, logSizeOfGroup, groupGen]: Domain): NapiDomain { return new Domain(logSizeOfGroup, fieldToRust(groupGen)); } - function domainFromRust(domain: WasmDomain): Domain { - let logSizeOfGroup = domain.log_size_of_group; - let groupGen = fieldFromRust(domain.group_gen); - return [0, logSizeOfGroup, groupGen]; + function domainFromRust(domain: NapiDomain): Domain { + return [0, domain.log_size_of_group, fieldFromRust(domain.group_gen)]; } - function verificationEvalsToRust(evals: VerificationEvals): WasmVerificationEvals { + function verificationEvalsToRust(evals: VerificationEvals): NapiVerificationEvals { let sigmaComm = core.polyCommsToRust(evals[1]); let coefficientsComm = core.polyCommsToRust(evals[2]); let genericComm = core.polyCommToRust(evals[3]); @@ -115,9 +112,7 @@ function verifierIndexConversionPerField( rotComm as any ); } - function verificationEvalsFromRust(evals: WasmVerificationEvals): VerificationEvals { - console.log('evals', evals.coefficients_comm); - + function verificationEvalsFromRust(evals: NapiVerificationEvals): VerificationEvals { let mlEvals: VerificationEvals = [ 0, core.polyCommsFromRust(evals.sigma_comm), @@ -138,7 +133,7 @@ function verifierIndexConversionPerField( return mlEvals; } - function lookupVerifierIndexToRust(lookup: Lookup): WasmLookupVerifierIndex { + function lookupVerifierIndexToRust(lookup: Lookup): NapiLookupVerifierIndex { let [ , joint_lookup_used, @@ -157,7 +152,7 @@ function verifierIndexConversionPerField( MlOption.mapFrom(runtime_tables_selector, core.polyCommToRust) as any ); } - function lookupVerifierIndexFromRust(lookup: WasmLookupVerifierIndex): Lookup { + function lookupVerifierIndexFromRust(lookup: NapiLookupVerifierIndex): Lookup { let mlLookup: Lookup = [ 0, MlBool(lookup.joint_lookup_used), @@ -176,7 +171,7 @@ function verifierIndexConversionPerField( xor, range_check, ffmul, - ]: LookupSelectors): WasmLookupSelector { + ]: LookupSelectors): NapiLookupSelector { return new LookupSelector( MlOption.mapFrom(xor, core.polyCommToRust) as any, MlOption.mapFrom(lookup, core.polyCommToRust) as any, @@ -184,7 +179,7 @@ function verifierIndexConversionPerField( MlOption.mapFrom(ffmul, core.polyCommToRust) as any ); } - function lookupSelectorsFromRust(selector: WasmLookupSelector): LookupSelectors { + function lookupSelectorsFromRust(selector: NapiLookupSelector): LookupSelectors { let lookup = MlOption.mapTo(selector.lookup, core.polyCommFromRust); let xor = MlOption.mapTo(selector.xor, core.polyCommFromRust); let range_check = MlOption.mapTo(selector.range_check, core.polyCommFromRust); @@ -195,18 +190,18 @@ function verifierIndexConversionPerField( function lookupInfoToRust([, maxPerRow, maxJointSize, features]: LookupInfo): WasmLookupInfo { let [, patterns, joint_lookup_used, uses_runtime_tables] = features; let [, xor, lookup, range_check, foreign_field_mul] = patterns; - let wasmPatterns = new wasm.LookupPatterns( + let napiPatterns = new napi.LookupPatterns( MlBool.from(xor), MlBool.from(lookup), MlBool.from(range_check), MlBool.from(foreign_field_mul) ); - let wasmFeatures = new wasm.LookupFeatures( - wasmPatterns, + let napiFeatures = new napi.LookupFeatures( + napiPatterns, MlBool.from(joint_lookup_used), MlBool.from(uses_runtime_tables) ); - return new wasm.LookupInfo(maxPerRow, maxJointSize, wasmFeatures); + return new napi.LookupInfo(maxPerRow, maxJointSize, napiFeatures); } function lookupInfoFromRust(info: WasmLookupInfo): LookupInfo { let features = info.features; @@ -232,16 +227,16 @@ function verifierIndexConversionPerField( } let self = { - shiftsToRust([, ...shifts]: MlArray): WasmShifts { + shiftsToRust([, ...shifts]: MlArray): NapiShifts { let s = shifts.map((s) => fieldToRust(s)); return new Shifts(s[0], s[1], s[2], s[3], s[4], s[5], s[6]); }, - shiftsFromRust(s: WasmShifts): MlArray { + shiftsFromRust(s: NapiShifts): MlArray { let shifts = [s.s0, s.s1, s.s2, s.s3, s.s4, s.s5, s.s6]; return [0, ...shifts.map(fieldFromRust)]; }, - verifierIndexToRust(vk: VerifierIndex): WasmVerifierIndex { + verifierIndexToRust(vk: VerifierIndex): NapiVerifierIndex { let domain = domainToRust(vk[1]); let maxPolySize = vk[2]; let nPublic = vk[3]; @@ -263,7 +258,8 @@ function verifierIndexConversionPerField( zkRows ); }, - verifierIndexFromRust(vk: WasmVerifierIndex): VerifierIndex { + verifierIndexFromRust(vk: NapiVerifierIndex): VerifierIndex { + console.log('vk from rust', vk); let mlVk: VerifierIndex = [ 0, domainFromRust(vk.domain), From c8cbc548b12feaadc80ec4bd2ef1f2e36fa88ad5 Mon Sep 17 00:00:00 2001 From: querolita Date: Wed, 3 Dec 2025 20:13:14 +0100 Subject: [PATCH 18/23] submodule: update mina --- src/mina | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/mina b/src/mina index 8437493a87..6f15cb8b8e 160000 --- a/src/mina +++ b/src/mina @@ -1 +1 @@ -Subproject commit 8437493a87dcc0db0065a19b8d74d34c567b4c53 +Subproject commit 6f15cb8b8ef76a1c7c1d57d04da02836a0d6f36e From fb1fa6db01e931e47b953f08feb559db150e4ade Mon Sep 17 00:00:00 2001 From: Shigoto-dev19 Date: Sun, 7 Dec 2025 21:51:52 -0300 Subject: [PATCH 19/23] shigoto + anais kimchi bindings changes for the first native prover poc --- src/bindings/crypto/bindings.ts | 12 +- .../crypto/bindings/conversion-base.ts | 20 +- .../crypto/bindings/conversion-core.ts | 14 +- .../crypto/bindings/conversion-proof.ts | 37 +-- .../bindings/conversion-verifier-index.ts | 4 +- src/bindings/crypto/napi-conversion-core.ts | 32 +- src/bindings/crypto/napi-conversion-proof.ts | 68 ++--- .../crypto/napi-conversion-verifier-index.ts | 287 ++++++++++++++++++ src/bindings/crypto/napi-srs.ts | 26 +- src/lib/proof-system/prover-keys.ts | 11 +- 10 files changed, 402 insertions(+), 109 deletions(-) create mode 100644 src/bindings/crypto/napi-conversion-verifier-index.ts diff --git a/src/bindings/crypto/bindings.ts b/src/bindings/crypto/bindings.ts index f3d4817c29..b43d15f1dd 100644 --- a/src/bindings/crypto/bindings.ts +++ b/src/bindings/crypto/bindings.ts @@ -3,6 +3,7 @@ * It is exposed to JSOO by populating a global variable with an object. * It gets imported as the first thing in ../../bindings.js so that the global variable is ready by the time JSOO code gets executed. */ +import type * as napiNamespace from '../compiled/node_bindings/plonk_wasm.cjs'; import type * as wasmNamespace from '../compiled/node_bindings/plonk_wasm.cjs'; import { prefixHashes, prefixHashesLegacy } from '../crypto/constants.js'; import { Bigint256Bindings } from './bindings/bigint256.js'; @@ -16,13 +17,13 @@ import { jsEnvironment } from './bindings/env.js'; import { FpBindings, FqBindings } from './bindings/field.js'; import { napiOraclesConversion } from './bindings/napi-conversion-oracles.js'; import { srs } from './bindings/srs.js'; -import { srs as napiSrs } from './napi-srs.js'; import { FpVectorBindings, FqVectorBindings } from './bindings/vector.js'; import { napiConversionCore } from './napi-conversion-core.js'; import { napiProofConversion } from './napi-conversion-proof.js'; -import type * as napiNamespace from '../compiled/node_bindings/plonk_wasm.cjs'; +import { napiVerifierIndexConversion } from './napi-conversion-verifier-index.js'; +import { srs as napiSrs } from './napi-srs.js'; -export { RustConversion, Wasm, Napi, createNativeRustConversion, getRustConversion }; +export { Napi, RustConversion, Wasm, createNativeRustConversion, getRustConversion }; /* TODO: Uncomment in phase 2 of conversion layer import { conversionCore as conversionCoreNative } from './native/conversion-core.js'; @@ -90,10 +91,11 @@ function buildWasmConversion(wasm: Wasm) { function createNativeRustConversion(napi: any) { let core = napiConversionCore(napi); let proof = napiProofConversion(napi, core); + let verif = napiVerifierIndexConversion(napi, core); let oracles = napiOraclesConversion(napi); return { - fp: { ...core.fp, ...proof.fp, ...oracles.fp }, - fq: { ...core.fq, ...proof.fq, ...oracles.fq }, + fp: { ...core.fp, ...proof.fp, ...verif.fp, ...oracles.fp }, + fq: { ...core.fq, ...proof.fq, ...verif.fq, ...oracles.fq }, }; } diff --git a/src/bindings/crypto/bindings/conversion-base.ts b/src/bindings/crypto/bindings/conversion-base.ts index 9f67829df3..5b8cdd601d 100644 --- a/src/bindings/crypto/bindings/conversion-base.ts +++ b/src/bindings/crypto/bindings/conversion-base.ts @@ -1,24 +1,24 @@ -import { Field } from './field.js'; -import { bigintToBytes32, bytesToBigint32 } from '../bigint-helpers.js'; +import type { MlArray } from '../../../lib/ml/base.js'; import type { WasmGPallas, WasmGVesta, WasmPallasGProjective, WasmVestaGProjective, } from '../../compiled/node_bindings/plonk_wasm.cjs'; -import type { MlArray } from '../../../lib/ml/base.js'; -import { OrInfinity, Infinity } from './curve.js'; +import { bigintToBytes32, bytesToBigint32 } from '../bigint-helpers.js'; +import { Infinity, OrInfinity } from './curve.js'; +import { Field } from './field.js'; export { - fieldToRust, + WasmAffine, + WasmProjective, + affineFromRust, + affineToRust, fieldFromRust, - fieldsToRustFlat, + fieldToRust, fieldsFromRustFlat, + fieldsToRustFlat, maybeFieldToRust, - affineToRust, - affineFromRust, - WasmAffine, - WasmProjective, }; // TODO: Hardcoding this is a little brittle diff --git a/src/bindings/crypto/bindings/conversion-core.ts b/src/bindings/crypto/bindings/conversion-core.ts index bae7c79d62..644504ec36 100644 --- a/src/bindings/crypto/bindings/conversion-core.ts +++ b/src/bindings/crypto/bindings/conversion-core.ts @@ -15,7 +15,7 @@ import { fieldsFromRustFlat, fieldsToRustFlat, } from './conversion-base.js'; -import { Gate, OrInfinity, PolyComm, Wire } from './kimchi-types.js'; +import { Gate, OrInfinity, Wire } from './kimchi-types.js'; import { mapTuple } from './util.js'; export { @@ -109,26 +109,26 @@ function conversionCorePerField( return [0, ...arr]; }, - polyCommToRust(polyComm: PolyComm): WasmPolyComm { + polyCommToRust(polyComm: any): WasmPolyComm { let [, camlElems] = polyComm; let rustShifted = undefined; let rustUnshifted = self.pointsToRust(camlElems); return new PolyComm(rustUnshifted, rustShifted); }, - polyCommFromRust(polyComm: WasmPolyComm): PolyComm { - console.log('polyComm', polyComm); + polyCommFromRust(polyComm: WasmPolyComm): any { + console.log('polyComm old', polyComm); let rustUnshifted = polyComm.unshifted; - console.log('polyCommFromRust', rustUnshifted); + console.log('rustUnshifted', rustUnshifted); let mlUnshifted = mapFromUintArray(rustUnshifted, (ptr) => { return affineFromRust(wrap(ptr, CommitmentCurve)); }); return [0, [0, ...mlUnshifted]]; }, - polyCommsToRust([, ...comms]: MlArray): Uint32Array { + polyCommsToRust([, ...comms]: MlArray): Uint32Array { return mapToUint32Array(comms, (c) => unwrap(self.polyCommToRust(c))); }, - polyCommsFromRust(rustComms: Uint32Array): MlArray { + polyCommsFromRust(rustComms: Uint32Array): MlArray { let comms = mapFromUintArray(rustComms, (ptr) => self.polyCommFromRust(wrap(ptr, PolyComm))); return [0, ...comms]; }, diff --git a/src/bindings/crypto/bindings/conversion-proof.ts b/src/bindings/crypto/bindings/conversion-proof.ts index ee106fb940..5fdc45e6ce 100644 --- a/src/bindings/crypto/bindings/conversion-proof.ts +++ b/src/bindings/crypto/bindings/conversion-proof.ts @@ -1,46 +1,46 @@ +import { MlArray, MlOption, MlTuple } from '../../../lib/ml/base.js'; +import type * as wasmNamespace from '../../compiled/node_bindings/plonk_wasm.cjs'; import type { WasmFpLookupCommitments, - WasmPastaFpLookupTable, WasmFpOpeningProof, WasmFpProverCommitments, WasmFpProverProof, WasmFpRuntimeTable, - WasmPastaFpRuntimeTableCfg, WasmFqLookupCommitments, WasmFqOpeningProof, WasmFqProverCommitments, - WasmPastaFqLookupTable, WasmFqProverProof, WasmFqRuntimeTable, + WasmPastaFpLookupTable, + WasmPastaFpRuntimeTableCfg, + WasmPastaFqLookupTable, WasmPastaFqRuntimeTableCfg, WasmVecVecFp, WasmVecVecFq, } from '../../compiled/node_bindings/plonk_wasm.cjs'; -import type * as wasmNamespace from '../../compiled/node_bindings/plonk_wasm.cjs'; +import { + fieldFromRust, + fieldToRust, + fieldsFromRustFlat, + fieldsToRustFlat, +} from './conversion-base.js'; +import { ConversionCore, ConversionCores, mapToUint32Array, unwrap } from './conversion-core.js'; import type { + Field, + LookupCommitments, + LookupTable, + OpeningProof, OrInfinity, PointEvaluations, PolyComm, - ProverProof, - ProofWithPublic, ProofEvaluations, + ProofWithPublic, ProverCommitments, - OpeningProof, + ProverProof, RecursionChallenge, - LookupCommitments, RuntimeTable, RuntimeTableCfg, - LookupTable, - Field, } from './kimchi-types.js'; -import { MlArray, MlOption, MlTuple } from '../../../lib/ml/base.js'; -import { - fieldToRust, - fieldFromRust, - fieldsToRustFlat, - fieldsFromRustFlat, -} from './conversion-base.js'; -import { ConversionCore, ConversionCores, mapToUint32Array, unwrap } from './conversion-core.js'; export { proofConversion }; @@ -178,6 +178,7 @@ function proofConversionPerField( } function runtimeTableToRust([, id, data]: RuntimeTable): WasmRuntimeTable { + console.log('old runtime table to rust!'); return new RuntimeTable(id, core.vectorToRust(data)); } diff --git a/src/bindings/crypto/bindings/conversion-verifier-index.ts b/src/bindings/crypto/bindings/conversion-verifier-index.ts index 8aae4771f4..d8eb126832 100644 --- a/src/bindings/crypto/bindings/conversion-verifier-index.ts +++ b/src/bindings/crypto/bindings/conversion-verifier-index.ts @@ -80,7 +80,7 @@ function verifierIndexConversionPerField( function domainFromRust(domain: WasmDomain): Domain { let logSizeOfGroup = domain.log_size_of_group; let groupGen = fieldFromRust(domain.group_gen); - domain.free(); + // domain.free(); return [0, logSizeOfGroup, groupGen]; } @@ -117,8 +117,6 @@ function verifierIndexConversionPerField( ); } function verificationEvalsFromRust(evals: WasmVerificationEvals): VerificationEvals { - console.log('evals', evals.coefficients_comm); - let mlEvals: VerificationEvals = [ 0, core.polyCommsFromRust(evals.sigma_comm), diff --git a/src/bindings/crypto/napi-conversion-core.ts b/src/bindings/crypto/napi-conversion-core.ts index 7409d16939..8e5c1e3a55 100644 --- a/src/bindings/crypto/napi-conversion-core.ts +++ b/src/bindings/crypto/napi-conversion-core.ts @@ -6,7 +6,6 @@ import { fieldsFromRustFlat, fieldsToRustFlat, } from './bindings/conversion-base.js'; -import { mapFromUintArray } from './bindings/conversion-core.js'; import { Field, Gate, LookupTable, OrInfinity, PolyComm, Wire } from './bindings/kimchi-types.js'; import { mapTuple } from './bindings/util.js'; @@ -70,13 +69,11 @@ function napiConversionCore(napi: any) { }; return { - fp: { ...fpCore }, + fp: { + ...fpCore, + }, fq: { ...fqCore, - shiftsFromRust: (s: any) => { - let shifts = [s.s0, s.s1, s.s2, s.s3, s.s4, s.s5, s.s6]; - return [0, ...shifts.map(fieldFromRust)]; - }, }, ...shared, }; @@ -156,9 +153,9 @@ function conversionCorePerField({ makeAffine, PolyComm }: NapiClasses) { }; const affineFromRust = (pt: NapiAffine): OrInfinity => { if (pt.infinity) return 0; - console.log('pt', pt); - console.log('pt.x', pt.x); - console.log('pt.y', pt.y); + // console.log('pt', pt); + // console.log('pt.x', pt.x); + // console.log('pt.y', pt.y); const xField = fieldFromRust(pt.x); const yField = fieldFromRust(pt.y); @@ -182,22 +179,13 @@ function conversionCorePerField({ makeAffine, PolyComm }: NapiClasses) { return new PolyCommClass(unshifted as unknown, undefined); }; - /* const polyCommFromRust = (polyComm: NapiPolyComm): PolyComm => { - console.log('polyComm', polyComm); + const polyCommFromRust = (polyComm: any): any => { + if (polyComm == null) return undefined; + // console.log('polyComm', polyComm); const rustUnshifted = asArrayLike(polyComm.unshifted, 'polyComm.unshifted'); - console.log('rustUnshifted', rustUnshifted); + // console.log('rustUnshifted', rustUnshifted); const mlUnshifted = rustUnshifted.map(affineFromRust); return [0, [0, ...mlUnshifted]]; - }; */ - const polyCommFromRust = (polyComm: any): any => { - let rustUnshifted = polyComm.unshifted; - console.log('rustUnshifted', rustUnshifted); - let mlUnshifted = mapFromUintArray(rustUnshifted, (ptr) => { - console.log('ptr', ptr); - /* return affineFromRust(wrap(ptr, CommitmentCurve)); - */ - }); - return [0, [0, ...mlUnshifted]]; }; const polyCommsToRust = ([, ...comms]: MlArray): NapiPolyComm[] => diff --git a/src/bindings/crypto/napi-conversion-proof.ts b/src/bindings/crypto/napi-conversion-proof.ts index 690b416783..0b3565516a 100644 --- a/src/bindings/crypto/napi-conversion-proof.ts +++ b/src/bindings/crypto/napi-conversion-proof.ts @@ -1,54 +1,55 @@ +import { MlArray, MlOption, MlTuple } from '../../lib/ml/base.js'; +import type * as napiNamespace from '../compiled/node_bindings/plonk_wasm.cjs'; import type { - WasmPastaFpLookupTable, + WasmFpLookupCommitments, + WasmFpOpeningProof, + WasmFpProverCommitments, + WasmFpProverProof, WasmFpRuntimeTable, + WasmFqLookupCommitments, + WasmFqOpeningProof, + WasmFqProverCommitments, + WasmFqProverProof, + WasmFqRuntimeTable, + WasmPastaFpLookupTable, WasmPastaFpRuntimeTableCfg, WasmPastaFqLookupTable, - WasmFqRuntimeTable, WasmPastaFqRuntimeTableCfg, WasmVecVecFp, WasmVecVecFq, - WasmFpProverCommitments, - WasmFqProverCommitments, - WasmFpOpeningProof, - WasmFqOpeningProof, - WasmFpProverProof, - WasmFqProverProof, - WasmFpLookupCommitments, - WasmFqLookupCommitments, } from '../compiled/node_bindings/plonk_wasm.cjs'; -import type * as napiNamespace from '../compiled/node_bindings/plonk_wasm.cjs'; +import { + fieldFromRust, + fieldToRust, + fieldsFromRustFlat, + fieldsToRustFlat, +} from './bindings/conversion-base.js'; +import type { Field } from './bindings/field.js'; import type { - RuntimeTable, - RuntimeTableCfg, + LookupCommitments, LookupTable, + OpeningProof, + OrInfinity, + PointEvaluations, + PolyComm, + ProofEvaluations, ProofWithPublic, + ProverCommitments, ProverProof, - LookupCommitments, - PolyComm, PointEvaluations, ProofEvaluations, RecursionChallenge, - ProverCommitments, - OpeningProof, - OrInfinity, + RuntimeTable, + RuntimeTableCfg, } from './bindings/kimchi-types.js'; -import { MlArray, MlTuple, MlOption} from '../../lib/ml/base.js'; -import { - fieldsToRustFlat, - fieldToRust, - fieldsFromRustFlat, - fieldFromRust, -} from './bindings/conversion-base.js'; import { ConversionCore, ConversionCores } from './napi-conversion-core.js'; -import type { Field } from './bindings/field.js'; export { napiProofConversion }; - + const fieldToRust_ = (x: Field) => fieldToRust(x); const proofEvaluationsToRust = mapProofEvaluations(fieldToRust_); const proofEvaluationsFromRust = mapProofEvaluations(fieldFromRust); const pointEvalsOptionToRust = mapPointEvalsOption(fieldToRust_); const pointEvalsOptionFromRust = mapPointEvalsOption(fieldFromRust); - type NapiProofEvaluations = [ 0, MlOption>, @@ -129,7 +130,7 @@ function proofConversionPerField( commitments.free(); return [0, wComm as MlTuple, zComm, tComm, lookup]; } - + function lookupCommitmentsToRust(lookup: LookupCommitments): NapiLookupCommitments { let sorted = core.polyCommsToRust(lookup[1]); let aggreg = core.polyCommToRust(lookup[2]); @@ -143,7 +144,7 @@ function proofConversionPerField( lookup.free(); return [0, sorted, aggreg, runtime]; } - + function openingProofToRust(proof: OpeningProof): NapiOpeningProof { let [_, [, ...lr], delta, z1, z2, sg] = proof; // We pass l and r as separate vectors over the FFI @@ -175,8 +176,9 @@ function proofConversionPerField( proof.free(); return [0, [0, ...lr], delta, z1, z2, sg]; } - + function runtimeTableToRust([, id, data]: RuntimeTable): NapiRuntimeTable { + console.log('runtime table'); return new RuntimeTable(id, core.vectorToRust(data)); } @@ -192,8 +194,6 @@ function proofConversionPerField( } return new LookupTable(id, wasmData); } - - return { proofToRust([, public_evals, proof]: ProofWithPublic): NapiProverProof { @@ -253,7 +253,7 @@ function proofConversionPerField( ]; return [0, publicEvals, proof]; }, - + runtimeTablesToRust([, ...tables]: MlArray): NapiRuntimeTable[] { return tables.map(runtimeTableToRust); }, diff --git a/src/bindings/crypto/napi-conversion-verifier-index.ts b/src/bindings/crypto/napi-conversion-verifier-index.ts new file mode 100644 index 0000000000..ffee3bda69 --- /dev/null +++ b/src/bindings/crypto/napi-conversion-verifier-index.ts @@ -0,0 +1,287 @@ +import { MlArray, MlBool, MlOption } from '../../lib/ml/base.js'; +import type * as napiNamespace from '../compiled/node_bindings/plonk_wasm.cjs'; +import type { + WasmFpDomain, + WasmFpLookupSelectors, + WasmFpLookupVerifierIndex, + WasmFpPlonkVerificationEvals, + WasmFpPlonkVerifierIndex, + WasmFpShifts, + WasmFqDomain, + WasmFqLookupSelectors, + WasmFqLookupVerifierIndex, + WasmFqPlonkVerificationEvals, + WasmFqPlonkVerifierIndex, + WasmFqShifts, + LookupInfo as WasmLookupInfo, +} from '../compiled/node_bindings/plonk_wasm.cjs'; +import { fieldFromRust, fieldToRust } from './bindings/conversion-base.js'; +import { + Domain, + Field, + PolyComm, + VerificationEvals, + VerifierIndex, +} from './bindings/kimchi-types.js'; +import { Lookup, LookupInfo, LookupSelectors } from './bindings/lookup.js'; +import { ConversionCore, ConversionCores } from './napi-conversion-core.js'; + +export { napiVerifierIndexConversion }; + +type napi = typeof napiNamespace; + +type NapiDomain = WasmFpDomain | WasmFqDomain; +type NapiVerificationEvals = WasmFpPlonkVerificationEvals | WasmFqPlonkVerificationEvals; +type NapiShifts = WasmFpShifts | WasmFqShifts; +type NapiVerifierIndex = WasmFpPlonkVerifierIndex | WasmFqPlonkVerifierIndex; +type NapiLookupVerifierIndex = WasmFpLookupVerifierIndex | WasmFqLookupVerifierIndex; +type NapiLookupSelector = WasmFpLookupSelectors | WasmFqLookupSelectors; + +type NapiClasses = { + Domain: typeof WasmFpDomain | typeof WasmFqDomain; + VerificationEvals: typeof WasmFpPlonkVerificationEvals | typeof WasmFqPlonkVerificationEvals; + Shifts: typeof WasmFpShifts | typeof WasmFqShifts; + VerifierIndex: typeof WasmFpPlonkVerifierIndex | typeof WasmFqPlonkVerifierIndex; + LookupVerifierIndex: typeof WasmFpLookupVerifierIndex | typeof WasmFqLookupVerifierIndex; + LookupSelector: typeof WasmFpLookupSelectors | typeof WasmFqLookupSelectors; +}; + +function napiVerifierIndexConversion(napi: any, core: ConversionCores) { + return { + fp: verifierIndexConversionPerField(napi, core.fp, { + Domain: napi.WasmFpDomain, + VerificationEvals: napi.WasmFpPlonkVerificationEvals, + Shifts: napi.WasmFpShifts, + VerifierIndex: napi.WasmFpPlonkVerifierIndex, + LookupVerifierIndex: napi.WasmFpLookupVerifierIndex, + LookupSelector: napi.WasmFpLookupSelectors, + }), + fq: verifierIndexConversionPerField(napi, core.fq, { + Domain: napi.WasmFqDomain, + VerificationEvals: napi.WasmFqPlonkVerificationEvals, + Shifts: napi.WasmFqShifts, + VerifierIndex: napi.WasmFqPlonkVerifierIndex, + LookupVerifierIndex: napi.WasmFqLookupVerifierIndex, + LookupSelector: napi.WasmFqLookupSelectors, + }), + }; +} + +function verifierIndexConversionPerField( + napi: any, + core: ConversionCore, + { + Domain, + VerificationEvals, + Shifts, + VerifierIndex, + LookupVerifierIndex, + LookupSelector, + }: NapiClasses +) { + function domainToRust([, logSizeOfGroup, groupGen]: Domain): NapiDomain { + return new Domain(logSizeOfGroup, fieldToRust(groupGen)); + } + function domainFromRust(domain: NapiDomain): Domain { + return [0, domain.log_size_of_group, fieldFromRust(domain.group_gen)]; + } + + function verificationEvalsToRust(evals: VerificationEvals): NapiVerificationEvals { + let sigmaComm = core.polyCommsToRust(evals[1]); + let coefficientsComm = core.polyCommsToRust(evals[2]); + let genericComm = core.polyCommToRust(evals[3]); + let psmComm = core.polyCommToRust(evals[4]); + let completeAddComm = core.polyCommToRust(evals[5]); + let mulComm = core.polyCommToRust(evals[6]); + let emulComm = core.polyCommToRust(evals[7]); + let endomulScalarComm = core.polyCommToRust(evals[8]); + let xorComm = MlOption.mapFrom(evals[9], core.polyCommToRust); + let rangeCheck0Comm = MlOption.mapFrom(evals[10], core.polyCommToRust); + let rangeCheck1Comm = MlOption.mapFrom(evals[11], core.polyCommToRust); + let foreignFieldAddComm = MlOption.mapFrom(evals[12], core.polyCommToRust); + let foreignFieldMulComm = MlOption.mapFrom(evals[13], core.polyCommToRust); + let rotComm = MlOption.mapFrom(evals[14], core.polyCommToRust); + return new VerificationEvals( + sigmaComm as any, + coefficientsComm as any, + genericComm as any, + psmComm as any, + completeAddComm as any, + mulComm as any, + emulComm as any, + endomulScalarComm as any, + xorComm as any, + rangeCheck0Comm as any, + rangeCheck1Comm as any, + foreignFieldAddComm as any, + foreignFieldMulComm as any, + rotComm as any + ); + } + function verificationEvalsFromRust(evals: NapiVerificationEvals): VerificationEvals { + let mlEvals: VerificationEvals = [ + 0, + core.polyCommsFromRust(evals.sigma_comm), + core.polyCommsFromRust(evals.coefficients_comm), + core.polyCommFromRust(evals.generic_comm), + core.polyCommFromRust(evals.psm_comm), + core.polyCommFromRust(evals.complete_add_comm), + core.polyCommFromRust(evals.mul_comm), + core.polyCommFromRust(evals.emul_comm), + core.polyCommFromRust(evals.endomul_scalar_comm), + MlOption.mapTo(evals.xor_comm, core.polyCommFromRust), + MlOption.mapTo(evals.range_check0_comm, core.polyCommFromRust), + MlOption.mapTo(evals.range_check1_comm, core.polyCommFromRust), + MlOption.mapTo(evals.foreign_field_add_comm, core.polyCommFromRust), + MlOption.mapTo(evals.foreign_field_mul_comm, core.polyCommFromRust), + MlOption.mapTo(evals.rot_comm, core.polyCommFromRust), + ]; + return mlEvals; + } + + function lookupVerifierIndexToRust(lookup: Lookup): NapiLookupVerifierIndex { + let [ + , + joint_lookup_used, + lookup_table, + selectors, + table_ids, + lookup_info, + runtime_tables_selector, + ] = lookup; + return new LookupVerifierIndex( + MlBool.from(joint_lookup_used), + core.polyCommsToRust(lookup_table) as any, + lookupSelectorsToRust(selectors), + MlOption.mapFrom(table_ids, core.polyCommToRust) as any, + lookupInfoToRust(lookup_info), + MlOption.mapFrom(runtime_tables_selector, core.polyCommToRust) as any + ); + } + function lookupVerifierIndexFromRust(lookup: NapiLookupVerifierIndex): Lookup { + console.log('lookup: ', lookup); + let mlLookup: Lookup = [ + 0, + MlBool(lookup.joint_lookup_used), + core.polyCommsFromRust(lookup.lookup_table), + lookupSelectorsFromRust(lookup.lookup_selectors), + MlOption.mapTo(lookup.table_ids, core.polyCommFromRust), + lookupInfoFromRust(lookup.lookup_info), + MlOption.mapTo(lookup.runtime_tables_selector, core.polyCommFromRust), + ]; + return mlLookup; + } + + function lookupSelectorsToRust([ + , + lookup, + xor, + range_check, + ffmul, + ]: LookupSelectors): NapiLookupSelector { + return new LookupSelector( + MlOption.mapFrom(xor, core.polyCommToRust) as any, + MlOption.mapFrom(lookup, core.polyCommToRust) as any, + MlOption.mapFrom(range_check, core.polyCommToRust) as any, + MlOption.mapFrom(ffmul, core.polyCommToRust) as any + ); + } + function lookupSelectorsFromRust(selector: NapiLookupSelector): LookupSelectors { + let lookup = MlOption.mapTo(selector.lookup, core.polyCommFromRust); + let xor = MlOption.mapTo(selector.xor, core.polyCommFromRust); + let range_check = MlOption.mapTo(selector.range_check, core.polyCommFromRust); + let ffmul = MlOption.mapTo(selector.ffmul, core.polyCommFromRust); + return [0, lookup, xor, range_check, ffmul]; + } + + function lookupInfoToRust([, maxPerRow, maxJointSize, features]: LookupInfo): WasmLookupInfo { + let [, patterns, joint_lookup_used, uses_runtime_tables] = features; + let [, xor, lookup, range_check, foreign_field_mul] = patterns; + let napiPatterns = new napi.LookupPatterns( + MlBool.from(xor), + MlBool.from(lookup), + MlBool.from(range_check), + MlBool.from(foreign_field_mul) + ); + let napiFeatures = new napi.LookupFeatures( + napiPatterns, + MlBool.from(joint_lookup_used), + MlBool.from(uses_runtime_tables) + ); + return new napi.LookupInfo(maxPerRow, maxJointSize, napiFeatures); + } + function lookupInfoFromRust(info: WasmLookupInfo): LookupInfo { + let features = info.features; + let patterns = features.patterns; + let mlInfo: LookupInfo = [ + 0, + info.max_per_row, + info.max_joint_size, + [ + 0, + [ + 0, + MlBool(patterns.xor), + MlBool(patterns.lookup), + MlBool(patterns.range_check), + MlBool(patterns.foreign_field_mul), + ], + MlBool(features.joint_lookup_used), + MlBool(features.uses_runtime_tables), + ], + ]; + return mlInfo; + } + + let self = { + shiftsToRust([, ...shifts]: MlArray): NapiShifts { + let s = shifts.map((s) => fieldToRust(s)); + return new Shifts(s[0], s[1], s[2], s[3], s[4], s[5], s[6]); + }, + shiftsFromRust(s: NapiShifts): MlArray { + let shifts = [s.s0, s.s1, s.s2, s.s3, s.s4, s.s5, s.s6]; + return [0, ...shifts.map(fieldFromRust)]; + }, + + verifierIndexToRust(vk: VerifierIndex): NapiVerifierIndex { + let domain = domainToRust(vk[1]); + let maxPolySize = vk[2]; + let nPublic = vk[3]; + let prevChallenges = vk[4]; + let srs = vk[5]; + let evals = verificationEvalsToRust(vk[6]); + let shifts = self.shiftsToRust(vk[7]); + let lookupIndex = MlOption.mapFrom(vk[8], lookupVerifierIndexToRust); + let zkRows = vk[9]; + return new VerifierIndex( + domain, + maxPolySize, + nPublic, + prevChallenges, + srs, + evals, + shifts, + lookupIndex, + zkRows + ); + }, + verifierIndexFromRust(vk: NapiVerifierIndex): VerifierIndex { + console.log('vk lookup index from rust', vk.lookup_index); + let mlVk: VerifierIndex = [ + 0, + domainFromRust(vk.domain), + vk.max_poly_size, + vk.public_, + vk.prev_challenges, + vk.srs, + verificationEvalsFromRust(vk.evals), + self.shiftsFromRust(vk.shifts), + MlOption.mapTo(vk.lookup_index, lookupVerifierIndexFromRust), + vk.zk_rows, + ]; + return mlVk; + }, + }; + + return self; +} diff --git a/src/bindings/crypto/napi-srs.ts b/src/bindings/crypto/napi-srs.ts index fe61e75b4b..202ab236d6 100644 --- a/src/bindings/crypto/napi-srs.ts +++ b/src/bindings/crypto/napi-srs.ts @@ -8,7 +8,7 @@ import { } from '../../lib/proof-system/cache.js'; import { assert } from '../../lib/util/errors.js'; import { type WasmFpSrs, type WasmFqSrs } from '../compiled/node_bindings/plonk_wasm.cjs'; -import type { RustConversion, Napi } from './bindings.js'; +import type { Napi, RustConversion } from './bindings.js'; import { OrInfinity, OrInfinityJson } from './bindings/curve.js'; import { PolyComm } from './bindings/kimchi-types.js'; @@ -62,7 +62,7 @@ function cacheHeaderSrs(f: 'fp' | 'fq', domainSize: number): CacheHeader { ); } -function srs(napi: Napi , conversion: RustConversion) { +function srs(napi: Napi, conversion: RustConversion) { return { fp: srsPerField('fp', napi, conversion), fq: srsPerField('fq', napi, conversion), @@ -103,7 +103,18 @@ function srsPerField(f: 'fp' | 'fq', napi: Napi, conversion: RustConversion) { let maybeLagrangeCommitment = (srs: NapiSrs, domain_size: number, i: number) => { try { - return napi[`caml_${f}_srs_maybe_lagrange_commitment`](srs, domain_size, i); + console.log(3); + console.log('srs napi', srs); + + /*let bytes = (napi as any)[`caml_${f}_srs_to_bytes`](srs); + console.log('bytes', bytes); + let wasmSrs = undefined; + if (f === 'fp') wasmSrs = (napi as any)[`caml_${f}_srs_from_bytes`](bytes); + else wasmSrs = (napi as any)[`caml_fq_srs_from_bytes`](bytes); + */ + let s = napi[`caml_${f}_srs_maybe_lagrange_commitment`](srs, domain_size, i); + console.log('S', s); + return s; } catch (error) { console.error(`Error in SRS maybe lagrange commitment for field ${f}`); throw error; @@ -197,7 +208,8 @@ function srsPerField(f: 'fp' | 'fq', napi: Napi, conversion: RustConversion) { // happy, fast case: if basis is already stored on the srs, return the ith commitment let commitment = maybeLagrangeCommitment(srs, domainSize, i); - if (commitment === undefined) { + if (commitment === undefined || commitment === null) { + console.log('comm was undefined'); if (cache === undefined) { // if there is no cache, recompute and store basis in memory commitment = lagrangeCommitment(srs, domainSize, i); @@ -235,6 +247,7 @@ function srsPerField(f: 'fp' | 'fq', napi: Napi, conversion: RustConversion) { commitment = c; } } + console.log('commitment was not undefined'); // edge case for when we have a writeable cache and the basis was already stored on the srs // but we didn't store it in the cache separately yet @@ -271,7 +284,10 @@ function srsPerField(f: 'fp' | 'fq', napi: Napi, conversion: RustConversion) { lagrangeCommitmentsWholeDomain(srs: NapiSrs, domainSize: number) { console.log('lagrangeCommitmentsWholeDomain'); try { - let napiComms = napi[`caml_${f}_srs_lagrange_commitments_whole_domain_ptr`](srs, domainSize); + let napiComms = napi[`caml_${f}_srs_lagrange_commitments_whole_domain_ptr`]( + srs, + domainSize + ); let mlComms = conversion[f].polyCommsFromRust(napiComms as any); return mlComms; } catch (error) { diff --git a/src/lib/proof-system/prover-keys.ts b/src/lib/proof-system/prover-keys.ts index e9fd8de7d2..122d8bf27b 100644 --- a/src/lib/proof-system/prover-keys.ts +++ b/src/lib/proof-system/prover-keys.ts @@ -10,7 +10,8 @@ import { WasmPastaFpPlonkIndex, WasmPastaFqPlonkIndex, } from '../../bindings/compiled/node_bindings/plonk_wasm.cjs'; -import { getRustConversion } from '../../bindings/crypto/bindings.js'; +// TODO: include conversion bundle to decide between wasm and napi conversion +import { createNativeRustConversion } from '../../bindings/crypto/bindings.js'; import { VerifierIndex } from '../../bindings/crypto/bindings/kimchi-types.js'; import { MlString } from '../ml/base.js'; import { CacheHeader, cacheHeaderVersion } from './cache.js'; @@ -95,13 +96,13 @@ function encodeProverKey(value: SnarkKey): Uint8Array { case KeyType.StepProvingKey: { let index = value[1][1]; let encoded = wasm.caml_pasta_fp_plonk_index_encode( - (wasm as any).prover_index_fp_from_bytes(index.serialize()) + (wasm as any).prover_index_fp_deserialize((wasm as any).prover_index_fp_serialize(index)) ); return encoded; } case KeyType.StepVerificationKey: { let vkMl = value[1]; - const rustConversion = getRustConversion(wasm); + const rustConversion = createNativeRustConversion(wasm); let vkWasm = rustConversion.fp.verifierIndexToRust(vkMl); let string = wasm.caml_pasta_fp_plonk_verifier_index_serialize(vkWasm); return new TextEncoder().encode(string); @@ -109,7 +110,7 @@ function encodeProverKey(value: SnarkKey): Uint8Array { case KeyType.WrapProvingKey: { let index = value[1][1]; let encoded = wasm.caml_pasta_fq_plonk_index_encode( - (wasm as any).prover_index_fq_from_bytes(index.serialize()) + (wasm as any).prover_index_fq_deserialize((wasm as any).prover_index_fq_serialize(index)) ); return encoded; } @@ -139,7 +140,7 @@ function decodeProverKey(header: SnarkKeyHeader, bytes: Uint8Array): SnarkKey { let srs = Pickles.loadSrsFp(); let string = new TextDecoder().decode(bytes); let vkWasm = wasm.caml_pasta_fp_plonk_verifier_index_deserialize(srs, string); - const rustConversion = getRustConversion(wasm); + const rustConversion = createNativeRustConversion(wasm); let vkMl = rustConversion.fp.verifierIndexFromRust(vkWasm); return [KeyType.StepVerificationKey, vkMl]; } From c7f16fe7e6bfad68eaef125a391a820e8e074041 Mon Sep 17 00:00:00 2001 From: querolita Date: Mon, 8 Dec 2025 19:04:18 +0100 Subject: [PATCH 20/23] submodule: update mina --- src/mina | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/mina b/src/mina index 54c170fe18..814a065c4f 160000 --- a/src/mina +++ b/src/mina @@ -1 +1 @@ -Subproject commit 54c170fe18d61c8bea3cebe2731aecfa60fc3747 +Subproject commit 814a065c4f0e69460000c1ae47bd6db49193b2c5 From 74a08627d0b83253366c5cba325f98b454f849a3 Mon Sep 17 00:00:00 2001 From: querolita Date: Mon, 8 Dec 2025 19:11:31 +0100 Subject: [PATCH 21/23] bindings: revert wasm changes --- src/bindings/crypto/bindings/conversion-core.ts | 10 +++++----- .../crypto/bindings/conversion-verifier-index.ts | 2 +- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/bindings/crypto/bindings/conversion-core.ts b/src/bindings/crypto/bindings/conversion-core.ts index 644504ec36..ea5002e6df 100644 --- a/src/bindings/crypto/bindings/conversion-core.ts +++ b/src/bindings/crypto/bindings/conversion-core.ts @@ -15,7 +15,7 @@ import { fieldsFromRustFlat, fieldsToRustFlat, } from './conversion-base.js'; -import { Gate, OrInfinity, Wire } from './kimchi-types.js'; +import { Gate, OrInfinity, PolyComm, Wire } from './kimchi-types.js'; import { mapTuple } from './util.js'; export { @@ -109,13 +109,13 @@ function conversionCorePerField( return [0, ...arr]; }, - polyCommToRust(polyComm: any): WasmPolyComm { + polyCommToRust(polyComm: PolyComm): WasmPolyComm { let [, camlElems] = polyComm; let rustShifted = undefined; let rustUnshifted = self.pointsToRust(camlElems); return new PolyComm(rustUnshifted, rustShifted); }, - polyCommFromRust(polyComm: WasmPolyComm): any { + polyCommFromRust(polyComm: WasmPolyComm): PolyComm { console.log('polyComm old', polyComm); let rustUnshifted = polyComm.unshifted; console.log('rustUnshifted', rustUnshifted); @@ -125,10 +125,10 @@ function conversionCorePerField( return [0, [0, ...mlUnshifted]]; }, - polyCommsToRust([, ...comms]: MlArray): Uint32Array { + polyCommsToRust([, ...comms]: MlArray): Uint32Array { return mapToUint32Array(comms, (c) => unwrap(self.polyCommToRust(c))); }, - polyCommsFromRust(rustComms: Uint32Array): MlArray { + polyCommsFromRust(rustComms: Uint32Array): MlArray { let comms = mapFromUintArray(rustComms, (ptr) => self.polyCommFromRust(wrap(ptr, PolyComm))); return [0, ...comms]; }, diff --git a/src/bindings/crypto/bindings/conversion-verifier-index.ts b/src/bindings/crypto/bindings/conversion-verifier-index.ts index d8eb126832..a5353a1316 100644 --- a/src/bindings/crypto/bindings/conversion-verifier-index.ts +++ b/src/bindings/crypto/bindings/conversion-verifier-index.ts @@ -80,7 +80,7 @@ function verifierIndexConversionPerField( function domainFromRust(domain: WasmDomain): Domain { let logSizeOfGroup = domain.log_size_of_group; let groupGen = fieldFromRust(domain.group_gen); - // domain.free(); + domain.free(); return [0, logSizeOfGroup, groupGen]; } From c6fc9d61c4e62befb9fe3f9bb7ae4c577243c692 Mon Sep 17 00:00:00 2001 From: querolita Date: Mon, 8 Dec 2025 21:56:04 +0100 Subject: [PATCH 22/23] submodule: update mina --- src/mina | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/mina b/src/mina index 814a065c4f..ebfe5d874f 160000 --- a/src/mina +++ b/src/mina @@ -1 +1 @@ -Subproject commit 814a065c4f0e69460000c1ae47bd6db49193b2c5 +Subproject commit ebfe5d874f09b5562d40e9eed6c647310cf9d2f1 From bd7613aa53b16965d626a4c22330135b1b40eca8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ana=C3=AFs=20Querol?= Date: Tue, 9 Dec 2025 13:47:37 +0100 Subject: [PATCH 23/23] Update src/bindings/crypto/napi-conversion-proof.ts Co-authored-by: zkShigoto <113286510+Shigoto-dev19@users.noreply.github.com> --- src/bindings/crypto/napi-conversion-proof.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/src/bindings/crypto/napi-conversion-proof.ts b/src/bindings/crypto/napi-conversion-proof.ts index 690b416783..cfb889afe4 100644 --- a/src/bindings/crypto/napi-conversion-proof.ts +++ b/src/bindings/crypto/napi-conversion-proof.ts @@ -241,7 +241,6 @@ function proofConversionPerField( let scalars = fieldsFromRustFlat(prevChallengeScalars.get(i)); return [0, scalars, comms]; }); - wasmProof.free(); let proof: ProverProof = [ 0, commitments,