Skip to content

Commit

Permalink
refactor: PublicCircuitPublicInputs and `PrivateCircuitPublicInputs…
Browse files Browse the repository at this point in the history
…` cleanup (#4360)

Cleanup of `PublicCircuitPublicInputs` serialization + more tests
  • Loading branch information
benesjan authored Feb 1, 2024
1 parent 433babd commit b92d690
Show file tree
Hide file tree
Showing 10 changed files with 274 additions and 302 deletions.
169 changes: 2 additions & 167 deletions yarn-project/acir-simulator/src/acvm/deserialize.ts
Original file line number Diff line number Diff line change
@@ -1,31 +1,4 @@
import {
CallContext,
ContractDeploymentData,
ContractStorageRead,
ContractStorageUpdateRequest,
HEADER_LENGTH,
Header,
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,
SideEffect,
SideEffectLinkedToNoteHash,
} from '@aztec/circuits.js';
import { AztecAddress } from '@aztec/foundation/aztec-address';
import { EthAddress } from '@aztec/foundation/eth-address';
import { Fr, Point } from '@aztec/foundation/fields';
import { FieldReader, Tuple } from '@aztec/foundation/serialize';
import { Fr } from '@aztec/foundation/fields';

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

Expand All @@ -44,6 +17,7 @@ export function fromACVMField(field: ACVMField): Fr {
* Converts a field to a number.
* @param fr - The field to convert.
* @returns The number.
* TODO(#4102): Nuke this once block number is big int.
*/
export function frToNumber(fr: Fr): number {
return Number(fr.value);
Expand All @@ -60,142 +34,3 @@ export function extractReturnWitness(acir: Buffer, partialWitness: ACVMWitness):
const sortedKeys = [...returnWitness.keys()].sort((a, b) => a - b);
return sortedKeys.map(key => returnWitness.get(key)!).map(fromACVMField);
}

/**
* Create a reader for the public inputs of the ACVM generated partial witness.
*/
function createPublicInputsReader(witness: ACVMWitness, acir: Buffer) {
const fields = extractReturnWitness(acir, witness);
return new FieldReader(fields);
}

/**
* Extracts the public inputs from the ACVM generated partial witness.
* @param partialWitness - The partial witness.
* @param acir - The ACIR bytecode.
* @returns The public inputs.
*/
export function extractPrivateCircuitPublicInputs(
partialWitness: ACVMWitness,
acir: Buffer,
): PrivateCircuitPublicInputs {
const witnessReader = createPublicInputsReader(partialWitness, acir);

const callContext = witnessReader.readObject(CallContext);
const argsHash = witnessReader.readField();
const returnValues = witnessReader.readFieldArray(RETURN_VALUES_LENGTH);
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);
const endSideEffectCounter = witnessReader.readField();

const encryptedLogsHash = witnessReader.readFieldArray(NUM_FIELDS_PER_SHA256);
const unencryptedLogsHash = witnessReader.readFieldArray(NUM_FIELDS_PER_SHA256);
const encryptedLogPreimagesLength = witnessReader.readField();
const unencryptedLogPreimagesLength = witnessReader.readField();

const header = Header.fromFields(witnessReader.readFieldArray(HEADER_LENGTH));

const contractDeploymentData = new ContractDeploymentData(
new Point(witnessReader.readField(), witnessReader.readField()),
witnessReader.readField(),
witnessReader.readField(),
witnessReader.readField(),
EthAddress.fromField(witnessReader.readField()),
);

const chainId = witnessReader.readField();
const version = witnessReader.readField();

return new PrivateCircuitPublicInputs(
callContext,
argsHash,
returnValues,
readRequests,
nullifierKeyValidationRequests,
newCommitments,
newNullifiers,
privateCallStack,
publicCallStack,
newL2ToL1Msgs,
endSideEffectCounter,
encryptedLogsHash,
unencryptedLogsHash,
encryptedLogPreimagesLength,
unencryptedLogPreimagesLength,
header,
contractDeploymentData,
chainId,
version,
);
}

/**
* Extracts the public circuit public inputs from the ACVM generated partial witness.
* @param partialWitness - The partial witness.
* @param acir - The ACIR bytecode.
* @returns The public inputs.
*/
export function extractPublicCircuitPublicInputs(partialWitness: ACVMWitness, acir: Buffer): PublicCircuitPublicInputs {
const witnessReader = createPublicInputsReader(partialWitness, acir);

const callContext = witnessReader.readObject(CallContext);

const argsHash = witnessReader.readField();
const returnValues = witnessReader.readFieldArray(RETURN_VALUES_LENGTH);

const contractStorageUpdateRequests = new Array(MAX_PUBLIC_DATA_UPDATE_REQUESTS_PER_CALL).fill(
ContractStorageUpdateRequest.empty(),
);
for (let i = 0; i < MAX_PUBLIC_DATA_UPDATE_REQUESTS_PER_CALL; i++) {
const request = new ContractStorageUpdateRequest(
witnessReader.readField(),
witnessReader.readField(),
witnessReader.readField(),
);
contractStorageUpdateRequests[i] = request;
}
const contractStorageReads = new Array(MAX_PUBLIC_DATA_READS_PER_CALL).fill(ContractStorageRead.empty());
for (let i = 0; i < MAX_PUBLIC_DATA_UPDATE_REQUESTS_PER_CALL; i++) {
const request = new ContractStorageRead(witnessReader.readField(), witnessReader.readField());
contractStorageReads[i] = request;
}

const publicCallStack = witnessReader.readFieldArray(MAX_PUBLIC_CALL_STACK_LENGTH_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);
const unencryptedLogPreimagesLength = witnessReader.readField();

const header = Header.fromFields(witnessReader.readFieldArray(HEADER_LENGTH));

const proverAddress = AztecAddress.fromField(witnessReader.readField());

return new PublicCircuitPublicInputs(
callContext,
argsHash,
returnValues,
contractStorageUpdateRequests as Tuple<
ContractStorageUpdateRequest,
typeof MAX_PUBLIC_DATA_UPDATE_REQUESTS_PER_CALL
>,
contractStorageReads as Tuple<ContractStorageRead, typeof MAX_PUBLIC_DATA_READS_PER_CALL>,
publicCallStack,
newCommitments,
newNullifiers,
newL2ToL1Msgs,
unencryptedLogsHash,
unencryptedLogPreimagesLength,
header,
proverAddress,
);
}
7 changes: 4 additions & 3 deletions yarn-project/acir-simulator/src/client/private_execution.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
import { FunctionData, PrivateCallStackItem } from '@aztec/circuits.js';
import { FunctionData, PrivateCallStackItem, PrivateCircuitPublicInputs } from '@aztec/circuits.js';
import { FunctionArtifactWithDebugMetadata, decodeReturnValues } from '@aztec/foundation/abi';
import { AztecAddress } from '@aztec/foundation/aztec-address';
import { Fr } from '@aztec/foundation/fields';
import { createDebugLogger } from '@aztec/foundation/log';
import { to2Fields } from '@aztec/foundation/serialize';

import { extractPrivateCircuitPublicInputs } from '../acvm/deserialize.js';
import { extractReturnWitness } from '../acvm/deserialize.js';
import { Oracle, acvm, extractCallStack } from '../acvm/index.js';
import { ExecutionError } from '../common/errors.js';
import { ClientExecutionContext } from './client_execution_context.js';
Expand Down Expand Up @@ -42,7 +42,8 @@ export async function executePrivateFunction(
},
);

const publicInputs = extractPrivateCircuitPublicInputs(partialWitness, acir);
const returnWitness = extractReturnWitness(acir, partialWitness);
const publicInputs = PrivateCircuitPublicInputs.fromFields(returnWitness);

const encryptedLogs = context.getEncryptedLogs();
const unencryptedLogs = context.getUnencryptedLogs();
Expand Down
7 changes: 4 additions & 3 deletions yarn-project/acir-simulator/src/public/executor.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { GlobalVariables, Header } from '@aztec/circuits.js';
import { GlobalVariables, Header, PublicCircuitPublicInputs } from '@aztec/circuits.js';
import { createDebugLogger } from '@aztec/foundation/log';

import { Oracle, acvm, extractCallStack, extractPublicCircuitPublicInputs } from '../acvm/index.js';
import { Oracle, acvm, extractCallStack, extractReturnWitness } from '../acvm/index.js';
import { ExecutionError, createSimulationError } from '../common/errors.js';
import { SideEffectCounter } from '../common/index.js';
import { PackedArgsCache } from '../common/packed_args_cache.js';
Expand Down Expand Up @@ -39,12 +39,13 @@ export async function executePublicFunction(
},
);

