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
Changes from 1 commit
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
Prev Previous commit
Next Next commit
move zkapp proving logic to separate file
mitschabaude committed Jan 10, 2024

Unverified

This commit is not signed, but one or more authors requires that any commit attributed to them is signed.
commit b3a3479726ff34d882e5201a1a7164cbe6d8a4ef
133 changes: 3 additions & 130 deletions src/lib/account_update.ts
Original file line number Diff line number Diff line change
@@ -47,7 +47,6 @@ export {
FeePayerUnsigned,
ZkappCommand,
addMissingSignatures,
addMissingProofs,
ZkappStateLength,
Events,
Actions,
@@ -57,8 +56,11 @@ export {
createChildAccountUpdate,
AccountUpdatesLayout,
zkAppProver,
ZkappProverData,
SmartContractContext,
dummySignature,
LazySignature,
LazyProof,
};

const ZkappStateLength = 8;
@@ -1748,11 +1750,6 @@ type ZkappCommandSigned = {
accountUpdates: (AccountUpdate & { lazyAuthorization?: LazyProof })[];
memo: string;
};
type ZkappCommandProved = {
feePayer: FeePayerUnsigned;
accountUpdates: (AccountUpdate & { lazyAuthorization?: LazySignature })[];
memo: string;
};

const ZkappCommand = {
toPretty(transaction: ZkappCommand) {
@@ -1976,127 +1973,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];
}
2 changes: 1 addition & 1 deletion src/lib/mina.ts
Original file line number Diff line number Diff line change
@@ -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,
@@ -33,6 +32,7 @@ import {
transactionCommitments,
verifyAccountUpdateSignature,
} from '../mina-signer/src/sign-zkapp-command.js';
import { addMissingProofs } from './mina/zkapp-proof.js';

export {
createTransaction,
152 changes: 152 additions & 0 deletions src/lib/mina/zkapp-proof.ts
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The code in this new file consists of:

  • Code that handles zkApp proof creation, which was moved here from account_update.ts
  • New method debugInconsistentConstraints(), which is triggered after encountering certain errors in the zkApp prover. It then runs the new runCircuit() twice, once in compile and once in prover mode, to identify any mismatch in the generated constraints. If a mismatch is found, we throw a very helpful error that points to it. Otherwise, we throw the original prover error.

Original file line number Diff line number Diff line change
@@ -0,0 +1,152 @@
import { Pickles } from '../../snarky.js';
import {
AccountUpdate,
Authorization,
FeePayerUnsigned,
LazyProof,
LazySignature,
ZkappCommand,
ZkappProverData,
ZkappPublicInput,
zkAppProver,
} from '../account_update.js';
import { MlArray } from '../ml/base.js';
import { MlFieldConstArray } from '../ml/fields.js';
import { Empty, Proof, dummyBase64Proof } from '../proof_system.js';
import { memoizationContext } from '../provable.js';

export { addMissingProofs };

type AccountUpdateProved = AccountUpdate & {
lazyAuthorization?: LazySignature;
};

type ZkappCommandProved = {
feePayer: FeePayerUnsigned;
accountUpdates: AccountUpdateProved[];
memo: string;
};

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];
}