From b92d6904fc9ad2cda30de1245fd546e00a5523e1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20Bene=C5=A1?= Date: Thu, 1 Feb 2024 17:01:30 +0100 Subject: [PATCH] refactor: `PublicCircuitPublicInputs` and `PrivateCircuitPublicInputs` cleanup (#4360) Cleanup of `PublicCircuitPublicInputs` serialization + more tests --- .../acir-simulator/src/acvm/deserialize.ts | 169 +---------------- .../src/client/private_execution.ts | 7 +- .../acir-simulator/src/public/executor.ts | 7 +- .../src/structs/contract_storage_read.test.ts | 21 ++ .../src/structs/contract_storage_read.ts | 77 ++++++++ .../contract_storage_update_request.test.ts | 21 ++ .../contract_storage_update_request.ts | 83 ++++++++ yarn-project/circuits.js/src/structs/index.ts | 2 + .../public_circuit_public_inputs.test.ts | 10 + .../structs/public_circuit_public_inputs.ts | 179 +++++------------- 10 files changed, 274 insertions(+), 302 deletions(-) create mode 100644 yarn-project/circuits.js/src/structs/contract_storage_read.test.ts create mode 100644 yarn-project/circuits.js/src/structs/contract_storage_read.ts create mode 100644 yarn-project/circuits.js/src/structs/contract_storage_update_request.test.ts create mode 100644 yarn-project/circuits.js/src/structs/contract_storage_update_request.ts diff --git a/yarn-project/acir-simulator/src/acvm/deserialize.ts b/yarn-project/acir-simulator/src/acvm/deserialize.ts index 1c4aa93ec26..57a9e437fc5 100644 --- a/yarn-project/acir-simulator/src/acvm/deserialize.ts +++ b/yarn-project/acir-simulator/src/acvm/deserialize.ts @@ -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'; @@ -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); @@ -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, - publicCallStack, - newCommitments, - newNullifiers, - newL2ToL1Msgs, - unencryptedLogsHash, - unencryptedLogPreimagesLength, - header, - proverAddress, - ); -} diff --git a/yarn-project/acir-simulator/src/client/private_execution.ts b/yarn-project/acir-simulator/src/client/private_execution.ts index c16fcd83b34..f02ca372be9 100644 --- a/yarn-project/acir-simulator/src/client/private_execution.ts +++ b/yarn-project/acir-simulator/src/client/private_execution.ts @@ -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'; @@ -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(); diff --git a/yarn-project/acir-simulator/src/public/executor.ts b/yarn-project/acir-simulator/src/public/executor.ts index 67af8c89a57..e5d3b935403 100644 --- a/yarn-project/acir-simulator/src/public/executor.ts +++ b/yarn-project/acir-simulator/src/public/executor.ts @@ -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'; @@ -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()); diff --git a/yarn-project/circuits.js/src/structs/contract_storage_read.test.ts b/yarn-project/circuits.js/src/structs/contract_storage_read.test.ts new file mode 100644 index 00000000000..51264c92afd --- /dev/null +++ b/yarn-project/circuits.js/src/structs/contract_storage_read.test.ts @@ -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); + }); +}); diff --git a/yarn-project/circuits.js/src/structs/contract_storage_read.ts b/yarn-project/circuits.js/src/structs/contract_storage_read.ts new file mode 100644 index 00000000000..907470b132d --- /dev/null +++ b/yarn-project/circuits.js/src/structs/contract_storage_read.ts @@ -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); + } +} diff --git a/yarn-project/circuits.js/src/structs/contract_storage_update_request.test.ts b/yarn-project/circuits.js/src/structs/contract_storage_update_request.test.ts new file mode 100644 index 00000000000..e38a0b031aa --- /dev/null +++ b/yarn-project/circuits.js/src/structs/contract_storage_update_request.test.ts @@ -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); + }); +}); diff --git a/yarn-project/circuits.js/src/structs/contract_storage_update_request.ts b/yarn-project/circuits.js/src/structs/contract_storage_update_request.ts new file mode 100644 index 00000000000..351ea091825 --- /dev/null +++ b/yarn-project/circuits.js/src/structs/contract_storage_update_request.ts @@ -0,0 +1,83 @@ +import { Fr } from '@aztec/foundation/fields'; +import { BufferReader, FieldReader, serializeToBuffer } from '@aztec/foundation/serialize'; +import { FieldsOf } from '@aztec/foundation/types'; + +/** + * Contract storage update request for a slot on a specific contract. + * + * Note: Similar to `PublicDataUpdateRequest` 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 ContractStorageUpdateRequest { + constructor( + /** + * Storage slot we are updating. + */ + public readonly storageSlot: Fr, + /** + * Old value of the storage slot. + */ + public readonly oldValue: Fr, + /** + * New value of the storage slot. + */ + public readonly newValue: Fr, + /** + * Optional side effect counter tracking position of this event in tx execution. + */ + public readonly sideEffectCounter?: number, + ) {} + + toBuffer() { + return serializeToBuffer(this.storageSlot, this.oldValue, this.newValue); + } + + static fromBuffer(buffer: Buffer | BufferReader) { + const reader = BufferReader.asReader(buffer); + return new ContractStorageUpdateRequest(Fr.fromBuffer(reader), Fr.fromBuffer(reader), Fr.fromBuffer(reader)); + } + + /** + * Create PublicCallRequest from a fields dictionary. + * @param fields - The dictionary. + * @returns A PublicCallRequest object. + */ + static from(fields: FieldsOf): ContractStorageUpdateRequest { + return new ContractStorageUpdateRequest(...ContractStorageUpdateRequest.getFields(fields)); + } + + /** + * Serialize into a field array. Low-level utility. + * @param fields - Object with fields. + * @returns The array. + */ + static getFields(fields: FieldsOf) { + return [fields.storageSlot, fields.oldValue, fields.newValue, fields.sideEffectCounter] as const; + } + + static empty() { + return new ContractStorageUpdateRequest(Fr.ZERO, Fr.ZERO, Fr.ZERO); + } + + isEmpty() { + return this.storageSlot.isZero() && this.oldValue.isZero() && this.newValue.isZero(); + } + + toFriendlyJSON() { + return `Slot=${this.storageSlot.toFriendlyJSON()}: ${this.oldValue.toFriendlyJSON()} => ${this.newValue.toFriendlyJSON()}`; + } + + toFields(): Fr[] { + return [this.storageSlot, this.oldValue, this.newValue]; + } + + static fromFields(fields: Fr[] | FieldReader): ContractStorageUpdateRequest { + const reader = FieldReader.asReader(fields); + + const storageSlot = reader.readField(); + const oldValue = reader.readField(); + const newValue = reader.readField(); + + return new ContractStorageUpdateRequest(storageSlot, oldValue, newValue); + } +} diff --git a/yarn-project/circuits.js/src/structs/index.ts b/yarn-project/circuits.js/src/structs/index.ts index 2218c043152..cb06d1b1412 100644 --- a/yarn-project/circuits.js/src/structs/index.ts +++ b/yarn-project/circuits.js/src/structs/index.ts @@ -5,6 +5,8 @@ export * from './call_request.js'; export * from './call_stack_item.js'; export * from './complete_address.js'; export * from './contract_deployment_data.js'; +export * from './contract_storage_read.js'; +export * from './contract_storage_update_request.js'; export * from './function_data.js'; export * from './function_leaf_preimage.js'; export * from './global_variables.js'; diff --git a/yarn-project/circuits.js/src/structs/public_circuit_public_inputs.test.ts b/yarn-project/circuits.js/src/structs/public_circuit_public_inputs.test.ts index 531a766e2fe..7fb1145ea63 100644 --- a/yarn-project/circuits.js/src/structs/public_circuit_public_inputs.test.ts +++ b/yarn-project/circuits.js/src/structs/public_circuit_public_inputs.test.ts @@ -1,6 +1,16 @@ +import { makePublicCircuitPublicInputs } from '../tests/factories.js'; import { PublicCircuitPublicInputs } from './public_circuit_public_inputs.js'; describe('PublicCircuitPublicInputs', () => { + it('serializes to field array and deserializes it back', () => { + const randomInt = Math.floor(Math.random() * 1000); + const expected = makePublicCircuitPublicInputs(randomInt, undefined); + + const fieldArray = expected.toFields(); + const res = PublicCircuitPublicInputs.fromFields(fieldArray); + expect(res).toEqual(expected); + }); + it(`initializes an empty PrivateCircuitPublicInputs`, () => { const target = PublicCircuitPublicInputs.empty(); expect(target.isEmpty()).toBe(true); diff --git a/yarn-project/circuits.js/src/structs/public_circuit_public_inputs.ts b/yarn-project/circuits.js/src/structs/public_circuit_public_inputs.ts index 572e76e273b..d0d7961f538 100644 --- a/yarn-project/circuits.js/src/structs/public_circuit_public_inputs.ts +++ b/yarn-project/circuits.js/src/structs/public_circuit_public_inputs.ts @@ -2,7 +2,7 @@ import { makeTuple } from '@aztec/foundation/array'; import { AztecAddress } from '@aztec/foundation/aztec-address'; import { isArrayEmpty } from '@aztec/foundation/collection'; import { Fr } from '@aztec/foundation/fields'; -import { BufferReader, Tuple, serializeToBuffer } from '@aztec/foundation/serialize'; +import { FieldReader, Tuple, serializeToBuffer, serializeToFieldArray } from '@aztec/foundation/serialize'; import { FieldsOf } from '@aztec/foundation/types'; import { @@ -12,137 +12,17 @@ import { MAX_PUBLIC_CALL_STACK_LENGTH_PER_CALL, MAX_PUBLIC_DATA_READS_PER_CALL, MAX_PUBLIC_DATA_UPDATE_REQUESTS_PER_CALL, + NUM_FIELDS_PER_SHA256, RETURN_VALUES_LENGTH, } from '../constants.gen.js'; import { CallContext } from './call_context.js'; -import { Header, SideEffect, SideEffectLinkedToNoteHash } from './index.js'; - -/** - * 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. - */ - 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()}`; - } -} - -/** - * Contract storage update request for a slot on a specific contract. - * - * Note: Similar to `PublicDataUpdateRequest` 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 ContractStorageUpdateRequest { - constructor( - /** - * Storage slot we are updating. - */ - public readonly storageSlot: Fr, - /** - * Old value of the storage slot. - */ - public readonly oldValue: Fr, - /** - * New value of the storage slot. - */ - public readonly newValue: Fr, - /** - * Optional side effect counter tracking position of this event in tx execution. - */ - public readonly sideEffectCounter?: number, - ) {} - - toBuffer() { - return serializeToBuffer(this.storageSlot, this.oldValue, this.newValue); - } - - static fromBuffer(buffer: Buffer | BufferReader) { - const reader = BufferReader.asReader(buffer); - return new ContractStorageUpdateRequest(Fr.fromBuffer(reader), Fr.fromBuffer(reader), Fr.fromBuffer(reader)); - } - - /** - * Create PublicCallRequest from a fields dictionary. - * @param fields - The dictionary. - * @returns A PublicCallRequest object. - */ - static from(fields: FieldsOf): ContractStorageUpdateRequest { - return new ContractStorageUpdateRequest(...ContractStorageUpdateRequest.getFields(fields)); - } - - /** - * Serialize into a field array. Low-level utility. - * @param fields - Object with fields. - * @returns The array. - */ - static getFields(fields: FieldsOf) { - return [fields.storageSlot, fields.oldValue, fields.newValue, fields.sideEffectCounter] as const; - } - - static empty() { - return new ContractStorageUpdateRequest(Fr.ZERO, Fr.ZERO, Fr.ZERO); - } - - isEmpty() { - return this.storageSlot.isZero() && this.oldValue.isZero() && this.newValue.isZero(); - } - - toFriendlyJSON() { - return `Slot=${this.storageSlot.toFriendlyJSON()}: ${this.oldValue.toFriendlyJSON()} => ${this.newValue.toFriendlyJSON()}`; - } -} +import { + ContractStorageRead, + ContractStorageUpdateRequest, + Header, + SideEffect, + SideEffectLinkedToNoteHash, +} from './index.js'; /** * Public inputs to a public circuit. @@ -291,4 +171,45 @@ export class PublicCircuitPublicInputs { toBuffer(): Buffer { return serializeToBuffer(...PublicCircuitPublicInputs.getFields(this)); } + + toFields(): Fr[] { + return serializeToFieldArray(...PublicCircuitPublicInputs.getFields(this)); + } + + static fromFields(fields: Fr[] | FieldReader): PublicCircuitPublicInputs { + const reader = FieldReader.asReader(fields); + + const callContext = CallContext.fromFields(reader); + const argsHash = reader.readField(); + const returnValues = reader.readFieldArray(RETURN_VALUES_LENGTH); + const contractStorageUpdateRequests = reader.readArray( + MAX_PUBLIC_DATA_UPDATE_REQUESTS_PER_CALL, + ContractStorageUpdateRequest, + ); + const contractStorageReads = reader.readArray(MAX_PUBLIC_DATA_READS_PER_CALL, ContractStorageRead); + const publicCallStackHashes = reader.readFieldArray(MAX_PUBLIC_CALL_STACK_LENGTH_PER_CALL); + const newCommitments = reader.readArray(MAX_NEW_COMMITMENTS_PER_CALL, SideEffect); + const newNullifiers = reader.readArray(MAX_NEW_NULLIFIERS_PER_CALL, SideEffectLinkedToNoteHash); + const newL2ToL1Msgs = reader.readFieldArray(MAX_NEW_L2_TO_L1_MSGS_PER_CALL); + const unencryptedLogsHash = reader.readFieldArray(NUM_FIELDS_PER_SHA256); + const unencryptedLogPreimagesLength = reader.readField(); + const historicalHeader = Header.fromFields(reader); + const proverAddress = AztecAddress.fromFields(reader); + + return new PublicCircuitPublicInputs( + callContext, + argsHash, + returnValues, + contractStorageUpdateRequests, + contractStorageReads, + publicCallStackHashes, + newCommitments, + newNullifiers, + newL2ToL1Msgs, + unencryptedLogsHash, + unencryptedLogPreimagesLength, + historicalHeader, + proverAddress, + ); + } }