const returnWitness = extractReturnWitness(acir, partialWitness);
const {
returnValues,
newL2ToL1Msgs,
newCommitments: newCommitmentsPadded,
newNullifiers: newNullifiersPadded,
} = extractPublicCircuitPublicInputs(partialWitness, acir);
} = PublicCircuitPublicInputs.fromFields(returnWitness);

const newL2ToL1Messages = newL2ToL1Msgs.filter(v => !v.isZero());
const newCommitments = newCommitmentsPadded.filter(v => !v.isEmpty());
Expand Down
21 changes: 21 additions & 0 deletions yarn-project/circuits.js/src/structs/contract_storage_read.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import { makeContractStorageRead } from '../tests/factories.js';
import { ContractStorageRead } from './contract_storage_read.js';

describe('ContractStorageRead', () => {
it('serializes to buffer and deserializes it back', () => {
const randomInt = Math.floor(Math.random() * 1000);
const expected = makeContractStorageRead(randomInt);
const buffer = expected.toBuffer();
const res = ContractStorageRead.fromBuffer(buffer);
expect(res).toEqual(expected);
});

it('serializes to field array and deserializes it back', () => {
const randomInt = Math.floor(Math.random() * 1000);
const expected = makeContractStorageRead(randomInt);

const fieldArray = expected.toFields();
const res = ContractStorageRead.fromFields(fieldArray);
expect(res).toEqual(expected);
});
});
77 changes: 77 additions & 0 deletions yarn-project/circuits.js/src/structs/contract_storage_read.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
import { Fr } from '@aztec/foundation/fields';
import { BufferReader, FieldReader, serializeToBuffer } from '@aztec/foundation/serialize';

