UNPKG

o1js

Version:

TypeScript framework for zk-SNARKs and zkApps

143 lines 5.63 kB
import { MlBool, MlOption } from '../ml/base.js'; // public API export { FeatureFlags }; // internal API export { featureFlagsToMlOption, featureFlagsFromGates }; /** * Feature flags indicate what custom gates are used in a proof of circuit. * Side loading, for example, requires a set of feature flags in advance (at compile time) in order to verify and side load proofs. * If the side loaded proofs and verification keys do not match the specified feature flag configurations, the verification will fail. * Flags specified as `undefined` are considered as `maybe` by Pickles. This means, proofs can be sided loaded that can, but don't have to, use a specific custom gate. * _Note:_ `Maybe` feature flags incur a proving overhead. */ const FeatureFlags = { /** * Returns a feature flag configuration where all flags are set to false. */ allNone: { rangeCheck0: false, rangeCheck1: false, foreignFieldAdd: false, foreignFieldMul: false, xor: false, rot: false, lookup: false, runtimeTables: false, }, /** * Returns a feature flag configuration where all flags are optional. */ allMaybe: { rangeCheck0: undefined, rangeCheck1: undefined, foreignFieldAdd: undefined, foreignFieldMul: undefined, xor: undefined, rot: undefined, lookup: undefined, runtimeTables: undefined, }, /** * Given a list of gates, returns the feature flag configuration that the gates use. */ fromGates: featureFlagsFromGates, /** * Given a ZkProgram, return the feature flag configuration that fits the given program. * This function considers all methods of the specified ZkProgram and finds a configuration that fits all. * Optionally, it accepts a flag indicating whether runtime tables are used in the program (default is false) */ fromZkProgram: async (program, withRuntimeTables = false) => await fromZkProgramList([program], withRuntimeTables), /** * Given a list of ZkPrograms, return the feature flag configuration that fits the given set of programs. * This function considers all methods of all specified ZkPrograms and finds a configuration that fits all. */ fromZkProgramList, }; async function fromZkProgramList(programs, withRuntimeTables = false) { let flatMethodIntfs = []; for (const program of programs) { let methodInterface = await program.analyzeMethods(); flatMethodIntfs.push(...Object.values(methodInterface)); } return featureFlagsfromFlatMethodIntfs(flatMethodIntfs, withRuntimeTables); } async function featureFlagsfromFlatMethodIntfs(methodIntfs, runtimeTables = false) { // compute feature flags that belong to each method let flags = methodIntfs.map(({ gates }) => { return featureFlagsFromGates(gates, runtimeTables); }); if (flags.length === 0) throw Error('The ZkProgram has no methods, in order to calculate feature flags, please attach a method to your ZkProgram.'); // initialize feature flags to all false let globalFlags = { rangeCheck0: false, rangeCheck1: false, foreignFieldAdd: false, foreignFieldMul: false, xor: false, rot: false, lookup: false, runtimeTables: false, }; // if there's only one method that means it defines the feature flags for the entire program if (flags.length === 1) return flags[0]; // calculating the crossover between all methods, compute the shared feature flag set flags.forEach((featureFlags, i) => { for (const [flagType, currentFlag] of Object.entries(featureFlags)) { if (i === 0) { // initialize first iteration of flags freely globalFlags[flagType] = currentFlag; } else if (globalFlags[flagType] != currentFlag) { // if flags conflict, set them to undefined to account for both cases (true and false) ^= maybe // otherwise side loading couldn't verify some proofs of some method branches! globalFlags[flagType] = undefined; } } }); return globalFlags; } // what feature flags to set to enable certain gate types const gateToFlag = { RangeCheck0: 'rangeCheck0', RangeCheck1: 'rangeCheck1', ForeignFieldAdd: 'foreignFieldAdd', ForeignFieldMul: 'foreignFieldMul', Xor16: 'xor', Rot64: 'rot', Lookup: 'lookup', }; function featureFlagsFromGates(gates, runtimeTables = false) { let flags = { rangeCheck0: false, rangeCheck1: false, foreignFieldAdd: false, foreignFieldMul: false, xor: false, rot: false, lookup: false, runtimeTables, }; for (let gate of gates) { let flag = gateToFlag[gate.type]; if (flag !== undefined) flags[flag] = true; } return flags; } function featureFlagsToMlOption(flags, withRuntimeTables) { const { rangeCheck0, rangeCheck1, foreignFieldAdd, foreignFieldMul, xor, rot, lookup, runtimeTables, } = flags; return [ 0, MlOption.mapTo(rangeCheck0, MlBool), MlOption.mapTo(rangeCheck1, MlBool), MlOption.mapTo(foreignFieldAdd, MlBool), MlOption.mapTo(foreignFieldMul, MlBool), MlOption.mapTo(xor, MlBool), MlOption.mapTo(rot, MlBool), MlOption.mapTo(lookup, MlBool), MlOption.mapTo(runtimeTables || withRuntimeTables, MlBool), ]; } //# sourceMappingURL=feature-flags.js.map