Skip to content

Commit

Permalink
feat: nullifier key validation (#4176)
Browse files Browse the repository at this point in the history
Please read [contributing guidelines](CONTRIBUTING.md) and remove this
line.
  • Loading branch information
LeilaWang authored Jan 26, 2024
1 parent 84967b2 commit 1c72c0d
Show file tree
Hide file tree
Showing 47 changed files with 3,895 additions and 1,755 deletions.
4 changes: 3 additions & 1 deletion l1-contracts/src/core/libraries/ConstantsGen.sol
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ library Constants {
uint256 internal constant MAX_PUBLIC_DATA_UPDATE_REQUESTS_PER_CALL = 16;
uint256 internal constant MAX_PUBLIC_DATA_READS_PER_CALL = 16;
uint256 internal constant MAX_READ_REQUESTS_PER_CALL = 32;
uint256 internal constant MAX_NULLIFIER_KEY_VALIDATION_REQUESTS_PER_CALL = 1;
uint256 internal constant MAX_NEW_COMMITMENTS_PER_TX = 64;
uint256 internal constant MAX_NEW_NULLIFIERS_PER_TX = 64;
uint256 internal constant MAX_PRIVATE_CALL_STACK_LENGTH_PER_TX = 8;
Expand All @@ -34,6 +35,7 @@ library Constants {
uint256 internal constant MAX_NEW_CONTRACTS_PER_TX = 1;
uint256 internal constant MAX_OPTIONALLY_REVEALED_DATA_LENGTH_PER_TX = 4;
uint256 internal constant MAX_READ_REQUESTS_PER_TX = 128;
uint256 internal constant MAX_NULLIFIER_KEY_VALIDATION_REQUESTS_PER_TX = 4;
uint256 internal constant NUM_ENCRYPTED_LOGS_HASHES_PER_TX = 1;
uint256 internal constant NUM_UNENCRYPTED_LOGS_HASHES_PER_TX = 1;
uint256 internal constant NUMBER_OF_L1_L2_MESSAGES_PER_ROLLUP = 16;
Expand Down Expand Up @@ -76,7 +78,7 @@ library Constants {
uint256 internal constant CONTRACT_STORAGE_READ_LENGTH = 2;
uint256 internal constant PUBLIC_CIRCUIT_PUBLIC_INPUTS_LENGTH = 190;
uint256 internal constant GET_NOTES_ORACLE_RETURN_LENGTH = 674;
uint256 internal constant CALL_PRIVATE_FUNCTION_RETURN_SIZE = 195;
uint256 internal constant CALL_PRIVATE_FUNCTION_RETURN_SIZE = 199;
uint256 internal constant PUBLIC_CIRCUIT_PUBLIC_INPUTS_HASH_INPUT_LENGTH = 87;
uint256 internal constant PRIVATE_CIRCUIT_PUBLIC_INPUTS_HASH_INPUT_LENGTH = 177;
uint256 internal constant COMMITMENTS_NUM_BYTES_PER_BASE_ROLLUP = 2048;
Expand Down
146 changes: 24 additions & 122 deletions yarn-project/acir-simulator/src/acvm/deserialize.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,16 +4,17 @@ import {
ContractDeploymentData,
ContractStorageRead,
ContractStorageUpdateRequest,
FunctionSelector,
MAX_NEW_COMMITMENTS_PER_CALL,
MAX_NEW_L2_TO_L1_MSGS_PER_CALL,
MAX_NEW_NULLIFIERS_PER_CALL,
MAX_NULLIFIER_KEY_VALIDATION_REQUESTS_PER_CALL,
MAX_PRIVATE_CALL_STACK_LENGTH_PER_CALL,
MAX_PUBLIC_CALL_STACK_LENGTH_PER_CALL,
MAX_PUBLIC_DATA_READS_PER_CALL,
MAX_PUBLIC_DATA_UPDATE_REQUESTS_PER_CALL,
MAX_READ_REQUESTS_PER_CALL,
NUM_FIELDS_PER_SHA256,
NullifierKeyValidationRequest,
PrivateCircuitPublicInputs,
PublicCircuitPublicInputs,
RETURN_VALUES_LENGTH,
Expand All @@ -23,40 +24,19 @@ import {
import { AztecAddress } from '@aztec/foundation/aztec-address';
import { EthAddress } from '@aztec/foundation/eth-address';
import { Fr, Point } from '@aztec/foundation/fields';
import { Tuple } from '@aztec/foundation/serialize';
import { FieldReader, Tuple } from '@aztec/foundation/serialize';

import { getReturnWitness } from '@noir-lang/acvm_js';

import { ACVMField, ACVMWitness } from './acvm_types.js';

/**
* Converts an ACVM field to a Buffer.
* @param field - The ACVM field to convert.
* @returns The Buffer.
*/
export function convertACVMFieldToBuffer(field: ACVMField): Buffer {
return Buffer.from(field.slice(2), 'hex');
}

/**
* Converts an ACVM field to a Fr.
* @param field - The ACVM field to convert.
* @returns The Fr.
*/
export function fromACVMField(field: ACVMField): Fr {
return Fr.fromBuffer(convertACVMFieldToBuffer(field));
}

// Utilities to read TS classes from ACVM Field arrays
// In the order that the ACVM provides them

/**
* Converts a field to an Aztec address.
* @param fr - The field to convert.
* @returns The Aztec address.
*/
export function frToAztecAddress(fr: Fr): AztecAddress {
return new AztecAddress(fr.toBuffer());
return Fr.fromBuffer(Buffer.from(field.slice(2), 'hex'));
}

/**
Expand All @@ -68,88 +48,24 @@ export function frToNumber(fr: Fr): number {
return Number(fr.value);
}

/**
* Converts a field to a boolean.
* @param fr - The field to convert.
* @returns The boolean.
*/
export function frToBoolean(fr: Fr): boolean {
const buf = fr.toBuffer();
return buf[buf.length - 1] !== 0;
}

/**
* Extracts the return fields of a given partial witness.
* @param acir - The bytecode of the function.
* @param partialWitness - The witness to extract from.
* @returns The return values.
*/
export function extractReturnWitness(acir: Buffer, partialWitness: ACVMWitness): ACVMField[] {
export function extractReturnWitness(acir: Buffer, partialWitness: ACVMWitness): Fr[] {
const returnWitness = getReturnWitness(acir, partialWitness);
const sortedKeys = [...returnWitness.keys()].sort((a, b) => a - b);
return sortedKeys.map(key => returnWitness.get(key)!);
return sortedKeys.map(key => returnWitness.get(key)!).map(fromACVMField);
}

/**
* A utility reader for the public inputs of the ACVM generated partial witness.
* Create a reader for the public inputs of the ACVM generated partial witness.
*/
export class PublicInputsReader {
private publicInputs: ACVMField[];

constructor(witness: ACVMWitness, acir: Buffer) {
this.publicInputs = extractReturnWitness(acir, witness);
}

/**
* Reads a field from the public inputs.
* @returns The field.
*/
public readField(): Fr {
const acvmField = this.publicInputs.shift();
if (!acvmField) {
throw new Error('Not enough public inputs');
}
return fromACVMField(acvmField);
}

/**
* Reads an array of fields from the public inputs.
* @param length - The length of the array.
* @returns The array of fields.
*/
public readFieldArray<N extends number>(length: N): Tuple<Fr, N> {
const array: Fr[] = [];
for (let i = 0; i < length; i++) {
array.push(this.readField());
}
return array as Tuple<Fr, N>;
}

/**
* Reads an array of SideEffects from the public inputs.
* @param length - The length of the array.
* @returns The array of SideEffects.
*/
public readSideEffectArray<N extends number>(length: N): Tuple<SideEffect, N> {
const array: SideEffect[] = [];
for (let i = 0; i < length; i++) {
array.push(new SideEffect(this.readField(), this.readField()));
}
return array as Tuple<SideEffect, N>;
}

/**
* Reads an array of SideEffectLinkedToNoteHashes from the public inputs.
* @param length - The length of the array.
* @returns The array of SideEffectLinkedToNoteHashes.
*/
public readSideEffectLinkedToNoteHashArray<N extends number>(length: N): Tuple<SideEffectLinkedToNoteHash, N> {
const array: SideEffectLinkedToNoteHash[] = [];
for (let i = 0; i < length; i++) {
array.push(new SideEffectLinkedToNoteHash(this.readField(), this.readField(), this.readField()));
}
return array as Tuple<SideEffectLinkedToNoteHash, N>;
}
function createPublicInputsReader(witness: ACVMWitness, acir: Buffer) {
const fields = extractReturnWitness(acir, witness);
return new FieldReader(fields);
}

/**
Expand All @@ -162,24 +78,18 @@ export function extractPrivateCircuitPublicInputs(
partialWitness: ACVMWitness,
acir: Buffer,
): PrivateCircuitPublicInputs {
const witnessReader = new PublicInputsReader(partialWitness, acir);

const callContext = new CallContext(
frToAztecAddress(witnessReader.readField()),
frToAztecAddress(witnessReader.readField()),
witnessReader.readField(),
FunctionSelector.fromField(witnessReader.readField()),
frToBoolean(witnessReader.readField()),
frToBoolean(witnessReader.readField()),
frToBoolean(witnessReader.readField()),
frToNumber(witnessReader.readField()),
);
const witnessReader = createPublicInputsReader(partialWitness, acir);

const callContext = witnessReader.readObject(CallContext);
const argsHash = witnessReader.readField();
const returnValues = witnessReader.readFieldArray(RETURN_VALUES_LENGTH);
const readRequests = witnessReader.readSideEffectArray(MAX_READ_REQUESTS_PER_CALL);
const newCommitments = witnessReader.readSideEffectArray(MAX_NEW_COMMITMENTS_PER_CALL);
const newNullifiers = witnessReader.readSideEffectLinkedToNoteHashArray(MAX_NEW_NULLIFIERS_PER_CALL);
const readRequests = witnessReader.readArray(MAX_READ_REQUESTS_PER_CALL, SideEffect);
const nullifierKeyValidationRequests = witnessReader.readArray(
MAX_NULLIFIER_KEY_VALIDATION_REQUESTS_PER_CALL,
NullifierKeyValidationRequest,
);
const newCommitments = witnessReader.readArray(MAX_NEW_COMMITMENTS_PER_CALL, SideEffect);
const newNullifiers = witnessReader.readArray(MAX_NEW_NULLIFIERS_PER_CALL, SideEffectLinkedToNoteHash);
const privateCallStack = witnessReader.readFieldArray(MAX_PRIVATE_CALL_STACK_LENGTH_PER_CALL);
const publicCallStack = witnessReader.readFieldArray(MAX_PUBLIC_CALL_STACK_LENGTH_PER_CALL);
const newL2ToL1Msgs = witnessReader.readFieldArray(MAX_NEW_L2_TO_L1_MSGS_PER_CALL);
Expand Down Expand Up @@ -217,6 +127,7 @@ export function extractPrivateCircuitPublicInputs(
argsHash,
returnValues,
readRequests,
nullifierKeyValidationRequests,
newCommitments,
newNullifiers,
privateCallStack,
Expand All @@ -241,18 +152,9 @@ export function extractPrivateCircuitPublicInputs(
* @returns The public inputs.
*/
export function extractPublicCircuitPublicInputs(partialWitness: ACVMWitness, acir: Buffer): PublicCircuitPublicInputs {
const witnessReader = new PublicInputsReader(partialWitness, acir);
const witnessReader = createPublicInputsReader(partialWitness, acir);

const callContext = new CallContext(
frToAztecAddress(witnessReader.readField()),
frToAztecAddress(witnessReader.readField()),
witnessReader.readField(),
FunctionSelector.fromField(witnessReader.readField()),
frToBoolean(witnessReader.readField()),
frToBoolean(witnessReader.readField()),
frToBoolean(witnessReader.readField()),
frToNumber(witnessReader.readField()),
);
const callContext = witnessReader.readObject(CallContext);

const argsHash = witnessReader.readField();
const returnValues = witnessReader.readFieldArray(RETURN_VALUES_LENGTH);
Expand All @@ -275,8 +177,8 @@ export function extractPublicCircuitPublicInputs(partialWitness: ACVMWitness, ac
}

const publicCallStack = witnessReader.readFieldArray(MAX_PUBLIC_CALL_STACK_LENGTH_PER_CALL);
const newCommitments = witnessReader.readSideEffectArray(MAX_NEW_COMMITMENTS_PER_CALL);
const newNullifiers = witnessReader.readSideEffectLinkedToNoteHashArray(MAX_NEW_NULLIFIERS_PER_CALL);
const newCommitments = witnessReader.readArray(MAX_NEW_COMMITMENTS_PER_CALL, SideEffect);
const newNullifiers = witnessReader.readArray(MAX_NEW_NULLIFIERS_PER_CALL, SideEffectLinkedToNoteHash);
const newL2ToL1Msgs = witnessReader.readFieldArray(MAX_NEW_L2_TO_L1_MSGS_PER_CALL);

const unencryptedLogsHash = witnessReader.readFieldArray(NUM_FIELDS_PER_SHA256);
Expand Down
7 changes: 4 additions & 3 deletions yarn-project/acir-simulator/src/acvm/serialize.ts
Original file line number Diff line number Diff line change
Expand Up @@ -143,9 +143,10 @@ export function toACVMPublicInputs(publicInputs: PrivateCircuitPublicInputs): AC
toACVMField(publicInputs.argsHash),

...publicInputs.returnValues.map(toACVMField),
...publicInputs.readRequests.flatMap(x => x.toFieldArray()).map(toACVMField),
...publicInputs.newCommitments.flatMap(x => x.toFieldArray()).map(toACVMField),
...publicInputs.newNullifiers.flatMap(x => x.toFieldArray()).map(toACVMField),
...publicInputs.readRequests.flatMap(x => x.toFields()).map(toACVMField),
...publicInputs.nullifierKeyValidationRequests.flatMap(x => x.toFields()).map(toACVMField),
...publicInputs.newCommitments.flatMap(x => x.toFields()).map(toACVMField),
...publicInputs.newNullifiers.flatMap(x => x.toFields()).map(toACVMField),
...publicInputs.privateCallStackHashes.map(toACVMField),
...publicInputs.publicCallStackHashes.map(toACVMField),
...publicInputs.newL2ToL1Msgs.map(toACVMField),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import { Fr } from '@aztec/foundation/fields';
import { createDebugLogger } from '@aztec/foundation/log';

import { extractReturnWitness } from '../acvm/deserialize.js';
import { ACVMField, Oracle, acvm, extractCallStack, fromACVMField, toACVMWitness } from '../acvm/index.js';
import { Oracle, acvm, extractCallStack, toACVMWitness } from '../acvm/index.js';
import { ExecutionError } from '../common/errors.js';
import { AcirSimulator } from '../index.js';
import { ViewDataOracle } from './view_data_oracle.js';
Expand Down Expand Up @@ -43,6 +43,5 @@ export async function executeUnconstrainedFunction(
);
});

const returnValues: ACVMField[] = extractReturnWitness(acir, partialWitness);
return decodeReturnValues(artifact, returnValues.map(fromACVMField));
return decodeReturnValues(artifact, extractReturnWitness(acir, partialWitness));
}
Loading

0 comments on commit 1c72c0d

Please sign in to comment.