o1js
Version:
TypeScript framework for zk-SNARKs and zkApps
143 lines • 5.63 kB
JavaScript
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