/**
* Contract storage read operation on a specific contract.
*
* Note: Similar to `PublicDataRead` but it's from the POV of contract storage so we are not working with public data
* tree leaf index but storage slot index.
*/
export class ContractStorageRead {
constructor(
/**
* Storage slot we are reading from.
*/
public readonly storageSlot: Fr,
/**
* Value read from the storage slot.
*/
public readonly currentValue: Fr,
/**
* Optional side effect counter tracking position of this event in tx execution.
* Note: Not serialized
*/
public readonly sideEffectCounter?: number,
) {}

static from(args: {
/**
* Storage slot we are reading from.
*/
storageSlot: Fr;
/**
* Value read from the storage slot.
*/
currentValue: Fr;
/**
* Optional side effect counter tracking position of this event in tx execution.
*/
sideEffectCounter?: number;
}) {
return new ContractStorageRead(args.storageSlot, args.currentValue, args.sideEffectCounter);
}

toBuffer() {
return serializeToBuffer(this.storageSlot, this.currentValue);
}

static fromBuffer(buffer: Buffer | BufferReader) {
const reader = BufferReader.asReader(buffer);
return new ContractStorageRead(Fr.fromBuffer(reader), Fr.fromBuffer(reader));
}

static empty() {
return new ContractStorageRead(Fr.ZERO, Fr.ZERO);
}

isEmpty() {
return this.storageSlot.isZero() && this.currentValue.isZero();
}

toFriendlyJSON() {
return `Slot=${this.storageSlot.toFriendlyJSON()}: ${this.currentValue.toFriendlyJSON()}`;
}

toFields(): Fr[] {
return [this.storageSlot, this.currentValue];
}

static fromFields(fields: Fr[] | FieldReader): ContractStorageRead {
const reader = FieldReader.asReader(fields);

const storageSlot = reader.readField();
const currentValue = reader.readField();

return new ContractStorageRead(storageSlot, currentValue);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import { makeContractStorageUpdateRequest } from '../tests/factories.js';
import { ContractStorageUpdateRequest } from './contract_storage_update_request.js';

describe('ContractStorageUpdateRequest', () => {
it('serializes to buffer and deserializes it back', () => {
const randomInt = Math.floor(Math.random() * 1000);
const expected = makeContractStorageUpdateRequest(randomInt);
const buffer = expected.toBuffer();
const res = ContractStorageUpdateRequest.fromBuffer(buffer);
expect(res).toEqual(expected);
});

it('serializes to field array and deserializes it back', () => {
const randomInt = Math.floor(Math.random() * 1000);
const expected = makeContractStorageUpdateRequest(randomInt);

const fieldArray = expected.toFields();
const res = ContractStorageUpdateRequest.fromFields(fieldArray);
expect(res).toEqual(expected);
});
});
Loading

0 comments on commit b92d690

Please sign in to comment.