Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Error feedback on compile-prover discrepancies #1363

Draft
wants to merge 29 commits into
base: v1
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
a79b0c9
start debugging dex example
mitschabaude Jan 8, 2024
01595e1
start adding types for low level snarky intf
mitschabaude Jan 8, 2024
b26f2b2
iterate on types
mitschabaude Jan 9, 2024
7444829
iterate on api and fix type error
mitschabaude Jan 10, 2024
bb54d4f
runner that can compare circuit constraints against a previous run
mitschabaude Jan 10, 2024
75a48f7
expose more flexible cs processing
mitschabaude Jan 10, 2024
cbf40cd
revert returning result
mitschabaude Jan 10, 2024
02fdbc8
don't return result from constraint system
mitschabaude Jan 10, 2024
08f9f72
use runCircuit in analyzeMethods
mitschabaude Jan 10, 2024
b3a3479
move zkapp proving logic to separate file
mitschabaude Jan 10, 2024
089e19e
minor
mitschabaude Jan 10, 2024
5121c46
WIP: compare constraints in zkapp proof to the ones created in analyz…
mitschabaude Jan 10, 2024
7b58e97
remove debugging, fix basic constraint data
mitschabaude Jan 10, 2024
a2064f6
add memoized witnesses to avoid failing constraints
mitschabaude Jan 10, 2024
923c5ac
hook prover consistency check into prover when detecting a relevant p…
mitschabaude Jan 10, 2024
ad20e1c
show traces to constraints in both versions
mitschabaude Jan 11, 2024
b615991
seems more correct but I'm not sure
mitschabaude Jan 11, 2024
73b17e0
helper to print account update layout
mitschabaude Jan 11, 2024
e3cfaf9
fix the damn bug
mitschabaude Jan 11, 2024
061cad0
[dex] remove obsolete scare comments
mitschabaude Jan 11, 2024
93568b8
minor example tweak
mitschabaude Jan 11, 2024
9127ae6
submodules
mitschabaude Jan 11, 2024
e727822
Merge branch 'fix/prover-bug-reloaded' into feature/debug-missing-wit…
mitschabaude Jan 11, 2024
f19076a
changelog
mitschabaude Jan 11, 2024
bb37bf1
Merge branch 'main' into feature/debug-missing-witnesses
mitschabaude Jan 17, 2024
e9979d9
reduce diff to main where changes weren't necessary
mitschabaude Jan 17, 2024
68487d7
add docs and changelog
mitschabaude Jan 17, 2024
dff07c4
more cases in which to check inconsistent constraints
mitschabaude Jan 17, 2024
e49b788
bindings
mitschabaude Jan 17, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,11 @@ This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm

- **SHA256 hash function** exposed via `Hash.SHA2_256` or `Gadgets.SHA256`. https://github.com/o1-labs/o1js/pull/1285

### Changed

- Massively improved error reporting when detecting mismatches in the constraints generated in `compile()` vs `prove()` https://github.com/o1-labs/o1js/pull/1363
- Made possible by new internal tooling to collect constraints, along with their stack trace, when they are added by snarky .

### Fixed

