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

feat: nullifier key validation #4176

Merged
merged 11 commits into from
Jan 26, 2024
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