Skip to content

Commit

Permalink
Merge pull request #1846 from o1-labs/feature/provable-proofs
Browse files Browse the repository at this point in the history
Proof file restructure
  • Loading branch information
mitschabaude authored Oct 2, 2024
2 parents b062cc5 + 6017d63 commit e255dcb
Show file tree
Hide file tree
Showing 14 changed files with 550 additions and 516 deletions.
17 changes: 12 additions & 5 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,8 @@ export type {
FlexibleProvablePure,
InferProvable,
} from './lib/provable/types/struct.js';
export { From } from './bindings/lib/provable-generic.js';
export { ProvableType } from './lib/provable/types/provable-intf.js';
export {
provable,
provablePure,
Expand Down Expand Up @@ -79,17 +81,19 @@ export { state, State, declareState } from './lib/mina/state.js';

export type { JsonProof } from './lib/proof-system/zkprogram.js';
export {
type ProofBase,
Proof,
DynamicProof,
SelfProof,
verify,
Empty,
Undefined,
Void,
VerificationKey,
FeatureFlags,
} from './lib/proof-system/zkprogram.js';
export {
type ProofBase,
Proof,
DynamicProof,
} from './lib/proof-system/proof.js';
export { FeatureFlags } from './lib/proof-system/feature-flags.js';
export { Cache, CacheHeader } from './lib/proof-system/cache.js';

export { Account } from './lib/mina/account.js';
Expand All @@ -104,7 +108,10 @@ export {
} from './lib/mina/account-update.js';

export { TokenAccountUpdateIterator } from './lib/mina/token/forest-iterator.js';
export { TokenContract, TokenContractV2 } from './lib/mina/token/token-contract.js';
export {
TokenContract,
TokenContractV2,
} from './lib/mina/token/token-contract.js';

export type { TransactionStatus } from './lib/mina/graphql.js';
export {
Expand Down
8 changes: 2 additions & 6 deletions src/lib/mina/account-update.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,12 +33,8 @@ import {
ClosedInterval,
getAccountPreconditions,
} from './precondition.js';
import {
dummyBase64Proof,
Empty,
Proof,
Prover,
} from '../proof-system/zkprogram.js';
import { dummyBase64Proof, Empty, Prover } from '../proof-system/zkprogram.js';
import { Proof } from '../proof-system/proof.js';
import { Memo } from '../../mina-signer/src/memo.js';
import {
Events as BaseEvents,
Expand Down
3 changes: 2 additions & 1 deletion src/lib/mina/actions/batch-reducer.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { Proof, SelfProof } from '../../proof-system/zkprogram.js';
import { SelfProof } from '../../proof-system/zkprogram.js';
import { Proof } from '../../proof-system/proof.js';
import { Bool, Field } from '../../provable/wrapped.js';
import { SmartContract } from '../zkapp.js';
import { assert, assertDefined } from '../../util/assert.js';
Expand Down
3 changes: 2 additions & 1 deletion src/lib/mina/actions/offchain-state-rollup.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { Proof, ZkProgram } from '../../proof-system/zkprogram.js';
import { ZkProgram } from '../../proof-system/zkprogram.js';
import { Proof } from '../../proof-system/proof.js';
import { Bool, Field } from '../../provable/wrapped.js';
import { MerkleList, MerkleListIterator } from '../../provable/merkle-list.js';
import { Actions } from '../../../bindings/mina-transaction/transaction-leaves.js';
Expand Down
2 changes: 1 addition & 1 deletion src/lib/mina/actions/offchain-state.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import {
toKeyHash,
} from './offchain-state-serialization.js';
import { Field } from '../../provable/wrapped.js';
import { Proof } from '../../proof-system/zkprogram.js';
import { Proof } from '../../proof-system/proof.js';
import {
OffchainStateCommitments,
OffchainStateRollup,
Expand Down
3 changes: 2 additions & 1 deletion src/lib/mina/transaction.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,8 @@ import {
import { Field } from '../provable/wrapped.js';
import { PrivateKey, PublicKey } from '../provable/crypto/signature.js';
import { UInt32, UInt64 } from '../provable/int.js';
import { Empty, Proof } from '../proof-system/zkprogram.js';
import { Empty } from '../proof-system/zkprogram.js';
import { Proof } from '../proof-system/proof.js';
import { currentTransaction } from './transaction-context.js';
import { Provable } from '../provable/provable.js';
import { assertPreconditionInvariants } from './precondition.js';
Expand Down
12 changes: 8 additions & 4 deletions src/lib/mina/zkapp.ts
Original file line number Diff line number Diff line change
Expand Up @@ -49,9 +49,9 @@ import {
methodArgumentsToConstant,
methodArgumentTypesAndValues,
MethodInterface,
Proof,
sortMethodArguments,
} from '../proof-system/zkprogram.js';
import { Proof } from '../proof-system/proof.js';
import { PublicKey } from '../provable/crypto/signature.js';
import {
InternalStateType,
Expand Down Expand Up @@ -1107,16 +1107,20 @@ super.init();
}

const queryFilterOptions: EventActionFilterOptions = {};
if(start.greaterThan(UInt32.from(0)).toBoolean()) {
if (start.greaterThan(UInt32.from(0)).toBoolean()) {
queryFilterOptions.from = start;
}
if(end) {
if (end) {
queryFilterOptions.to = end;
}
// filters all elements so that they are within the given range
// only returns { type: "", event: [] } in a flat format
let events = (
await Mina.fetchEvents(this.address, this.self.body.tokenId, queryFilterOptions)
await Mina.fetchEvents(
this.address,
this.self.body.tokenId,
queryFilterOptions
)
)
.map((event) => {
return event.events.map((eventData) => {
Expand Down
189 changes: 189 additions & 0 deletions src/lib/proof-system/feature-flags.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,189 @@
import { MlFeatureFlags, Gate, GateType } from '../../snarky.js';
import { MlBool, MlOption, MlArrayOptionalElements } from '../ml/base.js';
import type { analyzeMethod } from './zkprogram.js';

// public API
export { FeatureFlags };

// internal API
export { featureFlagsToMlOption, featureFlagsFromGates };

type AnalysableProgram = {
analyzeMethods: () => Promise<{
[I in keyof any]: Awaited<ReturnType<typeof analyzeMethod>>;
}>;
};

type FeatureFlags = {
rangeCheck0: boolean | undefined;
rangeCheck1: boolean | undefined;
foreignFieldAdd: boolean | undefined;
foreignFieldMul: boolean | undefined;
xor: boolean | undefined;
rot: boolean | undefined;
lookup: boolean | undefined;
runtimeTables: boolean | undefined;
};
/**
* 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.
*/
fromZkProgram: async (program: AnalysableProgram) =>
await fromZkProgramList([program]),

/**
* 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: Array<AnalysableProgram>) {
let flatMethodIntfs: Array<Awaited<ReturnType<typeof analyzeMethod>>> = [];
for (const program of programs) {
let methodInterface = await program.analyzeMethods();
flatMethodIntfs.push(...Object.values(methodInterface));
}

return featureFlagsfromFlatMethodIntfs(flatMethodIntfs);
}

async function featureFlagsfromFlatMethodIntfs(
methodIntfs: Array<Awaited<ReturnType<typeof analyzeMethod>>>
): Promise<FeatureFlags> {
// compute feature flags that belong to each method
let flags = methodIntfs.map(({ gates }) => {
return featureFlagsFromGates(gates);
});
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: Record<string, boolean | undefined> = {
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 as FeatureFlags;
}

// what feature flags to set to enable certain gate types

const gateToFlag: Partial<Record<GateType, keyof FeatureFlags>> = {
RangeCheck0: 'rangeCheck0',
RangeCheck1: 'rangeCheck1',
ForeignFieldAdd: 'foreignFieldAdd',
ForeignFieldMul: 'foreignFieldMul',
Xor16: 'xor',
Rot64: 'rot',
Lookup: 'lookup',
};

function featureFlagsFromGates(gates: Gate[]): FeatureFlags {
let flags: FeatureFlags = {
rangeCheck0: false,
rangeCheck1: false,
foreignFieldAdd: false,
foreignFieldMul: false,
xor: false,
rot: false,
lookup: false,
runtimeTables: false,
};
for (let gate of gates) {
let flag = gateToFlag[gate.type];
if (flag !== undefined) flags[flag] = true;
}
return flags;
}

function featureFlagsToMlOption(
flags: FeatureFlags
): MlArrayOptionalElements<MlFeatureFlags> {
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, MlBool),
];
}
2 changes: 1 addition & 1 deletion src/lib/proof-system/proof-system.unit-test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,11 @@ import { UInt64 } from '../provable/int.js';
import {
CompiledTag,
Empty,
Proof,
ZkProgram,
picklesRuleFromFunction,
sortMethodArguments,
} from './zkprogram.js';
import { Proof } from './proof.js';
import { expect } from 'expect';
import { Pickles, Snarky } from '../../snarky.js';
import { AnyFunction } from '../util/types.js';
Expand Down
Loading

0 comments on commit e255dcb

Please sign in to comment.