- Fix approving of complex account update layouts https://github.com/o1-labs/o1js/pull/1364
Expand Down
2 changes: 1 addition & 1 deletion src/bindings
173 changes: 22 additions & 151 deletions src/lib/account_update.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import {
provable,
provablePure,
} from './circuit_value.js';
import { memoizationContext, memoizeWitness, Provable } from './provable.js';
import { memoizeWitness, Provable } from './provable.js';
import { Field, Bool } from './core.js';
import { Pickles, Test } from '../snarky.js';
import { jsLayout } from '../bindings/mina-transaction/gen/js-layout.js';
Expand All @@ -18,7 +18,7 @@ import { UInt64, UInt32, Int64, Sign } from './int.js';
import * as Mina from './mina.js';
import { SmartContract } from './zkapp.js';
import * as Precondition from './precondition.js';
import { dummyBase64Proof, Empty, Proof, Prover } from './proof_system.js';
import { Proof, Prover } from './proof_system.js';
import { Memo } from '../mina-signer/src/memo.js';
import {
Events,
Expand All @@ -29,9 +29,7 @@ import { hashWithPrefix, packToFields } from './hash.js';
import { mocks, prefixes } from '../bindings/crypto/constants.js';
import { Context } from './global-context.js';
import { assert } from './errors.js';
import { MlArray } from './ml/base.js';
import { Signature, signFieldElement } from '../mina-signer/src/signature.js';
import { MlFieldConstArray } from './ml/fields.js';
import { transactionCommitments } from '../mina-signer/src/sign-zkapp-command.js';

// external API
Expand All @@ -47,7 +45,6 @@ export {
FeePayerUnsigned,
ZkappCommand,
addMissingSignatures,
addMissingProofs,
ZkappStateLength,
Events,
Actions,
Expand All @@ -57,8 +54,11 @@ export {
createChildAccountUpdate,
AccountUpdatesLayout,
zkAppProver,
ZkappProverData,
SmartContractContext,
dummySignature,
LazySignature,
LazyProof,
};

const ZkappStateLength = 8;
Expand Down Expand Up @@ -96,8 +96,8 @@ type Preconditions = AccountUpdateBody['preconditions'];
*/
type SetOrKeep<T> = { isSome: Bool; value: T };

const True = () => Bool(true);
const False = () => Bool(false);
const True = Bool(true);
const False = Bool(false);

/**
* One specific permission value.
Expand All @@ -114,45 +114,45 @@ let Permission = {
* Modification is impossible.
*/
impossible: (): Permission => ({
constant: True(),
signatureNecessary: True(),
signatureSufficient: False(),
constant: True,
signatureNecessary: True,
signatureSufficient: False,
}),

/**
* Modification is always permitted
*/
none: (): Permission => ({
constant: True(),
signatureNecessary: False(),
signatureSufficient: True(),
constant: True,
signatureNecessary: False,
signatureSufficient: True,
}),

/**
* Modification is permitted by zkapp proofs only
*/
proof: (): Permission => ({
constant: False(),
signatureNecessary: False(),
signatureSufficient: False(),
constant: False,
signatureNecessary: False,
signatureSufficient: False,
}),

/**
* Modification is permitted by signatures only, using the private key of the zkapp account
*/
signature: (): Permission => ({
constant: False(),
signatureNecessary: True(),
signatureSufficient: True(),
constant: False,
signatureNecessary: True,
signatureSufficient: True,
}),

/**
* Modification is permitted by zkapp proofs or signatures
*/
proofOrSignature: (): Permission => ({
constant: False(),
signatureNecessary: False(),
signatureSufficient: True(),
constant: False,
signatureNecessary: False,
signatureSufficient: True,
}),
};

Expand Down Expand Up @@ -1775,11 +1775,6 @@ type ZkappCommandSigned = {
accountUpdates: (AccountUpdate & { lazyAuthorization?: LazyProof })[];
memo: string;
};
type ZkappCommandProved = {
feePayer: FeePayerUnsigned;
accountUpdates: (AccountUpdate & { lazyAuthorization?: LazySignature })[];
memo: string;
};

const ZkappCommand = {
toPretty(transaction: ZkappCommand) {
Expand Down Expand Up @@ -2003,127 +1998,3 @@ type ZkappPublicInput = {
calls: Field;
};
let ZkappPublicInput = provablePure({ accountUpdate: Field, calls: Field });

async function addMissingProofs(
zkappCommand: ZkappCommand,
{ proofsEnabled = true }
): Promise<{
zkappCommand: ZkappCommandProved;
proofs: (Proof<ZkappPublicInput, Empty> | undefined)[];
}> {
let { feePayer, accountUpdates, memo } = zkappCommand;
// compute proofs serially. in parallel would clash with our global variable
// hacks
let accountUpdatesProved: AccountUpdateProved[] = [];
let proofs: (Proof<ZkappPublicInput, Empty> | undefined)[] = [];
for (let i = 0; i < accountUpdates.length; i++) {
let { accountUpdateProved, proof } = await addProof(
zkappCommand,
i,
proofsEnabled
);
accountUpdatesProved.push(accountUpdateProved);
proofs.push(proof);
}
return {
zkappCommand: { feePayer, accountUpdates: accountUpdatesProved, memo },
proofs,
};
}

async function addProof(
transaction: ZkappCommand,
index: number,
proofsEnabled: boolean
) {
let accountUpdate = transaction.accountUpdates[index];
accountUpdate = AccountUpdate.clone(accountUpdate);

if (accountUpdate.lazyAuthorization?.kind !== 'lazy-proof') {
return {
accountUpdateProved: accountUpdate as AccountUpdateProved,
proof: undefined,
};
}
if (!proofsEnabled) {
Authorization.setProof(accountUpdate, await dummyBase64Proof());
return {
accountUpdateProved: accountUpdate as AccountUpdateProved,
proof: undefined,
};
}

let lazyProof: LazyProof = accountUpdate.lazyAuthorization;
let prover = getZkappProver(lazyProof);
let proverData = { transaction, accountUpdate, index };
let proof = await createZkappProof(prover, lazyProof, proverData);

let accountUpdateProved = Authorization.setProof(
accountUpdate,
Pickles.proofToBase64Transaction(proof.proof)
);
return { accountUpdateProved, proof };
}

async function createZkappProof(
prover: Pickles.Prover,
{
methodName,
args,
previousProofs,
ZkappClass,
memoized,
blindingValue,
}: LazyProof,
{ transaction, accountUpdate, index }: ZkappProverData
): Promise<Proof<ZkappPublicInput, Empty>> {
let publicInput = accountUpdate.toPublicInput();
let publicInputFields = MlFieldConstArray.to(
ZkappPublicInput.toFields(publicInput)
);

let [, , proof] = await zkAppProver.run(
[accountUpdate.publicKey, accountUpdate.tokenId, ...args],
{ transaction, accountUpdate, index },
async () => {
let id = memoizationContext.enter({
memoized,
currentIndex: 0,
blindingValue,
});
try {
return await prover(publicInputFields, MlArray.to(previousProofs));
} catch (err) {
console.error(`Error when proving ${ZkappClass.name}.${methodName}()`);
throw err;
} finally {
memoizationContext.leave(id);
}
}
);

let maxProofsVerified = ZkappClass._maxProofsVerified!;
const Proof = ZkappClass.Proof();
return new Proof({
publicInput,
publicOutput: undefined,
proof,
maxProofsVerified,
});
}

function getZkappProver({ methodName, ZkappClass }: LazyProof) {
if (ZkappClass._provers === undefined)
throw Error(
`Cannot prove execution of ${methodName}(), no prover found. ` +
`Try calling \`await ${ZkappClass.name}.compile()\` first, this will cache provers in the background.`
);
let provers = ZkappClass._provers;
let methodError =
`Error when computing proofs: Method ${methodName} not found. ` +
`Make sure your environment supports decorators, and annotate with \`@method ${methodName}\`.`;
if (ZkappClass._methods === undefined) throw Error(methodError);
let i = ZkappClass._methods.findIndex((m) => m.methodName === methodName);
if (i === -1) throw Error(methodError);
return provers[i];
}
30 changes: 30 additions & 0 deletions src/lib/gates.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@ export {
foreignFieldAdd,
foreignFieldMul,
KimchiGateType,
KimchiGateTypeString,
gateTypeToString,
};

const Gates = {
Expand Down Expand Up @@ -265,3 +267,31 @@ enum KimchiGateType {
Xor16,
Rot64,
}

function gateTypeToString(gate: KimchiGateType) {
return KimchiGateTypeToString[gate];
}

type KimchiGateTypeString =
(typeof KimchiGateTypeToString)[keyof typeof KimchiGateTypeToString];

const KimchiGateTypeToString = {
[KimchiGateType.Zero]: 'Zero',
[KimchiGateType.Generic]: 'Generic',
[KimchiGateType.Poseidon]: 'Poseidon',
[KimchiGateType.CompleteAdd]: 'CompleteAdd',
[KimchiGateType.VarBaseMul]: 'VarBaseMul',
[KimchiGateType.EndoMul]: 'EndoMul',
[KimchiGateType.EndoMulScalar]: 'EndoMulScalar',
[KimchiGateType.Lookup]: 'Lookup',
[KimchiGateType.CairoClaim]: 'CairoClaim',
[KimchiGateType.CairoInstruction]: 'CairoInstruction',
[KimchiGateType.CairoFlags]: 'CairoFlags',
[KimchiGateType.CairoTransition]: 'CairoTransition',
[KimchiGateType.RangeCheck0]: 'RangeCheck0',
[KimchiGateType.RangeCheck1]: 'RangeCheck1',
[KimchiGateType.ForeignFieldAdd]: 'ForeignFieldAdd',
[KimchiGateType.ForeignFieldMul]: 'ForeignFieldMul',
[KimchiGateType.Xor16]: 'Xor16',
[KimchiGateType.Rot64]: 'Rot64',
} as const;
2 changes: 1 addition & 1 deletion src/lib/mina.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ import { Field } from './core.js';
import { UInt32, UInt64 } from './int.js';
import { PrivateKey, PublicKey } from './signature.js';
import {
addMissingProofs,
addMissingSignatures,
FeePayerUnsigned,
ZkappCommand,
Expand Down Expand Up @@ -33,6 +32,7 @@ import {
transactionCommitments,
verifyAccountUpdateSignature,
} from '../mina-signer/src/sign-zkapp-command.js';
import { addMissingProofs } from './mina/zkapp-proof.js';

export {
createTransaction,
Expand Down
Loading
Loading