From 433b9ebdb505b21bef40c174e4feec0e6ca211e8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20Bene=C5=A1?= Date: Thu, 1 Feb 2024 14:05:40 +0100 Subject: [PATCH] refactor: `toFields()`/`fromFields(...)` methods in more classes (#4335) Partially fixes #4332 The goal of this PR was to progress on nuking yarn-project/acir-simulator/src/acvm/serialize.ts and move it to the individual classes to then be able to easily test it in the classes tests and improve readability. I didn't fully tackle the issue in this one because it was all getting too big. --- .../acir-simulator/src/acvm/deserialize.ts | 4 +- .../acir-simulator/src/acvm/oracle/oracle.ts | 16 +- .../src/acvm/oracle/typed_oracle.ts | 33 ++-- .../acir-simulator/src/acvm/serialize.ts | 154 +----------------- .../src/client/client_execution_context.ts | 14 +- .../src/client/private_execution.test.ts | 14 +- .../acir-simulator/src/public/index.test.ts | 9 +- .../src/public/public_execution_context.ts | 10 +- yarn-project/circuits.js/src/abis/abis.ts | 4 +- .../call_stack_item.test.ts.snap | 44 +++++ .../circuits.js/src/structs/call_context.ts | 6 +- .../src/structs/call_stack_item.test.ts | 28 ++++ .../src/structs/call_stack_item.ts | 23 ++- .../structs/contract_deployment_data.test.ts | 17 ++ .../src/structs/contract_deployment_data.ts | 94 +++++++++++ .../circuits.js/src/structs/function_data.ts | 19 ++- .../src/structs/global_variables.ts | 10 +- .../circuits.js/src/structs/header.test.ts | 4 +- .../circuits.js/src/structs/header.ts | 35 ++-- yarn-project/circuits.js/src/structs/index.ts | 1 + .../src/structs/partial_state_reference.ts | 24 ++- .../private_circuit_public_inputs.test.ts | 15 ++ .../structs/private_circuit_public_inputs.ts | 44 ++++- .../rollup/append_only_tree_snapshot.ts | 10 +- .../src/structs/state_reference.ts | 13 +- .../circuits.js/src/structs/tx_context.ts | 75 +-------- .../foundation/src/serialize/serialize.ts | 40 +++++ .../src/type_conversion.test.ts | 2 +- .../pxe/src/simulator_oracle/index.ts | 6 +- .../src/simulator/public_executor.ts | 6 +- 30 files changed, 434 insertions(+), 340 deletions(-) create mode 100644 yarn-project/circuits.js/src/structs/__snapshots__/call_stack_item.test.ts.snap create mode 100644 yarn-project/circuits.js/src/structs/call_stack_item.test.ts create mode 100644 yarn-project/circuits.js/src/structs/contract_deployment_data.test.ts create mode 100644 yarn-project/circuits.js/src/structs/contract_deployment_data.ts diff --git a/yarn-project/acir-simulator/src/acvm/deserialize.ts b/yarn-project/acir-simulator/src/acvm/deserialize.ts index a49f060e2e7..1c4aa93ec26 100644 --- a/yarn-project/acir-simulator/src/acvm/deserialize.ts +++ b/yarn-project/acir-simulator/src/acvm/deserialize.ts @@ -101,7 +101,7 @@ export function extractPrivateCircuitPublicInputs( const encryptedLogPreimagesLength = witnessReader.readField(); const unencryptedLogPreimagesLength = witnessReader.readField(); - const header = Header.fromFieldArray(witnessReader.readFieldArray(HEADER_LENGTH)); + const header = Header.fromFields(witnessReader.readFieldArray(HEADER_LENGTH)); const contractDeploymentData = new ContractDeploymentData( new Point(witnessReader.readField(), witnessReader.readField()), @@ -176,7 +176,7 @@ export function extractPublicCircuitPublicInputs(partialWitness: ACVMWitness, ac const unencryptedLogsHash = witnessReader.readFieldArray(NUM_FIELDS_PER_SHA256); const unencryptedLogPreimagesLength = witnessReader.readField(); - const header = Header.fromFieldArray(witnessReader.readFieldArray(HEADER_LENGTH)); + const header = Header.fromFields(witnessReader.readFieldArray(HEADER_LENGTH)); const proverAddress = AztecAddress.fromField(witnessReader.readField()); diff --git a/yarn-project/acir-simulator/src/acvm/oracle/oracle.ts b/yarn-project/acir-simulator/src/acvm/oracle/oracle.ts index 22d3d9dd04c..984d16c7992 100644 --- a/yarn-project/acir-simulator/src/acvm/oracle/oracle.ts +++ b/yarn-project/acir-simulator/src/acvm/oracle/oracle.ts @@ -8,13 +8,7 @@ import { createDebugLogger } from '@aztec/foundation/log'; import { ACVMField } from '../acvm_types.js'; import { frToNumber, fromACVMField } from '../deserialize.js'; -import { - toACVMField, - toACVMHeader, - toAcvmCallPrivateStackItem, - toAcvmEnqueuePublicFunctionResult, - toAcvmL1ToL2MessageLoadOracleInputs, -} from '../serialize.js'; +import { toACVMField, toAcvmEnqueuePublicFunctionResult } from '../serialize.js'; import { acvmFieldMessageToString, oracleDebugCallToFormattedStr } from './debug.js'; import { TypedOracle } from './typed_oracle.js'; @@ -132,7 +126,7 @@ export class Oracle { if (!header) { throw new Error(`Block header not found for block ${parsedBlockNumber}.`); } - return toACVMHeader(header); + return header.toFields().map(toACVMField); } async getAuthWitness([messageHash]: ACVMField[]): Promise { @@ -226,8 +220,8 @@ export class Oracle { } async getL1ToL2Message([msgKey]: ACVMField[]): Promise { - const { ...message } = await this.typedOracle.getL1ToL2Message(fromACVMField(msgKey)); - return toAcvmL1ToL2MessageLoadOracleInputs(message); + const message = await this.typedOracle.getL1ToL2Message(fromACVMField(msgKey)); + return message.toFields().map(toACVMField); } async getPortalContractAddress([aztecAddress]: ACVMField[]): Promise { @@ -297,7 +291,7 @@ export class Oracle { fromACVMField(argsHash), frToNumber(fromACVMField(sideffectCounter)), ); - return toAcvmCallPrivateStackItem(callStackItem); + return callStackItem.toFields().map(toACVMField); } async callPublicFunction( diff --git a/yarn-project/acir-simulator/src/acvm/oracle/typed_oracle.ts b/yarn-project/acir-simulator/src/acvm/oracle/typed_oracle.ts index ffa9ac1fbfc..f4c58c9d33d 100644 --- a/yarn-project/acir-simulator/src/acvm/oracle/typed_oracle.ts +++ b/yarn-project/acir-simulator/src/acvm/oracle/typed_oracle.ts @@ -48,23 +48,22 @@ export interface NoteData { index?: bigint; } -/** - * The data for L1 to L2 Messages provided by other data sources. - */ -export interface MessageLoadOracleInputs { - /** - * An collapsed array of fields containing all of the l1 to l2 message components. - * `l1ToL2Message.toFieldArray()` -\> [sender, chainId, recipient, version, content, secretHash, deadline, fee] - */ - message: Fr[]; - /** - * The path in the merkle tree to the message. - */ - siblingPath: Fr[]; - /** - * The index of the message commitment in the merkle tree. - */ - index: bigint; +export class MessageLoadOracleInputs { + constructor( + /** + * An collapsed array of fields containing all of the l1 to l2 message components. + * `l1ToL2Message.toFieldArray()` -\> [sender, chainId, recipient, version, content, secretHash, deadline, fee] + */ + public message: Fr[], + /** The index of the message commitment in the merkle tree. */ + public index: bigint, + /** The path in the merkle tree to the message. */ + public siblingPath: Fr[], + ) {} + + toFields(): Fr[] { + return [...this.message, new Fr(this.index), ...this.siblingPath]; + } } /** diff --git a/yarn-project/acir-simulator/src/acvm/serialize.ts b/yarn-project/acir-simulator/src/acvm/serialize.ts index c2c7440d8a9..28ca4b3e27f 100644 --- a/yarn-project/acir-simulator/src/acvm/serialize.ts +++ b/yarn-project/acir-simulator/src/acvm/serialize.ts @@ -1,19 +1,9 @@ -import { - CallContext, - ContractDeploymentData, - FunctionData, - GlobalVariables, - Header, - PrivateCallStackItem, - PrivateCircuitPublicInputs, - PublicCallRequest, -} from '@aztec/circuits.js'; +import { PublicCallRequest } from '@aztec/circuits.js'; import { AztecAddress } from '@aztec/foundation/aztec-address'; import { EthAddress } from '@aztec/foundation/eth-address'; import { Fr } from '@aztec/foundation/fields'; import { ACVMField } from './acvm_types.js'; -import { MessageLoadOracleInputs } from './oracle/typed_oracle.js'; /** * Adapts the buffer to the field size. @@ -53,125 +43,6 @@ export function toACVMField( // Utilities to write TS classes to ACVM Field arrays // In the order that the ACVM expects them -/** - * Converts a function data to ACVM fields. - * @param functionData - The function data to convert. - * @returns The ACVM fields. - */ -export function toACVMFunctionData(functionData: FunctionData): ACVMField[] { - return [ - toACVMField(functionData.selector.toBuffer()), - toACVMField(functionData.isInternal), - toACVMField(functionData.isPrivate), - toACVMField(functionData.isConstructor), - ]; -} - -/** - * Converts a call context to ACVM fields. - * @param callContext - The call context to convert. - * @returns The ACVM fields. - */ -export function toACVMCallContext(callContext: CallContext): ACVMField[] { - return [ - toACVMField(callContext.msgSender), - toACVMField(callContext.storageContractAddress), - toACVMField(callContext.portalContractAddress), - toACVMField(callContext.functionSelector.toField()), - toACVMField(callContext.isDelegateCall), - toACVMField(callContext.isStaticCall), - toACVMField(callContext.isContractDeployment), - toACVMField(callContext.startSideEffectCounter), - ]; -} - -/** - * Converts a contract deployment data to ACVM fields. - * @param contractDeploymentData - The contract deployment data to convert. - * @returns The ACVM fields. - */ -export function toACVMContractDeploymentData(contractDeploymentData: ContractDeploymentData): ACVMField[] { - return [ - toACVMField(contractDeploymentData.publicKey.x), - toACVMField(contractDeploymentData.publicKey.y), - toACVMField(contractDeploymentData.initializationHash), - toACVMField(contractDeploymentData.contractClassId), - toACVMField(contractDeploymentData.contractAddressSalt), - toACVMField(contractDeploymentData.portalContractAddress), - ]; -} - -/** - * Converts a block header into ACVM fields. - * @param header - The block header object to convert. - * @returns The ACVM fields. - */ -export function toACVMHeader(header: Header): ACVMField[] { - return header.toFieldArray().map(toACVMField); -} - -/** - * Converts global variables into ACVM fields - * @param globalVariables - The global variables object to convert. - * @returns The ACVM fields - */ -export function toACVMGlobalVariables(globalVariables: GlobalVariables): ACVMField[] { - return [ - toACVMField(globalVariables.chainId), - toACVMField(globalVariables.version), - toACVMField(globalVariables.blockNumber), - toACVMField(globalVariables.timestamp), - ]; -} - -/** - * Converts the public inputs structure to ACVM fields. - * @param publicInputs - The public inputs to convert. - * @returns The ACVM fields. - */ -export function toACVMPublicInputs(publicInputs: PrivateCircuitPublicInputs): ACVMField[] { - return [ - ...toACVMCallContext(publicInputs.callContext), - toACVMField(publicInputs.argsHash), - - ...publicInputs.returnValues.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), - toACVMField(publicInputs.endSideEffectCounter), - ...publicInputs.encryptedLogsHash.map(toACVMField), - ...publicInputs.unencryptedLogsHash.map(toACVMField), - - toACVMField(publicInputs.encryptedLogPreimagesLength), - toACVMField(publicInputs.unencryptedLogPreimagesLength), - - ...toACVMHeader(publicInputs.historicalHeader), - - ...toACVMContractDeploymentData(publicInputs.contractDeploymentData), - - toACVMField(publicInputs.chainId), - toACVMField(publicInputs.version), - ]; -} - -/** - * Converts a private call stack item to ACVM fields. - * @param item - The private call stack item to convert. - * @returns The ACVM fields. - */ -export function toAcvmCallPrivateStackItem(item: PrivateCallStackItem): ACVMField[] { - return [ - toACVMField(item.contractAddress), - ...toACVMFunctionData(item.functionData), - ...toACVMPublicInputs(item.publicInputs), - toACVMField(item.isExecutionRequest), - ]; -} - /** * Converts a public call stack item with the request for executing a public function to * a set of ACVM fields accepted by the enqueue_public_function_call_oracle Aztec.nr function. @@ -182,24 +53,11 @@ export function toAcvmCallPrivateStackItem(item: PrivateCallStackItem): ACVMFiel */ export function toAcvmEnqueuePublicFunctionResult(item: PublicCallRequest): ACVMField[] { return [ - toACVMField(item.contractAddress), - ...toACVMFunctionData(item.functionData), - ...toACVMCallContext(item.callContext), - toACVMField(item.getArgsHash()), - ]; -} - -/** - * Converts the result of loading messages to ACVM fields. - * @param messageLoadOracleInputs - The result of loading messages to convert. - * @returns The Message Oracle Fields. - */ -export function toAcvmL1ToL2MessageLoadOracleInputs(messageLoadOracleInputs: MessageLoadOracleInputs): ACVMField[] { - return [ - ...messageLoadOracleInputs.message.map(f => toACVMField(f)), - toACVMField(messageLoadOracleInputs.index), - ...messageLoadOracleInputs.siblingPath.map(f => toACVMField(f)), - ]; + item.contractAddress.toField(), + ...item.functionData.toFields(), + ...item.callContext.toFields(), + item.getArgsHash(), + ].map(toACVMField); } /** diff --git a/yarn-project/acir-simulator/src/client/client_execution_context.ts b/yarn-project/acir-simulator/src/client/client_execution_context.ts index f3d684f4ef0..27afde14d26 100644 --- a/yarn-project/acir-simulator/src/client/client_execution_context.ts +++ b/yarn-project/acir-simulator/src/client/client_execution_context.ts @@ -17,13 +17,7 @@ import { AztecAddress } from '@aztec/foundation/aztec-address'; import { Fr, Point } from '@aztec/foundation/fields'; import { createDebugLogger } from '@aztec/foundation/log'; -import { - NoteData, - toACVMCallContext, - toACVMContractDeploymentData, - toACVMHeader, - toACVMWitness, -} from '../acvm/index.js'; +import { NoteData, toACVMWitness } from '../acvm/index.js'; import { PackedArgsCache } from '../common/packed_args_cache.js'; import { DBOracle } from './db_oracle.js'; import { ExecutionNoteCache } from './execution_note_cache.js'; @@ -96,9 +90,9 @@ export class ClientExecutionContext extends ViewDataOracle { } const fields = [ - ...toACVMCallContext(this.callContext), - ...toACVMHeader(this.historicalHeader), - ...toACVMContractDeploymentData(contractDeploymentData), + ...this.callContext.toFields(), + ...this.historicalHeader.toFields(), + ...contractDeploymentData.toFields(), this.txContext.chainId, this.txContext.version, diff --git a/yarn-project/acir-simulator/src/client/private_execution.test.ts b/yarn-project/acir-simulator/src/client/private_execution.test.ts index 0587f3ec4dd..b3ace37240b 100644 --- a/yarn-project/acir-simulator/src/client/private_execution.test.ts +++ b/yarn-project/acir-simulator/src/client/private_execution.test.ts @@ -56,7 +56,7 @@ import { jest } from '@jest/globals'; import { MockProxy, mock } from 'jest-mock-extended'; import { getFunctionSelector } from 'viem'; -import { KeyPair } from '../acvm/index.js'; +import { KeyPair, MessageLoadOracleInputs } from '../acvm/index.js'; import { buildL1ToL2Message } from '../test/utils.js'; import { computeSlotForMapping } from '../utils.js'; import { DBOracle } from './db_oracle.js'; @@ -544,11 +544,13 @@ describe('Private Execution test suite', () => { const mockOracles = async () => { const tree = await insertLeaves([messageKey ?? preimage.hash()], 'l1ToL2Messages'); oracle.getL1ToL2Message.mockImplementation(async () => { - return Promise.resolve({ - message: preimage.toFieldArray(), - index: 0n, - siblingPath: (await tree.getSiblingPath(0n, false)).toFieldArray(), - }); + return Promise.resolve( + new MessageLoadOracleInputs( + preimage.toFieldArray(), + 0n, + (await tree.getSiblingPath(0n, false)).toFieldArray(), + ), + ); }); }; diff --git a/yarn-project/acir-simulator/src/public/index.test.ts b/yarn-project/acir-simulator/src/public/index.test.ts index dde71adb1ea..4d7fdb81c92 100644 --- a/yarn-project/acir-simulator/src/public/index.test.ts +++ b/yarn-project/acir-simulator/src/public/index.test.ts @@ -21,6 +21,7 @@ import { MockProxy, mock } from 'jest-mock-extended'; import { type MemDown, default as memdown } from 'memdown'; import { getFunctionSelector } from 'viem'; +import { MessageLoadOracleInputs } from '../index.js'; import { buildL1ToL2Message } from '../test/utils.js'; import { computeSlotForMapping } from '../utils.js'; import { CommitmentsDB, PublicContractsDB, PublicStateDB } from './db.js'; @@ -459,12 +460,8 @@ describe('ACIR public execution simulator', () => { for (const sibling of siblingPath) { root = Fr.fromBuffer(pedersenHash([root.toBuffer(), sibling.toBuffer()])); } - commitmentsDb.getL1ToL2Message.mockImplementation(async () => { - return await Promise.resolve({ - message: preimage.toFieldArray(), - index: 0n, - siblingPath, - }); + commitmentsDb.getL1ToL2Message.mockImplementation(() => { + return Promise.resolve(new MessageLoadOracleInputs(preimage.toFieldArray(), 0n, siblingPath)); }); return new AppendOnlyTreeSnapshot( diff --git a/yarn-project/acir-simulator/src/public/public_execution_context.ts b/yarn-project/acir-simulator/src/public/public_execution_context.ts index 5ed036b1413..1c275fae334 100644 --- a/yarn-project/acir-simulator/src/public/public_execution_context.ts +++ b/yarn-project/acir-simulator/src/public/public_execution_context.ts @@ -5,7 +5,7 @@ import { EthAddress } from '@aztec/foundation/eth-address'; import { Fr } from '@aztec/foundation/fields'; import { createDebugLogger } from '@aztec/foundation/log'; -import { TypedOracle, toACVMCallContext, toACVMGlobalVariables, toACVMHeader, toACVMWitness } from '../acvm/index.js'; +import { TypedOracle, toACVMWitness } from '../acvm/index.js'; import { PackedArgsCache, SideEffectCounter } from '../common/index.js'; import { CommitmentsDB, PublicContractsDB, PublicStateDB } from './db.js'; import { PublicExecution, PublicExecutionResult } from './execution.js'; @@ -49,13 +49,7 @@ export class PublicExecutionContext extends TypedOracle { */ public getInitialWitness(witnessStartIndex = 0) { const { callContext, args } = this.execution; - const fields = [ - ...toACVMCallContext(callContext), - ...toACVMHeader(this.header), - ...toACVMGlobalVariables(this.globalVariables), - - ...args, - ]; + const fields = [...callContext.toFields(), ...this.header.toFields(), ...this.globalVariables.toFields(), ...args]; return toACVMWitness(witnessStartIndex, fields); } diff --git a/yarn-project/circuits.js/src/abis/abis.ts b/yarn-project/circuits.js/src/abis/abis.ts index 8f346fbd79e..003829def1b 100644 --- a/yarn-project/circuits.js/src/abis/abis.ts +++ b/yarn-project/circuits.js/src/abis/abis.ts @@ -397,7 +397,7 @@ function computePrivateInputsHash(input: PrivateCircuitPublicInputs) { ...input.unencryptedLogsHash.map(fr => fr.toBuffer()), input.encryptedLogPreimagesLength.toBuffer(), input.unencryptedLogPreimagesLength.toBuffer(), - ...(input.historicalHeader.toFieldArray().map(fr => fr.toBuffer()) as Buffer[]), + ...(input.historicalHeader.toFields().map(fr => fr.toBuffer()) as Buffer[]), computeContractDeploymentDataHash(input.contractDeploymentData).toBuffer(), input.chainId.toBuffer(), input.version.toBuffer(), @@ -463,7 +463,7 @@ export function computePublicInputsHash(input: PublicCircuitPublicInputs) { ...input.newL2ToL1Msgs.map(fr => fr.toBuffer()), ...input.unencryptedLogsHash.map(fr => fr.toBuffer()), input.unencryptedLogPreimagesLength.toBuffer(), - ...input.historicalHeader.toFieldArray().map(fr => fr.toBuffer()), + ...input.historicalHeader.toFields().map(fr => fr.toBuffer()), input.proverAddress.toBuffer(), ]; if (toHash.length != PUBLIC_CIRCUIT_PUBLIC_INPUTS_HASH_INPUT_LENGTH) { diff --git a/yarn-project/circuits.js/src/structs/__snapshots__/call_stack_item.test.ts.snap b/yarn-project/circuits.js/src/structs/__snapshots__/call_stack_item.test.ts.snap new file mode 100644 index 00000000000..d54ec645a35 --- /dev/null +++ b/yarn-project/circuits.js/src/structs/__snapshots__/call_stack_item.test.ts.snap @@ -0,0 +1,44 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`PrivateCallStackItem computes hash 1`] = ` +Fr { + "asBigInt": 20211835094457188239301134546342553663959029760227448898294272588084026667910n, + "asBuffer": { + "data": [ + 44, + 175, + 126, + 70, + 125, + 139, + 43, + 118, + 26, + 81, + 126, + 221, + 198, + 219, + 65, + 196, + 232, + 191, + 214, + 222, + 18, + 149, + 88, + 100, + 79, + 28, + 235, + 175, + 176, + 129, + 243, + 134, + ], + "type": "Buffer", + }, +} +`; diff --git a/yarn-project/circuits.js/src/structs/call_context.ts b/yarn-project/circuits.js/src/structs/call_context.ts index 5f35bced17d..3640040718d 100644 --- a/yarn-project/circuits.js/src/structs/call_context.ts +++ b/yarn-project/circuits.js/src/structs/call_context.ts @@ -1,6 +1,6 @@ import { AztecAddress } from '@aztec/foundation/aztec-address'; import { EthAddress } from '@aztec/foundation/eth-address'; -import { BufferReader, FieldReader, serializeToBuffer } from '@aztec/foundation/serialize'; +import { BufferReader, FieldReader, serializeToBuffer, serializeToFieldArray } from '@aztec/foundation/serialize'; import { FieldsOf } from '@aztec/foundation/types'; import { Fr, FunctionSelector } from './index.js'; @@ -107,6 +107,10 @@ export class CallContext { return serializeToBuffer(...CallContext.getFields(this)); } + toFields(): Fr[] { + return serializeToFieldArray(...CallContext.getFields(this)); + } + /** * Deserialize this from a buffer. * @param buffer - The bufferable type from which to deserialize. diff --git a/yarn-project/circuits.js/src/structs/call_stack_item.test.ts b/yarn-project/circuits.js/src/structs/call_stack_item.test.ts new file mode 100644 index 00000000000..0a483a35699 --- /dev/null +++ b/yarn-project/circuits.js/src/structs/call_stack_item.test.ts @@ -0,0 +1,28 @@ +import { makePrivateCallStackItem } from '../tests/factories.js'; +import { PrivateCallStackItem } from './call_stack_item.js'; + +describe('PrivateCallStackItem', () => { + it('serializes to buffer and deserializes it back', () => { + const randomInt = Math.floor(Math.random() * 1000); + const expected = makePrivateCallStackItem(randomInt); + const buffer = expected.toBuffer(); + const res = PrivateCallStackItem.fromBuffer(buffer); + expect(res).toEqual(expected); + }); + + it('serializes to field array and deserializes it back', () => { + const randomInt = Math.floor(Math.random() * 1000); + const expected = makePrivateCallStackItem(randomInt); + + const fieldArray = expected.toFields(); + const res = PrivateCallStackItem.fromFields(fieldArray); + expect(res).toEqual(expected); + }); + + it('computes hash', () => { + const seed = 9870243; + const PrivateCallStackItem = makePrivateCallStackItem(seed); + const hash = PrivateCallStackItem.hash(); + expect(hash).toMatchSnapshot(); + }); +}); diff --git a/yarn-project/circuits.js/src/structs/call_stack_item.ts b/yarn-project/circuits.js/src/structs/call_stack_item.ts index 4752238d7bc..397b1654289 100644 --- a/yarn-project/circuits.js/src/structs/call_stack_item.ts +++ b/yarn-project/circuits.js/src/structs/call_stack_item.ts @@ -1,6 +1,6 @@ import { AztecAddress } from '@aztec/foundation/aztec-address'; import { Fr } from '@aztec/foundation/fields'; -import { BufferReader, serializeToBuffer } from '@aztec/foundation/serialize'; +import { BufferReader, FieldReader, serializeToBuffer } from '@aztec/foundation/serialize'; import { computePrivateCallStackItemHash, computePublicCallStackItemHash } from '../abis/abis.js'; import { CallRequest, CallerContext } from './call_request.js'; @@ -10,7 +10,6 @@ import { PublicCircuitPublicInputs } from './public_circuit_public_inputs.js'; /** * Call stack item on a private call. - * @see cpp/src/aztec3/circuits/abis/call_stack_item.hpp. */ export class PrivateCallStackItem { constructor( @@ -40,6 +39,15 @@ export class PrivateCallStackItem { return serializeToBuffer(this.contractAddress, this.functionData, this.publicInputs, this.isExecutionRequest); } + toFields(): Fr[] { + return [ + this.contractAddress.toField(), + ...this.functionData.toFields(), + ...this.publicInputs.toFields(), + new Fr(this.isExecutionRequest), + ]; + } + /** * Deserializes from a buffer or reader. * @param buffer - Buffer or reader to read from. @@ -55,6 +63,17 @@ export class PrivateCallStackItem { ); } + static fromFields(fields: Fr[] | FieldReader): PrivateCallStackItem { + const reader = FieldReader.asReader(fields); + + const contractAddress = AztecAddress.fromFields(reader); + const functionData = FunctionData.fromFields(reader); + const publicInputs = PrivateCircuitPublicInputs.fromFields(reader); + const isExecutionRequest = reader.readBoolean(); + + return new PrivateCallStackItem(contractAddress, functionData, publicInputs, isExecutionRequest); + } + /** * Returns a new instance of PrivateCallStackItem with zero contract address, function data and public inputs. * @returns A new instance of PrivateCallStackItem with zero contract address, function data and public inputs. diff --git a/yarn-project/circuits.js/src/structs/contract_deployment_data.test.ts b/yarn-project/circuits.js/src/structs/contract_deployment_data.test.ts new file mode 100644 index 00000000000..40fa0173a03 --- /dev/null +++ b/yarn-project/circuits.js/src/structs/contract_deployment_data.test.ts @@ -0,0 +1,17 @@ +import { makeContractDeploymentData } from '../tests/factories.js'; +import { ContractDeploymentData } from './contract_deployment_data.js'; + +describe('ContractDeploymentData', () => { + it(`serializes to buffer and deserializes it back`, () => { + const expected = makeContractDeploymentData(1); + const buffer = expected.toBuffer(); + const res = ContractDeploymentData.fromBuffer(buffer); + expect(res).toEqual(expected); + expect(res.isEmpty()).toBe(false); + }); + + it(`initializes an empty ContractDeploymentData`, () => { + const target = ContractDeploymentData.empty(); + expect(target.isEmpty()).toBe(true); + }); +}); diff --git a/yarn-project/circuits.js/src/structs/contract_deployment_data.ts b/yarn-project/circuits.js/src/structs/contract_deployment_data.ts new file mode 100644 index 00000000000..d99d15044a0 --- /dev/null +++ b/yarn-project/circuits.js/src/structs/contract_deployment_data.ts @@ -0,0 +1,94 @@ +import { BufferReader, FieldReader, serializeToBuffer } from '@aztec/foundation/serialize'; + +import { EthAddress, Fr, Point, PublicKey } from '../index.js'; + +/** + * Contract deployment data in a TxContext + * Not to be confused with NewContractData. + */ +export class ContractDeploymentData { + constructor( + /** Public key of the contract. */ + public publicKey: PublicKey, + /** Hash of the initialization payload. */ + public initializationHash: Fr, + /** Contract class identifier. */ + public contractClassId: Fr, + /** Contract address salt (used when deriving a contract address). */ + public contractAddressSalt: Fr, + /** Ethereum address of the portal contract on L1. */ + public portalContractAddress: EthAddress, + ) {} + + toBuffer() { + return serializeToBuffer( + this.publicKey, + this.initializationHash, + this.contractClassId, + this.contractAddressSalt, + this.portalContractAddress, + ); + } + + toFields(): Fr[] { + return [ + ...this.publicKey.toFields(), + this.initializationHash, + this.contractClassId, + this.contractAddressSalt, + this.portalContractAddress.toField(), + ]; + } + + /** + * Returns an empty ContractDeploymentData. + * @returns The empty ContractDeploymentData. + */ + public static empty(): ContractDeploymentData { + return new ContractDeploymentData(Point.ZERO, Fr.ZERO, Fr.ZERO, Fr.ZERO, EthAddress.ZERO); + } + + isEmpty() { + return ( + this.publicKey.isZero() && + this.initializationHash.isZero() && + this.contractClassId.isZero() && + this.contractAddressSalt.isZero() && + this.portalContractAddress.isZero() + ); + } + + /** + * Deserializes contract deployment data rom a buffer or reader. + * @param buffer - Buffer to read from. + * @returns The deserialized ContractDeploymentData. + */ + static fromBuffer(buffer: Buffer | BufferReader): ContractDeploymentData { + const reader = BufferReader.asReader(buffer); + return new ContractDeploymentData( + reader.readObject(Point), + Fr.fromBuffer(reader), + Fr.fromBuffer(reader), + Fr.fromBuffer(reader), + new EthAddress(reader.readBytes(32)), + ); + } + + static fromFields(fields: Fr[] | FieldReader): ContractDeploymentData { + const reader = FieldReader.asReader(fields); + + const publicKey = Point.fromFields(reader); + const initializationHash = reader.readField(); + const contractClassId = reader.readField(); + const contractAddressSalt = reader.readField(); + const portalContractAddress = new EthAddress(reader.readField().toBuffer()); + + return new ContractDeploymentData( + publicKey, + initializationHash, + contractClassId, + contractAddressSalt, + portalContractAddress, + ); + } +} diff --git a/yarn-project/circuits.js/src/structs/function_data.ts b/yarn-project/circuits.js/src/structs/function_data.ts index 7bafa4c126a..d35eb8dfffa 100644 --- a/yarn-project/circuits.js/src/structs/function_data.ts +++ b/yarn-project/circuits.js/src/structs/function_data.ts @@ -1,7 +1,7 @@ import { FunctionAbi, FunctionSelector, FunctionType } from '@aztec/foundation/abi'; -import { BufferReader, serializeToBuffer } from '@aztec/foundation/serialize'; +import { BufferReader, FieldReader, serializeToBuffer } from '@aztec/foundation/serialize'; -import { ContractFunctionDao } from '../index.js'; +import { ContractFunctionDao, Fr } from '../index.js'; /** * Function description for circuit. @@ -44,6 +44,10 @@ export class FunctionData { return serializeToBuffer(this.selector, this.isInternal, this.isPrivate, this.isConstructor); } + toFields(): Fr[] { + return [this.selector.toField(), new Fr(this.isInternal), new Fr(this.isPrivate), new Fr(this.isConstructor)]; + } + /** * Returns whether this instance is empty. * @returns True if the function selector is zero. @@ -93,4 +97,15 @@ export class FunctionData { reader.readBoolean(), ); } + + static fromFields(fields: Fr[] | FieldReader): FunctionData { + const reader = FieldReader.asReader(fields); + + const selector = FunctionSelector.fromFields(reader); + const isInternal = reader.readBoolean(); + const isPrivate = reader.readBoolean(); + const isConstructor = reader.readBoolean(); + + return new FunctionData(selector, isInternal, isPrivate, isConstructor); + } } diff --git a/yarn-project/circuits.js/src/structs/global_variables.ts b/yarn-project/circuits.js/src/structs/global_variables.ts index fd353693a10..04491e46aba 100644 --- a/yarn-project/circuits.js/src/structs/global_variables.ts +++ b/yarn-project/circuits.js/src/structs/global_variables.ts @@ -1,5 +1,5 @@ import { Fr } from '@aztec/foundation/fields'; -import { BufferReader, serializeToBuffer } from '@aztec/foundation/serialize'; +import { BufferReader, FieldReader, serializeToBuffer } from '@aztec/foundation/serialize'; import { FieldsOf } from '@aztec/foundation/types'; /** @@ -52,6 +52,12 @@ export class GlobalVariables { ); } + static fromFields(fields: Fr[] | FieldReader): GlobalVariables { + const reader = FieldReader.asReader(fields); + + return new GlobalVariables(reader.readField(), reader.readField(), reader.readField(), reader.readField()); + } + static getFields(fields: FieldsOf) { // Note: The order here must match the order in the HeaderLib solidity library. return [fields.chainId, fields.version, fields.blockNumber, fields.timestamp] as const; @@ -61,7 +67,7 @@ export class GlobalVariables { return serializeToBuffer(...GlobalVariables.getFields(this)); } - toFieldArray() { + toFields() { return GlobalVariables.getFields(this); } diff --git a/yarn-project/circuits.js/src/structs/header.test.ts b/yarn-project/circuits.js/src/structs/header.test.ts index 72d49ae9bf2..9f25ffc0648 100644 --- a/yarn-project/circuits.js/src/structs/header.test.ts +++ b/yarn-project/circuits.js/src/structs/header.test.ts @@ -14,8 +14,8 @@ describe('Header', () => { const randomInt = Math.floor(Math.random() * 1000); const expected = makeHeader(randomInt, undefined); - const fieldArray = expected.toFieldArray(); - const res = Header.fromFieldArray(fieldArray); + const fieldArray = expected.toFields(); + const res = Header.fromFields(fieldArray); expect(res).toEqual(expected); }); diff --git a/yarn-project/circuits.js/src/structs/header.ts b/yarn-project/circuits.js/src/structs/header.ts index 016f9e9f98e..5923e869541 100644 --- a/yarn-project/circuits.js/src/structs/header.ts +++ b/yarn-project/circuits.js/src/structs/header.ts @@ -1,10 +1,9 @@ import { pedersenHash } from '@aztec/foundation/crypto'; import { Fr } from '@aztec/foundation/fields'; -import { BufferReader, from2Fields, serializeToBuffer, to2Fields } from '@aztec/foundation/serialize'; +import { BufferReader, FieldReader, from2Fields, serializeToBuffer, to2Fields } from '@aztec/foundation/serialize'; import { GeneratorIndex, HEADER_LENGTH } from '../constants.gen.js'; import { GlobalVariables } from './global_variables.js'; -import { PartialStateReference } from './partial_state_reference.js'; import { AppendOnlyTreeSnapshot } from './rollup/append_only_tree_snapshot.js'; import { StateReference } from './state_reference.js'; @@ -32,13 +31,13 @@ export class Header { return serializeToBuffer(this.lastArchive, this.bodyHash, this.state, this.globalVariables); } - toFieldArray(): Fr[] { + toFields(): Fr[] { // Note: The order here must match the order in header.nr const serialized = [ - ...this.lastArchive.toFieldArray(), + ...this.lastArchive.toFields(), ...to2Fields(this.bodyHash), ...this.state.toFieldArray(), - ...this.globalVariables.toFieldArray(), + ...this.globalVariables.toFields(), ]; if (serialized.length !== HEADER_LENGTH) { throw new Error(`Expected header to have ${HEADER_LENGTH} fields, but it has ${serialized.length} fields`); @@ -57,23 +56,13 @@ export class Header { ); } - static fromFieldArray(fields: Fr[]): Header { - if (fields.length !== HEADER_LENGTH) { - throw new Error(`Expected header to have ${HEADER_LENGTH} fields, but it has ${fields.length} fields`); - } - // Note: The order here must match the order in header.nr - const lastArchive = new AppendOnlyTreeSnapshot(fields[0], Number(fields[1].toBigInt())); - const bodyHash = from2Fields(fields[2], fields[3]); - const state = new StateReference( - new AppendOnlyTreeSnapshot(fields[4], Number(fields[5].toBigInt())), - new PartialStateReference( - new AppendOnlyTreeSnapshot(fields[6], Number(fields[7].toBigInt())), - new AppendOnlyTreeSnapshot(fields[8], Number(fields[9].toBigInt())), - new AppendOnlyTreeSnapshot(fields[10], Number(fields[11].toBigInt())), - new AppendOnlyTreeSnapshot(fields[12], Number(fields[13].toBigInt())), - ), - ); - const globalVariables = new GlobalVariables(fields[14], fields[15], fields[16], fields[17]); + static fromFields(fields: Fr[] | FieldReader): Header { + const reader = FieldReader.asReader(fields); + + const lastArchive = new AppendOnlyTreeSnapshot(reader.readField(), Number(reader.readField().toBigInt())); + const bodyHash = from2Fields(reader.readField(), reader.readField()); + const state = StateReference.fromFields(reader); + const globalVariables = GlobalVariables.fromFields(reader); return new Header(lastArchive, bodyHash, state, globalVariables); } @@ -112,7 +101,7 @@ export class Header { hash(): Fr { return Fr.fromBuffer( pedersenHash( - this.toFieldArray().map(f => f.toBuffer()), + this.toFields().map(f => f.toBuffer()), GeneratorIndex.BLOCK_HASH, ), ); diff --git a/yarn-project/circuits.js/src/structs/index.ts b/yarn-project/circuits.js/src/structs/index.ts index ca2cb39c1c6..2218c043152 100644 --- a/yarn-project/circuits.js/src/structs/index.ts +++ b/yarn-project/circuits.js/src/structs/index.ts @@ -4,6 +4,7 @@ export * from './call_context.js'; export * from './call_request.js'; export * from './call_stack_item.js'; export * from './complete_address.js'; +export * from './contract_deployment_data.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/partial_state_reference.ts b/yarn-project/circuits.js/src/structs/partial_state_reference.ts index 7e375eeb12a..369b0732c41 100644 --- a/yarn-project/circuits.js/src/structs/partial_state_reference.ts +++ b/yarn-project/circuits.js/src/structs/partial_state_reference.ts @@ -1,4 +1,5 @@ -import { BufferReader, serializeToBuffer } from '@aztec/foundation/serialize'; +import { Fr } from '@aztec/foundation/fields'; +import { BufferReader, FieldReader, serializeToBuffer } from '@aztec/foundation/serialize'; import { AppendOnlyTreeSnapshot } from './rollup/append_only_tree_snapshot.js'; @@ -27,6 +28,17 @@ export class PartialStateReference { ); } + static fromFields(fields: Fr[] | FieldReader): PartialStateReference { + const reader = FieldReader.asReader(fields); + + const noteHashTree = AppendOnlyTreeSnapshot.fromFields(reader); + const nullifierTree = AppendOnlyTreeSnapshot.fromFields(reader); + const contractTree = AppendOnlyTreeSnapshot.fromFields(reader); + const publicDataTree = AppendOnlyTreeSnapshot.fromFields(reader); + + return new PartialStateReference(noteHashTree, nullifierTree, contractTree, publicDataTree); + } + static empty(): PartialStateReference { return new PartialStateReference( AppendOnlyTreeSnapshot.zero(), @@ -40,12 +52,12 @@ export class PartialStateReference { return serializeToBuffer(this.noteHashTree, this.nullifierTree, this.contractTree, this.publicDataTree); } - toFieldArray() { + toFields() { return [ - ...this.noteHashTree.toFieldArray(), - ...this.nullifierTree.toFieldArray(), - ...this.contractTree.toFieldArray(), - ...this.publicDataTree.toFieldArray(), + ...this.noteHashTree.toFields(), + ...this.nullifierTree.toFields(), + ...this.contractTree.toFields(), + ...this.publicDataTree.toFields(), ]; } diff --git a/yarn-project/circuits.js/src/structs/private_circuit_public_inputs.test.ts b/yarn-project/circuits.js/src/structs/private_circuit_public_inputs.test.ts index a5f54760c11..fd7cea4b38d 100644 --- a/yarn-project/circuits.js/src/structs/private_circuit_public_inputs.test.ts +++ b/yarn-project/circuits.js/src/structs/private_circuit_public_inputs.test.ts @@ -1,6 +1,21 @@ +import { makePrivateCircuitPublicInputs } from '../tests/factories.js'; import { PrivateCircuitPublicInputs } from './private_circuit_public_inputs.js'; describe('PrivateCircuitPublicInputs', () => { + it('serializes to buffer and back', () => { + const target = makePrivateCircuitPublicInputs(100); + const buffer = target.toBuffer(); + const result = PrivateCircuitPublicInputs.fromBuffer(buffer); + expect(result).toEqual(target); + }); + + it('serializes to fields and back', () => { + const target = makePrivateCircuitPublicInputs(100); + const fields = target.toFields(); + const result = PrivateCircuitPublicInputs.fromFields(fields); + expect(result).toEqual(target); + }); + it(`initializes an empty PrivateCircuitPublicInputs`, () => { const target = PrivateCircuitPublicInputs.empty(); expect(target.isEmpty()).toBe(true); diff --git a/yarn-project/circuits.js/src/structs/private_circuit_public_inputs.ts b/yarn-project/circuits.js/src/structs/private_circuit_public_inputs.ts index 9ddd2fbcdde..c5f7332d6c6 100644 --- a/yarn-project/circuits.js/src/structs/private_circuit_public_inputs.ts +++ b/yarn-project/circuits.js/src/structs/private_circuit_public_inputs.ts @@ -1,7 +1,13 @@ import { makeTuple } from '@aztec/foundation/array'; import { isArrayEmpty } from '@aztec/foundation/collection'; import { Fr } from '@aztec/foundation/fields'; -import { BufferReader, Tuple, serializeToBuffer } from '@aztec/foundation/serialize'; +import { + BufferReader, + FieldReader, + Tuple, + serializeToBuffer, + serializeToFieldArray, +} from '@aztec/foundation/serialize'; import { FieldsOf } from '@aztec/foundation/types'; import { @@ -16,9 +22,8 @@ import { RETURN_VALUES_LENGTH, } from '../constants.gen.js'; import { CallContext } from './call_context.js'; -import { Header, SideEffect, SideEffectLinkedToNoteHash } from './index.js'; +import { ContractDeploymentData, Header, SideEffect, SideEffectLinkedToNoteHash } from './index.js'; import { NullifierKeyValidationRequest } from './nullifier_key_validation_request.js'; -import { ContractDeploymentData } from './tx_context.js'; /** * Public inputs to a private circuit. @@ -154,6 +159,31 @@ export class PrivateCircuitPublicInputs { ); } + static fromFields(fields: Fr[] | FieldReader): PrivateCircuitPublicInputs { + const reader = FieldReader.asReader(fields); + return new PrivateCircuitPublicInputs( + reader.readObject(CallContext), + reader.readField(), + reader.readFieldArray(RETURN_VALUES_LENGTH), + reader.readArray(MAX_READ_REQUESTS_PER_CALL, SideEffect), + reader.readArray(MAX_NULLIFIER_KEY_VALIDATION_REQUESTS_PER_CALL, NullifierKeyValidationRequest), + reader.readArray(MAX_NEW_COMMITMENTS_PER_CALL, SideEffect), + reader.readArray(MAX_NEW_NULLIFIERS_PER_CALL, SideEffectLinkedToNoteHash), + reader.readFieldArray(MAX_PRIVATE_CALL_STACK_LENGTH_PER_CALL), + reader.readFieldArray(MAX_PUBLIC_CALL_STACK_LENGTH_PER_CALL), + reader.readFieldArray(MAX_NEW_L2_TO_L1_MSGS_PER_CALL), + reader.readField(), + reader.readFieldArray(NUM_FIELDS_PER_SHA256), + reader.readFieldArray(NUM_FIELDS_PER_SHA256), + reader.readField(), + reader.readField(), + reader.readObject(Header), + reader.readObject(ContractDeploymentData), + reader.readField(), + reader.readField(), + ); + } + /** * Create an empty PrivateCircuitPublicInputs. * @returns An empty PrivateCircuitPublicInputs object. @@ -237,6 +267,7 @@ export class PrivateCircuitPublicInputs { fields.version, ] as const; } + /** * Serialize this as a buffer. * @returns The buffer. @@ -244,4 +275,11 @@ export class PrivateCircuitPublicInputs { toBuffer(): Buffer { return serializeToBuffer(...PrivateCircuitPublicInputs.getFields(this)); } + + /** + * Serialize this as a field array. + */ + toFields(): Fr[] { + return serializeToFieldArray(...PrivateCircuitPublicInputs.getFields(this)); + } } diff --git a/yarn-project/circuits.js/src/structs/rollup/append_only_tree_snapshot.ts b/yarn-project/circuits.js/src/structs/rollup/append_only_tree_snapshot.ts index 09216a8a0a1..6f39790a162 100644 --- a/yarn-project/circuits.js/src/structs/rollup/append_only_tree_snapshot.ts +++ b/yarn-project/circuits.js/src/structs/rollup/append_only_tree_snapshot.ts @@ -1,5 +1,5 @@ import { Fr } from '@aztec/foundation/fields'; -import { BufferReader, serializeToBuffer } from '@aztec/foundation/serialize'; +import { BufferReader, FieldReader, serializeToBuffer } from '@aztec/foundation/serialize'; import { STRING_ENCODING, UInt32 } from '../shared.js'; @@ -30,7 +30,7 @@ export class AppendOnlyTreeSnapshot { return serializeToBuffer(this.root, this.nextAvailableLeafIndex); } - toFieldArray(): Fr[] { + toFields(): Fr[] { return [this.root, new Fr(this.nextAvailableLeafIndex)]; } @@ -47,6 +47,12 @@ export class AppendOnlyTreeSnapshot { return AppendOnlyTreeSnapshot.fromBuffer(Buffer.from(str, STRING_ENCODING)); } + static fromFields(fields: Fr[] | FieldReader): AppendOnlyTreeSnapshot { + const reader = FieldReader.asReader(fields); + + return new AppendOnlyTreeSnapshot(reader.readField(), Number(reader.readField().toBigInt())); + } + static zero() { return new AppendOnlyTreeSnapshot(Fr.ZERO, 0); } diff --git a/yarn-project/circuits.js/src/structs/state_reference.ts b/yarn-project/circuits.js/src/structs/state_reference.ts index 284d988e07f..1e312739c15 100644 --- a/yarn-project/circuits.js/src/structs/state_reference.ts +++ b/yarn-project/circuits.js/src/structs/state_reference.ts @@ -1,5 +1,5 @@ import { Fr } from '@aztec/foundation/fields'; -import { BufferReader, serializeToBuffer } from '@aztec/foundation/serialize'; +import { BufferReader, FieldReader, serializeToBuffer } from '@aztec/foundation/serialize'; import { PartialStateReference } from './partial_state_reference.js'; import { AppendOnlyTreeSnapshot } from './rollup/append_only_tree_snapshot.js'; @@ -21,7 +21,7 @@ export class StateReference { } toFieldArray(): Fr[] { - return [...this.l1ToL2MessageTree.toFieldArray(), ...this.partial.toFieldArray()]; + return [...this.l1ToL2MessageTree.toFields(), ...this.partial.toFields()]; } static fromBuffer(buffer: Buffer | BufferReader): StateReference { @@ -29,6 +29,15 @@ export class StateReference { return new StateReference(reader.readObject(AppendOnlyTreeSnapshot), reader.readObject(PartialStateReference)); } + static fromFields(fields: Fr[] | FieldReader): StateReference { + const reader = FieldReader.asReader(fields); + + const l1ToL2MessageTree = AppendOnlyTreeSnapshot.fromFields(reader); + const partial = PartialStateReference.fromFields(reader); + + return new StateReference(l1ToL2MessageTree, partial); + } + static empty(): StateReference { return new StateReference(AppendOnlyTreeSnapshot.zero(), PartialStateReference.empty()); } diff --git a/yarn-project/circuits.js/src/structs/tx_context.ts b/yarn-project/circuits.js/src/structs/tx_context.ts index f84a47b5b8c..07becc7f95e 100644 --- a/yarn-project/circuits.js/src/structs/tx_context.ts +++ b/yarn-project/circuits.js/src/structs/tx_context.ts @@ -1,83 +1,10 @@ import { BufferReader, serializeToBuffer } from '@aztec/foundation/serialize'; import { FieldsOf } from '@aztec/foundation/types'; -import { PublicKey } from '../index.js'; -import { AztecAddress, EthAddress, Fr, Point } from './index.js'; - -/** - * Contract deployment data in a TxContext - * Not to be confused with NewContractData. - */ -export class ContractDeploymentData { - /** Ethereum address of the portal contract on L1. */ - public portalContractAddress: EthAddress; - - constructor( - /** Public key of the contract. */ - public publicKey: PublicKey, - /** Hash of the initialization payload. */ - public initializationHash: Fr, - /** Contract class identifier. */ - public contractClassId: Fr, - /** Contract address salt (used when deriving a contract address). */ - public contractAddressSalt: Fr, - /** - * Ethereum address of the portal contract on L1. - * TODO(AD): union type kludge due to cbind compiler having special needs - */ - portalContractAddress: EthAddress | AztecAddress, - ) { - this.portalContractAddress = EthAddress.fromField(portalContractAddress.toField()); - } - - toBuffer() { - return serializeToBuffer( - this.publicKey, - this.initializationHash, - this.contractClassId, - this.contractAddressSalt, - this.portalContractAddress, - ); - } - - /** - * Returns an empty ContractDeploymentData. - * @returns The empty ContractDeploymentData. - */ - public static empty(): ContractDeploymentData { - return new ContractDeploymentData(Point.ZERO, Fr.ZERO, Fr.ZERO, Fr.ZERO, EthAddress.ZERO); - } - - isEmpty() { - return ( - this.publicKey.isZero() && - this.initializationHash.isZero() && - this.contractClassId.isZero() && - this.contractAddressSalt.isZero() && - this.portalContractAddress.isZero() - ); - } - - /** - * Deserializes contract deployment data rom a buffer or reader. - * @param buffer - Buffer to read from. - * @returns The deserialized ContractDeploymentData. - */ - static fromBuffer(buffer: Buffer | BufferReader): ContractDeploymentData { - const reader = BufferReader.asReader(buffer); - return new ContractDeploymentData( - reader.readObject(Point), - Fr.fromBuffer(reader), - Fr.fromBuffer(reader), - Fr.fromBuffer(reader), - new EthAddress(reader.readBytes(32)), - ); - } -} +import { ContractDeploymentData, Fr } from './index.js'; /** * Transaction context. - * @see cpp/src/aztec3/circuits/abis/tx_context.hpp. */ export class TxContext { constructor( diff --git a/yarn-project/foundation/src/serialize/serialize.ts b/yarn-project/foundation/src/serialize/serialize.ts index 5ab8a8ac5c4..d8eef08529b 100644 --- a/yarn-project/foundation/src/serialize/serialize.ts +++ b/yarn-project/foundation/src/serialize/serialize.ts @@ -1,5 +1,6 @@ import { toBigIntBE, toBufferBE } from '@aztec/foundation/bigint-buffer'; +import { Fr } from '../fields/fields.js'; import { numToUInt32BE } from './free_funcs.js'; /** @@ -118,6 +119,22 @@ export type Bufferable = } | Bufferable[]; +/** A type that can be converted to a Field or a Field array. */ +export type Fieldeable = + | Fr + | boolean + | number + | bigint + | { + /** Serialize to a field. */ + toField: () => Fr; + } + | { + /** Serialize to an array of fields. */ + toFields: () => Fr[]; + } + | Fieldeable[]; + /** * Checks whether an object implements the toBuffer32 method. * @param obj - The object to check. @@ -168,6 +185,29 @@ export function serializeToBufferArray(...objs: Bufferable[]): Buffer[] { return ret; } +/** + * Serializes a list of objects contiguously. + * @param objs - Objects to serialize. + * @returns An array of fields with the concatenation of all fields. + */ +export function serializeToFieldArray(...objs: Fieldeable[]): Fr[] { + let ret: Fr[] = []; + for (const obj of objs) { + if (Array.isArray(obj)) { + ret = [...ret, ...serializeToFieldArray(...obj)]; + } else if (obj instanceof Fr) { + ret.push(obj); + } else if (typeof obj === 'boolean' || typeof obj === 'number' || typeof obj === 'bigint') { + ret.push(new Fr(obj)); + } else if ('toFields' in obj) { + ret = [...ret, ...obj.toFields()]; + } else { + ret.push(obj.toField()); + } + } + return ret; +} + /** * Serializes a list of objects contiguously. * @param objs - Objects to serialize. diff --git a/yarn-project/noir-protocol-circuits/src/type_conversion.test.ts b/yarn-project/noir-protocol-circuits/src/type_conversion.test.ts index 96295999765..562d9aa09e0 100644 --- a/yarn-project/noir-protocol-circuits/src/type_conversion.test.ts +++ b/yarn-project/noir-protocol-circuits/src/type_conversion.test.ts @@ -59,7 +59,7 @@ describe('Noir<>Circuits.js type conversion test suite', () => { new Fr(29n), new Fr(30n), new Fr(31n), - AztecAddress.random(), + EthAddress.random(), ); it('should map contract deployment data', () => { diff --git a/yarn-project/pxe/src/simulator_oracle/index.ts b/yarn-project/pxe/src/simulator_oracle/index.ts index a7b2a4dcd81..6077752fdcb 100644 --- a/yarn-project/pxe/src/simulator_oracle/index.ts +++ b/yarn-project/pxe/src/simulator_oracle/index.ts @@ -123,11 +123,7 @@ export class SimulatorOracle implements DBOracle { const message = messageAndIndex.message.toFieldArray(); const index = messageAndIndex.index; const siblingPath = await this.stateInfoProvider.getL1ToL2MessageSiblingPath('latest', index); - return { - message, - siblingPath: siblingPath.toFieldArray(), - index, - }; + return new MessageLoadOracleInputs(message, index, siblingPath.toFieldArray()); } /** diff --git a/yarn-project/sequencer-client/src/simulator/public_executor.ts b/yarn-project/sequencer-client/src/simulator/public_executor.ts index 9015fb06bc4..de082839ab0 100644 --- a/yarn-project/sequencer-client/src/simulator/public_executor.ts +++ b/yarn-project/sequencer-client/src/simulator/public_executor.ts @@ -150,11 +150,7 @@ export class WorldStateDB implements CommitmentsDB { const index = (await this.db.findLeafIndex(MerkleTreeId.L1_TO_L2_MESSAGE_TREE, messageKey.toBuffer()))!; const siblingPath = await this.db.getSiblingPath(MerkleTreeId.L1_TO_L2_MESSAGE_TREE, index); - return { - message: message.toFieldArray(), - siblingPath: siblingPath.toFieldArray(), - index, - }; + return new MessageLoadOracleInputs(message.toFieldArray(), index, siblingPath.toFieldArray()); } public async getCommitmentIndex(commitment: Fr): Promise {