Skip to content

Commit

Permalink
feat: use latest compatible acvm simulator (#1000)
Browse files Browse the repository at this point in the history
* feat: use latest compatible acvm simulator

* style: fix lint

* refactor: update signatures of oracles

* chore: update artifacts

* refactor: improve callback signatures
  • Loading branch information
sirasistant authored Jul 10, 2023
1 parent 61c27db commit 8d76c86
Show file tree
Hide file tree
Showing 27 changed files with 196 additions and 228 deletions.
2 changes: 1 addition & 1 deletion yarn-project/acir-simulator/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@
"@aztec/merkle-tree": "workspace:^",
"@aztec/noir-contracts": "workspace:^",
"@aztec/types": "workspace:^",
"acvm-simulator": "github:sirasistant/acvm-simulator-wasm#180bc5bec8b79034e9e3a508a959cac3fe801ed1",
"acvm_js": "github:sirasistant/acvm-simulator-wasm#1e84f2b4ecf0d0a7aee37f3d8d568add711b9e93",
"levelup": "^5.1.1",
"memdown": "^6.1.1",
"tslib": "^2.4.0"
Expand Down
77 changes: 39 additions & 38 deletions yarn-project/acir-simulator/src/acvm/acvm.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { AztecAddress } from '@aztec/foundation/aztec-address';
import { EthAddress } from '@aztec/foundation/eth-address';
import { Fr } from '@aztec/foundation/fields';
import { createDebugLogger } from '@aztec/foundation/log';
import { WitnessMap, executeCircuit } from 'acvm-simulator';
import { ForeignCallInput, ForeignCallOutput, WitnessMap, executeCircuit } from 'acvm_js';

/**
* The format for fields on the ACVM.
Expand All @@ -16,42 +16,39 @@ export type ACVMWitness = WitnessMap;
export const ZERO_ACVM_FIELD: ACVMField = `0x${'00'.repeat(Fr.SIZE_IN_BYTES)}`;
export const ONE_ACVM_FIELD: ACVMField = `0x${'00'.repeat(Fr.SIZE_IN_BYTES - 1)}01`;

/**
* The supported oracle names.
*/
type ORACLE_NAMES =
| 'packArguments'
| 'getSecretKey'
| 'getNotes'
| 'getRandomField'
| 'notifyCreatedNote'
| 'notifyNullifiedNote'
| 'callPrivateFunction'
| 'callPublicFunction'
| 'enqueuePublicFunctionCall'
| 'storageRead'
| 'storageWrite'
| 'createCommitment'
| 'createL2ToL1Message'
| 'createNullifier'
| 'getCommitment'
| 'getL1ToL2Message'
| 'emitEncryptedLog'
| 'emitUnencryptedLog'
| 'debugLog';

/**
* A type that does not require all keys to be present.
*/
type PartialRecord<K extends keyof any, T> = Partial<Record<K, T>>;

/**
* The callback interface for the ACIR.
*/
export interface ACIRCallback {
/**
* Oracle call used to pack a set of arguments for the execution
*/
packArguments(params: ACVMField[]): Promise<ACVMField[]>;
getSecretKey(params: ACVMField[]): Promise<[ACVMField]>;
getNotes(params: ACVMField[]): Promise<ACVMField[]>;
getRandomField(): Promise<[ACVMField]>;
notifyCreatedNote(params: ACVMField[]): Promise<[ACVMField]>;
notifyNullifiedNote(params: ACVMField[]): Promise<[ACVMField]>;
callPrivateFunction(params: ACVMField[]): Promise<ACVMField[]>;
callPublicFunction(params: ACVMField[]): Promise<ACVMField[]>;
enqueuePublicFunctionCall(params: ACVMField[]): Promise<ACVMField[]>;
storageRead(params: ACVMField[]): Promise<ACVMField[]>;
storageWrite(params: ACVMField[]): Promise<ACVMField[]>;
createCommitment(params: ACVMField[]): Promise<[ACVMField]>;
createL2ToL1Message(params: ACVMField[]): Promise<[ACVMField]>;
createNullifier(params: ACVMField[]): Promise<[ACVMField]>;
getCommitment(params: ACVMField[]): Promise<ACVMField[]>;
getL1ToL2Message(params: ACVMField[]): Promise<ACVMField[]>;
/**
* Oracle call used to emit an encrypted log.
*/
emitEncryptedLog: (params: ACVMField[]) => Promise<ACVMField[]>;
/**
* Oracle call used to emit an unencrypted log.
*/
emitUnencryptedLog: (params: ACVMField[]) => Promise<string[]>;
/**
* Debugging utility for printing out info from Noir (i.e. console.log).
*/
debugLog: (params: ACVMField[]) => Promise<ACVMField[]>;
}
export type ACIRCallback = PartialRecord<ORACLE_NAMES, (...args: ForeignCallInput[]) => Promise<ForeignCallOutput>>;

/**
* The result of executing an ACIR.
Expand All @@ -72,12 +69,16 @@ export async function acvm(
callback: ACIRCallback,
): Promise<ACIRExecutionResult> {
const logger = createDebugLogger('aztec:simulator:acvm');
const partialWitness = await executeCircuit(acir, initialWitness, async (name: string, args: string[]) => {
const partialWitness = await executeCircuit(acir, initialWitness, async (name: string, args: ForeignCallInput[]) => {
try {
logger(`Oracle callback ${name}`);
if (!(name in callback)) throw new Error(`Callback ${name} not found`);
const result = await callback[name as keyof ACIRCallback](args);
return result;
const oracleFunction = callback[name as ORACLE_NAMES];
if (!oracleFunction) {
throw new Error(`Callback ${name} not found`);
}

const result = await oracleFunction.call(callback, ...args);
return [result];
} catch (err: any) {
logger(`Error in ACVM callback ${name}: ${err.message ?? err ?? 'Unknown'}`);
throw err;
Expand Down
2 changes: 1 addition & 1 deletion yarn-project/acir-simulator/src/acvm/deserialize.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ import {
import { AztecAddress } from '@aztec/foundation/aztec-address';
import { EthAddress } from '@aztec/foundation/eth-address';
import { Fr, Point } from '@aztec/foundation/fields';
import { getReturnWitness } from 'acvm-simulator';
import { getReturnWitness } from 'acvm_js';
import { ACVMField, ACVMWitness, fromACVMField } from './acvm.js';

// Utilities to read TS classes from ACVM Field arrays
Expand Down
39 changes: 25 additions & 14 deletions yarn-project/acir-simulator/src/client/client_execution_context.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ import { AztecAddress } from '@aztec/foundation/aztec-address';
import { Fr } from '@aztec/foundation/fields';
import {
ACVMField,
ACVMFieldsReader,
fromACVMField,
toACVMField,
toAcvmCommitmentLoadOracleInputs,
Expand Down Expand Up @@ -53,21 +52,33 @@ export class ClientTxExecutionContext {
* to the private kernel.
*
* @param contractAddress - The contract address.
* @param fields - An array of ACVM fields.
* @param storageSlot - The storage slot.
* @param noteSize - The note size.
* @param sortBy - The sort by fields.
* @param sortOrder - The sort order fields.
* @param limit - The limit.
* @param offset - The offset.
* @param returnSize - The return size.
* @returns An array of ACVM fields for the note count and the requested note preimages,
* and another array of indices corresponding to each note
*/
public async getNotes(contractAddress: AztecAddress, fields: ACVMField[]) {
const reader = new ACVMFieldsReader(fields);
const storageSlot = reader.readField();
const noteSize = reader.readNumber();
const sortBy = reader.readNumberArray(noteSize);
const sortOrder = reader.readNumberArray(noteSize);
const limit = reader.readNumber();
const offset = reader.readNumber();
const returnSize = reader.readNumber();

const { count, notes } = await this.db.getNotes(contractAddress, storageSlot, sortBy, sortOrder, limit, offset);
public async getNotes(
contractAddress: AztecAddress,
storageSlot: ACVMField,
sortBy: ACVMField[],
sortOrder: ACVMField[],
limit: ACVMField,
offset: ACVMField,
returnSize: ACVMField,
) {
const { count, notes } = await this.db.getNotes(
contractAddress,
fromACVMField(storageSlot),
sortBy.map(field => +field),
sortOrder.map(field => +field),
+limit,
+offset,
);
const preimages = notes.flatMap(({ preimage }) => preimage);

// TODO(dbanks12): https://github.com/AztecProtocol/aztec-packages/issues/779
Expand All @@ -77,7 +88,7 @@ export class ClientTxExecutionContext {
const indices = notes.map(({ index }) => index).filter(index => index != BigInt(-1));
this.readRequestCommitmentIndices.push(...indices);

const paddedZeros = Array(returnSize - 1 - preimages.length).fill(Fr.ZERO);
const paddedZeros = Array(+returnSize - 1 - preimages.length).fill(Fr.ZERO);
return [count, preimages, paddedZeros].flat().map(f => toACVMField(f));
}

Expand Down
32 changes: 14 additions & 18 deletions yarn-project/acir-simulator/src/client/debug.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { ForeignCallInput } from 'acvm_js';
import { ACVMField } from '../acvm/index.js';

/**
Expand Down Expand Up @@ -47,28 +48,23 @@ function applyStringFormatting(formatStr: string, args: ACVMField[]): string {
/**
* Convert an array of ACVMFields from ACVM to a formatted string.
*
* @param fields - either a single field to be printed, or a string to be formatted.
* When it is a string to be formatted:
* The last entry in `fields` is `numArgs` (the number of formatting
* args). The `formatArgs` occupy the end of the `fields` array,
* excluding that last entry (`numArgs`). The message string `msg`
* takes up the remaining entries at the start of the `fields` array.
* @param parameters - either one parameter representing a simple field, or two parameters when
* It's a message without args or three parameters when it's a message with arguments.
*
* @returns formatted string
*/
export function fieldsToFormattedStr(fields: ACVMField[]): string {
if (fields.length === 1) {
return `${fields[0]}`;
} else {
const numArgs = Number(fields[fields.length - 1]);
const msgLen = fields.length - 1 - numArgs;

const msgFields = fields.slice(0, msgLen);
const formatArgs = fields.slice(msgLen, fields.length - 1);
export function oracleDebugCallToFormattedStr(parameters: ForeignCallInput[]): string {
if (parameters.length === 1) {
return `${parameters[0][0]}`;
}

const msg = acvmFieldMessageToString(msgFields);
const formattedMsg = applyStringFormatting(msg, formatArgs);
let formatArgs: string[] = [];

return formattedMsg;
if (parameters.length > 2) {
formatArgs = parameters[1];
}

const formattedMsg = applyStringFormatting(acvmFieldMessageToString(parameters[0]), formatArgs);

return formattedMsg;
}
57 changes: 23 additions & 34 deletions yarn-project/acir-simulator/src/client/private_execution.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,6 @@ import { FunctionL2Logs, NotePreimage, NoteSpendingInfo } from '@aztec/types';
import { decodeReturnValues } from '../abi_coder/decoder.js';
import { extractPublicInputs, frToAztecAddress, frToSelector } from '../acvm/deserialize.js';
import {
ACVMField,
ZERO_ACVM_FIELD,
acvm,
convertACVMFieldToBuffer,
Expand All @@ -31,11 +30,7 @@ import {
} from '../acvm/index.js';
import { ExecutionResult, NewNoteData, NewNullifierData } from '../index.js';
import { ClientTxExecutionContext } from './client_execution_context.js';
import { fieldsToFormattedStr } from './debug.js';

const notAvailable = () => {
return Promise.reject(new Error(`Not available for private function execution`));
};
import { oracleDebugCallToFormattedStr } from './debug.js';

/**
* The private function execution class.
Expand Down Expand Up @@ -73,10 +68,10 @@ export class PrivateFunctionExecution {
const unencryptedLogs = new FunctionL2Logs([]);

const { partialWitness } = await acvm(acir, initialWitness, {
packArguments: async (args: ACVMField[]) => {
return [toACVMField(await this.context.packedArgsCache.pack(args.map(fromACVMField)))];
packArguments: async args => {
return toACVMField(await this.context.packedArgsCache.pack(args.map(fromACVMField)));
},
getSecretKey: async ([ownerX, ownerY]: ACVMField[]) => [
getSecretKey: async ([ownerX], [ownerY]) =>
toACVMField(
await this.context.db.getSecretKey(
this.contractAddress,
Expand All @@ -86,25 +81,25 @@ export class PrivateFunctionExecution {
),
),
),
],
getNotes: (fields: ACVMField[]) => this.context.getNotes(this.contractAddress, fields),
getRandomField: () => Promise.resolve([toACVMField(Fr.random())]),
notifyCreatedNote: ([storageSlot, ...acvmPreimage]: ACVMField[]) => {
getNotes: ([slot], sortBy, sortOrder, [limit], [offset], [returnSize]) =>
this.context.getNotes(this.contractAddress, slot, sortBy, sortOrder, limit, offset, returnSize),
getRandomField: () => Promise.resolve(toACVMField(Fr.random())),
notifyCreatedNote: ([storageSlot], acvmPreimage) => {
newNotePreimages.push({
storageSlot: fromACVMField(storageSlot),
preimage: acvmPreimage.map(f => fromACVMField(f)),
});
return Promise.resolve([ZERO_ACVM_FIELD]);
return Promise.resolve(ZERO_ACVM_FIELD);
},
notifyNullifiedNote: ([slot, nullifier, ...acvmPreimage]: ACVMField[]) => {
notifyNullifiedNote: ([slot], [nullifier], acvmPreimage) => {
newNullifiers.push({
preimage: acvmPreimage.map(f => fromACVMField(f)),
storageSlot: fromACVMField(slot),
nullifier: fromACVMField(nullifier),
});
return Promise.resolve([ZERO_ACVM_FIELD]);
return Promise.resolve(ZERO_ACVM_FIELD);
},
callPrivateFunction: async ([acvmContractAddress, acvmFunctionSelector, acvmArgsHash]) => {
callPrivateFunction: async ([acvmContractAddress], [acvmFunctionSelector], [acvmArgsHash]) => {
const contractAddress = fromACVMField(acvmContractAddress);
const functionSelector = fromACVMField(acvmFunctionSelector);
this.log(
Expand All @@ -123,15 +118,15 @@ export class PrivateFunctionExecution {

return toAcvmCallPrivateStackItem(childExecutionResult.callStackItem);
},
getL1ToL2Message: ([msgKey]: ACVMField[]) => {
getL1ToL2Message: ([msgKey]) => {
return this.context.getL1ToL2Message(fromACVMField(msgKey));
},
getCommitment: ([commitment]: ACVMField[]) => this.context.getCommitment(this.contractAddress, commitment),
debugLog: (fields: ACVMField[]) => {
this.log(fieldsToFormattedStr(fields));
return Promise.resolve([ZERO_ACVM_FIELD]);
getCommitment: ([commitment]) => this.context.getCommitment(this.contractAddress, commitment),
debugLog: (...args) => {
this.log(oracleDebugCallToFormattedStr(args));
return Promise.resolve(ZERO_ACVM_FIELD);
},
enqueuePublicFunctionCall: async ([acvmContractAddress, acvmFunctionSelector, acvmArgsHash]) => {
enqueuePublicFunctionCall: async ([acvmContractAddress], [acvmFunctionSelector], [acvmArgsHash]) => {
const enqueuedRequest = await this.enqueuePublicFunctionCall(
frToAztecAddress(fromACVMField(acvmContractAddress)),
frToSelector(fromACVMField(acvmFunctionSelector)),
Expand All @@ -143,20 +138,14 @@ export class PrivateFunctionExecution {
enqueuedPublicFunctionCalls.push(enqueuedRequest);
return toAcvmEnqueuePublicFunctionResult(enqueuedRequest);
},
storageRead: notAvailable,
storageWrite: notAvailable,
createCommitment: notAvailable,
createL2ToL1Message: notAvailable,
createNullifier: notAvailable,
callPublicFunction: notAvailable,
emitUnencryptedLog: ([...args]: ACVMField[]) => {
emitUnencryptedLog: message => {
// https://github.com/AztecProtocol/aztec-packages/issues/885
const log = Buffer.concat(args.map(charBuffer => convertACVMFieldToBuffer(charBuffer).subarray(-1)));
const log = Buffer.concat(message.map(charBuffer => convertACVMFieldToBuffer(charBuffer).subarray(-1)));
unencryptedLogs.logs.push(log);
this.log(`Emitted unencrypted log: "${log.toString('ascii')}"`);
return Promise.resolve([ZERO_ACVM_FIELD]);
return Promise.resolve(ZERO_ACVM_FIELD);
},
emitEncryptedLog: ([acvmContractAddress, acvmStorageSlot, ownerX, ownerY, ...acvmPreimage]: ACVMField[]) => {
emitEncryptedLog: ([acvmContractAddress], [acvmStorageSlot], [ownerX], [ownerY], acvmPreimage) => {
const contractAddress = AztecAddress.fromBuffer(convertACVMFieldToBuffer(acvmContractAddress));
const storageSlot = fromACVMField(acvmStorageSlot);
const preimage = acvmPreimage.map(f => fromACVMField(f));
Expand All @@ -172,7 +161,7 @@ export class PrivateFunctionExecution {

encryptedLogs.logs.push(encryptedNotePreimage);

return Promise.resolve([ZERO_ACVM_FIELD]);
return Promise.resolve(ZERO_ACVM_FIELD);
},
});

Expand Down
Loading

0 comments on commit 8d76c86

Please # to comment.