UNPKG

o1js

Version:

TypeScript framework for zk-SNARKs and zkApps

266 lines 10.8 kB
import { MlArray } from '../../../lib/ml/base.js'; import { readCache, withVersion, writeCache, } from '../../../lib/proof-system/cache.js'; import { srsCache as cache } from '../cache.js'; import { assert } from '../../../lib/util/errors.js'; import { OrInfinity } from '../bindings/curve.js'; export { srs }; function empty() { return {}; } const srsStore = { fp: empty(), fq: empty() }; const CacheReadRegister = new Map(); const srsVersion = 1; function cacheHeaderLagrange(f, domainSize) { let id = `lagrange-basis-${f}-${domainSize}`; return withVersion({ kind: 'lagrange-basis', persistentId: id, uniqueId: id, dataType: 'string', }, srsVersion); } function cacheHeaderSrs(f, domainSize) { let id = `srs-${f}-${domainSize}`; return withVersion({ kind: 'srs', persistentId: id, uniqueId: id, dataType: 'string', }, srsVersion); } function srs(napi, conversion) { return { fp: srsPerField('fp', napi, conversion), fq: srsPerField('fq', napi, conversion), }; } function srsPerField(f, napi, conversion) { // note: these functions are properly typed, thanks to TS template literal types let createSrs = (size) => { try { return napi[`caml_${f}_srs_create_parallel`](size); } catch (error) { console.error(`Error in SRS get for field ${f}`); throw error; } }; let getSrs = (srs) => { try { let fn = napi[`caml_${f}_srs_get`]; return fn(srs); } catch (error) { console.error(`Error in SRS get for field ${f}`); throw error; } }; let isEmptySrs = (srs) => { try { let points = getSrs(srs); return points == null || points.length <= 1; } catch { return true; } }; let setSrs = (points) => { try { let fn = napi[`caml_${f}_srs_set`]; return fn(points); } catch (error) { console.error(`Error in SRS set for field ${f} args ${points}`); throw error; } }; let maybeLagrangeCommitment = (srs, domain_size, i) => { try { let fn = napi[`caml_${f}_srs_maybe_lagrange_commitment`]; return fn(srs, domain_size, i); } catch (error) { console.error(`Error in SRS maybe lagrange commitment for field ${f}`); throw error; } }; let lagrangeCommitment = (srs, domain_size, i) => { try { let fn = napi[`caml_${f}_srs_lagrange_commitment`]; return fn(srs, domain_size, i); } catch (error) { console.error(`Error in SRS lagrange commitment for field ${f}`); throw error; } }; let setLagrangeBasis = (srs, domain_size, input) => { try { let fn = napi[`caml_${f}_srs_set_lagrange_basis`]; return fn(srs, domain_size, input); } catch (error) { console.error(`Error in SRS set lagrange basis for field ${f}`); throw error; } }; let getLagrangeBasis = (srs, n) => { try { let fn = napi[`caml_${f}_srs_get_lagrange_basis`]; return fn(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) { let srs = srsStore[f][size]; if (srs !== undefined && isEmptySrs(srs)) { delete srsStore[f][size]; srs = undefined; } if (srs === undefined) { if (cache === undefined) { // if there is no cache, create SRS in memory srs = createSrs(size); } else { let header = cacheHeaderSrs(f, size); // try to read SRS from cache / recompute and write if not found srs = readCache(cache, header, (bytes) => { // TODO: this takes a bit too long, about 300ms for 2^16 // `pointsToRust` is the clear bottleneck let jsonSrs = JSON.parse(new TextDecoder().decode(bytes)); let mlSrs = MlArray.mapTo(jsonSrs, OrInfinity.fromJSON); let wasmSrs = conversion[f].pointsToRust(mlSrs); let candidate = setSrs(wasmSrs); if (isEmptySrs(candidate)) return undefined; return candidate; }); if (srs === undefined) { // not in cache srs = createSrs(size); if (cache.canWrite) { let wasmSrs = getSrs(srs); 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); } } } srsStore[f][size] = srs; } return srsStore[f][size]; }, /** * returns ith Lagrange basis commitment for a given domain size */ lagrangeCommitment(srs, domainSize, i) { // happy, fast case: if basis is already stored on the srs, return the ith commitment let commitment = maybeLagrangeCommitment(srs, domainSize, i); if (commitment === undefined || commitment === null) { 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) { 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); } 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); } } if (commitment == null) { throw Error('lagrangeCommitment: missing commitment'); } return conversion[f].polyCommFromRust(commitment); }, /** * Returns the Lagrange basis commitments for the whole domain */ lagrangeCommitmentsWholeDomain(srs, domainSize) { try { let napiComms = napi[`caml_${f}_srs_lagrange_commitments_whole_domain_ptr`](srs, domainSize); let mlComms = conversion[f].polyCommsFromRust(napiComms); 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, logSize) { // this ensures that basis is stored on the srs, no need to duplicate caching logic this.lagrangeCommitment(srs, 1 << logSize, 0); }, }; } function polyCommsToJSON(comms) { return MlArray.mapFrom(comms, ([, elems]) => { return { shifted: MlArray.mapFrom(elems, OrInfinity.toJSON), unshifted: undefined, }; }); } function polyCommsFromJSON(json) { return MlArray.mapTo(json, ({ shifted, unshifted }) => { return [0, MlArray.mapTo(shifted, OrInfinity.fromJSON)]; }); } function readCacheLazy(cache, header, conversion, f, srs, domainSize, setLagrangeBasis) { if (CacheReadRegister.get(header.uniqueId) === true) return true; return readCache(cache, header, (bytes) => { let comms = 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; }); } //# sourceMappingURL=napi-srs.js.map