From ab8362858eafc5aeea8192f62a5ec8524798932f Mon Sep 17 00:00:00 2001 From: Santiago Palladino Date: Tue, 19 Nov 2024 09:14:19 -0300 Subject: [PATCH 1/2] chore: Skip emitting public bytecode Skips emitting the event with the contract public bytecode when registering the contract class. This allows for smaller L1 txs so they fit in Sepolia. This is a temporary hack to be reverted. To get bytecode into the nodes, we push it forcefully from the PXE whenever we register a new contract. However, this only gets the bytecode into the node that the PXE is connected to. To avoid nodes or prover nodes from missing bytecode that is to be used for known deployments, such as the token or token bridge contracts, we now manually register them on initialization. --- .../src/events/class_registered.nr | 18 +-- .../src/main.nr | 30 +++-- yarn-project/archiver/package.json | 2 +- .../archiver/src/archiver/archiver.ts | 12 +- yarn-project/archiver/src/factory.ts | 24 +++- yarn-project/archiver/tsconfig.json | 6 +- .../aztec-node/src/aztec-node/server.ts | 6 + .../src/interfaces/archiver.test.ts | 12 ++ .../circuit-types/src/interfaces/archiver.ts | 2 + .../src/interfaces/aztec-node.test.ts | 8 ++ .../src/interfaces/aztec-node.ts | 10 ++ .../interfaces/contract_data_source.ts | 6 + .../contract_class_registration.test.ts | 108 +++++++++++------- .../src/e2e_deploy_contract/legacy.test.ts | 3 +- .../pxe/src/pxe_service/pxe_service.ts | 8 +- .../util/txe_public_contract_data_source.ts | 6 + 16 files changed, 191 insertions(+), 70 deletions(-) diff --git a/noir-projects/noir-contracts/contracts/contract_class_registerer_contract/src/events/class_registered.nr b/noir-projects/noir-contracts/contracts/contract_class_registerer_contract/src/events/class_registered.nr index 3a5e11515ed6..7537442d287c 100644 --- a/noir-projects/noir-contracts/contracts/contract_class_registerer_contract/src/events/class_registered.nr +++ b/noir-projects/noir-contracts/contracts/contract_class_registerer_contract/src/events/class_registered.nr @@ -1,29 +1,33 @@ use dep::aztec::protocol_types::{ constants::{ - MAX_PACKED_PUBLIC_BYTECODE_SIZE_IN_FIELDS, REGISTERER_CONTRACT_CLASS_REGISTERED_MAGIC_VALUE, + REGISTERER_CONTRACT_CLASS_REGISTERED_MAGIC_VALUE, }, contract_class_id::ContractClassId, traits::Serialize, }; -// #[event] +// TODO(#10007): Use MAX_PACKED_PUBLIC_BYTECODE_SIZE_IN_FIELDS instead +pub global MAX_BROADCASTEABLE_PACKED_PUBLIC_BYTECODE_SIZE_IN_FIELDS: u32 = 100; + pub struct ContractClassRegistered { contract_class_id: ContractClassId, version: Field, artifact_hash: Field, private_functions_root: Field, - packed_public_bytecode: [Field; MAX_PACKED_PUBLIC_BYTECODE_SIZE_IN_FIELDS], + packed_public_bytecode: [Field; MAX_BROADCASTEABLE_PACKED_PUBLIC_BYTECODE_SIZE_IN_FIELDS], } -impl Serialize for ContractClassRegistered { - fn serialize(self: Self) -> [Field; MAX_PACKED_PUBLIC_BYTECODE_SIZE_IN_FIELDS + 5] { - let mut packed = [0; MAX_PACKED_PUBLIC_BYTECODE_SIZE_IN_FIELDS + 5]; +impl Serialize for ContractClassRegistered { + fn serialize( + self: Self, + ) -> [Field; MAX_BROADCASTEABLE_PACKED_PUBLIC_BYTECODE_SIZE_IN_FIELDS + 5] { + let mut packed = [0; MAX_BROADCASTEABLE_PACKED_PUBLIC_BYTECODE_SIZE_IN_FIELDS + 5]; packed[0] = REGISTERER_CONTRACT_CLASS_REGISTERED_MAGIC_VALUE; packed[1] = self.contract_class_id.to_field(); packed[2] = self.version; packed[3] = self.artifact_hash; packed[4] = self.private_functions_root; - for i in 0..MAX_PACKED_PUBLIC_BYTECODE_SIZE_IN_FIELDS { + for i in 0..MAX_BROADCASTEABLE_PACKED_PUBLIC_BYTECODE_SIZE_IN_FIELDS { packed[i + 5] = self.packed_public_bytecode[i]; } packed diff --git a/noir-projects/noir-contracts/contracts/contract_class_registerer_contract/src/main.nr b/noir-projects/noir-contracts/contracts/contract_class_registerer_contract/src/main.nr index 8464b960fb01..1e8fb176fd10 100644 --- a/noir-projects/noir-contracts/contracts/contract_class_registerer_contract/src/main.nr +++ b/noir-projects/noir-contracts/contracts/contract_class_registerer_contract/src/main.nr @@ -22,7 +22,9 @@ contract ContractClassRegisterer { }; use crate::events::{ - class_registered::ContractClassRegistered, + class_registered::{ + ContractClassRegistered, MAX_BROADCASTEABLE_PACKED_PUBLIC_BYTECODE_SIZE_IN_FIELDS, + }, private_function_broadcasted::{ ClassPrivateFunctionBroadcasted, InnerPrivateFunction, PrivateFunction, }, @@ -81,13 +83,6 @@ contract ContractClassRegisterer { ); // Emit the contract class id as a nullifier to be able to prove that this class has been (not) registered - let event = ContractClassRegistered { - contract_class_id, - version: 1, - artifact_hash, - private_functions_root, - packed_public_bytecode, - }; context.push_nullifier(contract_class_id.to_field()); // Broadcast class info including public bytecode @@ -100,7 +95,24 @@ contract ContractClassRegisterer { public_bytecode_commitment, ], ); - emit_contract_class_log(&mut context, event.serialize()); + + // TODO(#10007): Drop this conditional and always emit the bytecode. We allow skipping the broadcast + // as a stopgap solution to allow txs to fit in Sepolia when we broadcast public bytecode. + if bytecode_length_in_fields <= MAX_BROADCASTEABLE_PACKED_PUBLIC_BYTECODE_SIZE_IN_FIELDS { + let mut event_public_bytecode = + [0; MAX_BROADCASTEABLE_PACKED_PUBLIC_BYTECODE_SIZE_IN_FIELDS]; + for i in 0..MAX_BROADCASTEABLE_PACKED_PUBLIC_BYTECODE_SIZE_IN_FIELDS { + event_public_bytecode[i] = packed_public_bytecode[i]; + } + let event = ContractClassRegistered { + contract_class_id, + version: 1, + artifact_hash, + private_functions_root, + packed_public_bytecode: event_public_bytecode, + }; + emit_contract_class_log(&mut context, event.serialize()); + } } #[private] diff --git a/yarn-project/archiver/package.json b/yarn-project/archiver/package.json index ff0dd170380f..8ce9df4a6323 100644 --- a/yarn-project/archiver/package.json +++ b/yarn-project/archiver/package.json @@ -71,6 +71,7 @@ "@aztec/foundation": "workspace:^", "@aztec/kv-store": "workspace:^", "@aztec/l1-artifacts": "workspace:^", + "@aztec/noir-contracts.js": "workspace:^", "@aztec/protocol-contracts": "workspace:^", "@aztec/telemetry-client": "workspace:^", "@aztec/types": "workspace:^", @@ -83,7 +84,6 @@ "ws": "^8.13.0" }, "devDependencies": { - "@aztec/noir-contracts.js": "workspace:^", "@jest/globals": "^29.5.0", "@types/debug": "^4.1.7", "@types/jest": "^29.5.0", diff --git a/yarn-project/archiver/src/archiver/archiver.ts b/yarn-project/archiver/src/archiver/archiver.ts index a2a10d8c7a26..b2d8887dd971 100644 --- a/yarn-project/archiver/src/archiver/archiver.ts +++ b/yarn-project/archiver/src/archiver/archiver.ts @@ -712,6 +712,12 @@ export class Archiver implements ArchiveSource { return this.store.getContractClassIds(); } + // TODO(#10007): Remove this method + async addContractClass(contractClass: ContractClassPublic): Promise { + await this.store.addContractClasses([contractClass], 0); + return; + } + addContractArtifact(address: AztecAddress, artifact: ContractArtifact): Promise { return this.store.addContractArtifact(address, artifact); } @@ -764,7 +770,6 @@ class ArchiverStoreHelper ArchiverDataStore, | 'addLogs' | 'deleteLogs' - | 'addContractClasses' | 'deleteContractClasses' | 'addContractInstances' | 'deleteContractInstances' @@ -775,6 +780,11 @@ class ArchiverStoreHelper constructor(private readonly store: ArchiverDataStore) {} + // TODO(#10007): Remove this method + addContractClasses(contractClasses: ContractClassPublic[], blockNum: number): Promise { + return this.store.addContractClasses(contractClasses, blockNum); + } + /** * Extracts and stores contract classes out of ContractClassRegistered events emitted by the class registerer contract. * @param allLogs - All logs emitted in a bunch of blocks. diff --git a/yarn-project/archiver/src/factory.ts b/yarn-project/archiver/src/factory.ts index e439ab370d59..28ed2ec035bb 100644 --- a/yarn-project/archiver/src/factory.ts +++ b/yarn-project/archiver/src/factory.ts @@ -1,9 +1,10 @@ import { type ArchiverApi, type Service } from '@aztec/circuit-types'; -import { type ContractClassPublic } from '@aztec/circuits.js'; +import { type ContractClassPublic, getContractClassFromArtifact } from '@aztec/circuits.js'; import { createDebugLogger } from '@aztec/foundation/log'; import { type Maybe } from '@aztec/foundation/types'; import { type DataStoreConfig } from '@aztec/kv-store/config'; import { createStore } from '@aztec/kv-store/utils'; +import { TokenBridgeContractArtifact, TokenContractArtifact } from '@aztec/noir-contracts.js'; import { getCanonicalProtocolContract, protocolContractNames } from '@aztec/protocol-contracts'; import { type TelemetryClient } from '@aztec/telemetry-client'; import { NoopTelemetryClient } from '@aztec/telemetry-client/noop'; @@ -21,14 +22,15 @@ export async function createArchiver( if (!config.archiverUrl) { const store = await createStore('archiver', config, createDebugLogger('aztec:archiver:lmdb')); const archiverStore = new KVArchiverDataStore(store, config.maxLogs); - await initWithProtocolContracts(archiverStore); + await registerProtocolContracts(archiverStore); + await registerCommonContracts(archiverStore); return Archiver.createAndSync(config, archiverStore, telemetry, opts.blockUntilSync); } else { return createArchiverClient(config.archiverUrl); } } -async function initWithProtocolContracts(store: KVArchiverDataStore) { +async function registerProtocolContracts(store: KVArchiverDataStore) { const blockNumber = 0; for (const name of protocolContractNames) { const contract = getCanonicalProtocolContract(name); @@ -42,3 +44,19 @@ async function initWithProtocolContracts(store: KVArchiverDataStore) { await store.addContractInstances([contract.instance], blockNumber); } } + +// TODO(#10007): Remove this method. We are explicitly registering these contracts +// here to ensure they are available to all nodes and all prover nodes, since the PXE +// was tweaked to automatically push contract classes to the node it is registered, +// but other nodes in the network may require the contract classes to be registered as well. +// TODO(#10007): Remove the dependency on noir-contracts.js from this package once we remove this. +async function registerCommonContracts(store: KVArchiverDataStore) { + const blockNumber = 0; + const artifacts = [TokenBridgeContractArtifact, TokenContractArtifact]; + const classes = artifacts.map(artifact => ({ + ...getContractClassFromArtifact(artifact), + privateFunctions: [], + unconstrainedFunctions: [], + })); + await store.addContractClasses(classes, blockNumber); +} diff --git a/yarn-project/archiver/tsconfig.json b/yarn-project/archiver/tsconfig.json index dbe9915c0107..901d60f657d1 100644 --- a/yarn-project/archiver/tsconfig.json +++ b/yarn-project/archiver/tsconfig.json @@ -24,6 +24,9 @@ { "path": "../l1-artifacts" }, + { + "path": "../noir-contracts.js" + }, { "path": "../protocol-contracts" }, @@ -32,9 +35,6 @@ }, { "path": "../types" - }, - { - "path": "../noir-contracts.js" } ], "include": ["src"] diff --git a/yarn-project/aztec-node/src/aztec-node/server.ts b/yarn-project/aztec-node/src/aztec-node/server.ts index c71e811edc04..058d5d6a7702 100644 --- a/yarn-project/aztec-node/src/aztec-node/server.ts +++ b/yarn-project/aztec-node/src/aztec-node/server.ts @@ -812,7 +812,13 @@ export class AztecNodeService implements AztecNode { }); } + // TODO(#10007): Remove this method + public addContractClass(contractClass: ContractClassPublic): Promise { + return this.contractDataSource.addContractClass(contractClass); + } + public addContractArtifact(address: AztecAddress, artifact: ContractArtifact): Promise { + // TODO: Node should validate the artifact before accepting it return this.contractDataSource.addContractArtifact(address, artifact); } diff --git a/yarn-project/circuit-types/src/interfaces/archiver.test.ts b/yarn-project/circuit-types/src/interfaces/archiver.test.ts index b6b411c31770..6003a86e4270 100644 --- a/yarn-project/circuit-types/src/interfaces/archiver.test.ts +++ b/yarn-project/circuit-types/src/interfaces/archiver.test.ts @@ -232,6 +232,15 @@ describe('ArchiverApiSchema', () => { version: 1, }); }); + + it('addContractClass', async () => { + const contractClass = getContractClassFromArtifact(artifact); + await context.client.addContractClass({ + ...omit(contractClass, 'publicBytecodeCommitment'), + unconstrainedFunctions: [], + privateFunctions: [], + }); + }); }); class MockArchiver implements ArchiverApi { @@ -362,4 +371,7 @@ class MockArchiver implements ArchiverApi { expect(l1ToL2Message).toBeInstanceOf(Fr); return Promise.resolve(1n); } + addContractClass(_contractClass: ContractClassPublic): Promise { + return Promise.resolve(); + } } diff --git a/yarn-project/circuit-types/src/interfaces/archiver.ts b/yarn-project/circuit-types/src/interfaces/archiver.ts index 0dc118875aa7..4e32edb2b8fc 100644 --- a/yarn-project/circuit-types/src/interfaces/archiver.ts +++ b/yarn-project/circuit-types/src/interfaces/archiver.ts @@ -70,4 +70,6 @@ export const ArchiverApiSchema: ApiSchemaFor = { addContractArtifact: z.function().args(schemas.AztecAddress, ContractArtifactSchema).returns(z.void()), getL1ToL2Messages: z.function().args(schemas.BigInt).returns(z.array(schemas.Fr)), getL1ToL2MessageIndex: z.function().args(schemas.Fr).returns(schemas.BigInt.optional()), + // TODO(#10007): Remove this method + addContractClass: z.function().args(ContractClassPublicSchema).returns(z.void()), }; diff --git a/yarn-project/circuit-types/src/interfaces/aztec-node.test.ts b/yarn-project/circuit-types/src/interfaces/aztec-node.test.ts index d415249c3f4b..2e810f65b3f0 100644 --- a/yarn-project/circuit-types/src/interfaces/aztec-node.test.ts +++ b/yarn-project/circuit-types/src/interfaces/aztec-node.test.ts @@ -313,6 +313,11 @@ describe('AztecNodeApiSchema', () => { const response = await context.client.getEpochProofQuotes(1n); expect(response).toEqual([expect.any(EpochProofQuote)]); }); + + it('addContractClass', async () => { + const contractClass = getContractClassFromArtifact(artifact); + await context.client.addContractClass({ ...contractClass, unconstrainedFunctions: [], privateFunctions: [] }); + }); }); class MockAztecNode implements AztecNode { @@ -538,4 +543,7 @@ class MockAztecNode implements AztecNode { expect(epoch).toEqual(1n); return Promise.resolve([EpochProofQuote.random()]); } + addContractClass(_contractClass: ContractClassPublic): Promise { + return Promise.resolve(); + } } diff --git a/yarn-project/circuit-types/src/interfaces/aztec-node.ts b/yarn-project/circuit-types/src/interfaces/aztec-node.ts index 91b7dd8d0ca8..b2a6c6a3149b 100644 --- a/yarn-project/circuit-types/src/interfaces/aztec-node.ts +++ b/yarn-project/circuit-types/src/interfaces/aztec-node.ts @@ -391,6 +391,13 @@ export interface AztecNode extends ProverCoordination { * @param epoch - The epoch for which to get the quotes */ getEpochProofQuotes(epoch: bigint): Promise; + + /** + * Adds a contract class bypassing the registerer. + * TODO(#10007): Remove this method. + * @param contractClass - The class to register. + */ + addContractClass(contractClass: ContractClassPublic): Promise; } export const AztecNodeApiSchema: ApiSchemaFor = { @@ -514,6 +521,9 @@ export const AztecNodeApiSchema: ApiSchemaFor = { addEpochProofQuote: z.function().args(EpochProofQuote.schema).returns(z.void()), getEpochProofQuotes: z.function().args(schemas.BigInt).returns(z.array(EpochProofQuote.schema)), + + // TODO(#10007): Remove this method + addContractClass: z.function().args(ContractClassPublicSchema).returns(z.void()), }; export function createAztecNodeClient(url: string, fetch = defaultFetch): AztecNode { diff --git a/yarn-project/circuits.js/src/contract/interfaces/contract_data_source.ts b/yarn-project/circuits.js/src/contract/interfaces/contract_data_source.ts index 6f545df85db8..b67afc8ac50e 100644 --- a/yarn-project/circuits.js/src/contract/interfaces/contract_data_source.ts +++ b/yarn-project/circuits.js/src/contract/interfaces/contract_data_source.ts @@ -26,6 +26,12 @@ export interface ContractDataSource { */ getContractClass(id: Fr): Promise; + /** + * Adds a contract class to the database. + * TODO(#10007): Remove this method + */ + addContractClass(contractClass: ContractClassPublic): Promise; + /** * Returns a publicly deployed contract instance given its address. * @param address - Address of the deployed contract. diff --git a/yarn-project/end-to-end/src/e2e_deploy_contract/contract_class_registration.test.ts b/yarn-project/end-to-end/src/e2e_deploy_contract/contract_class_registration.test.ts index 9908dfecbec9..a95a94df77ac 100644 --- a/yarn-project/end-to-end/src/e2e_deploy_contract/contract_class_registration.test.ts +++ b/yarn-project/end-to-end/src/e2e_deploy_contract/contract_class_registration.test.ts @@ -5,8 +5,10 @@ import { type ContractClassWithId, type ContractInstanceWithAddress, type DebugLogger, + type FieldsOf, Fr, type PXE, + type TxReceipt, TxStatus, type Wallet, getContractClassFromArtifact, @@ -18,10 +20,10 @@ import { deployInstance, registerContractClass, } from '@aztec/aztec.js/deployment'; -import { type ContractClassIdPreimage, PublicKeys } from '@aztec/circuits.js'; +import { type ContractClassIdPreimage, PublicKeys, computeContractClassId } from '@aztec/circuits.js'; import { FunctionSelector, FunctionType } from '@aztec/foundation/abi'; import { writeTestData } from '@aztec/foundation/testing'; -import { StatefulTestContract } from '@aztec/noir-contracts.js'; +import { StatefulTestContract, TokenContractArtifact } from '@aztec/noir-contracts.js'; import { TestContract } from '@aztec/noir-contracts.js/Test'; import { DUPLICATE_NULLIFIER_ERROR } from '../fixtures/fixtures.js'; @@ -43,56 +45,74 @@ describe('e2e_deploy_contract contract class registration', () => { let artifact: ContractArtifact; let contractClass: ContractClassWithId & ContractClassIdPreimage; + let registrationTxReceipt: FieldsOf; beforeAll(async () => { artifact = StatefulTestContract.artifact; - await registerContractClass(wallet, artifact).then(c => c.send().wait()); + registrationTxReceipt = await registerContractClass(wallet, artifact).then(c => c.send().wait()); contractClass = getContractClassFromArtifact(artifact); - }); - it('registers the contract class on the node', async () => { - const registeredClass = await aztecNode.getContractClass(contractClass.id); - expect(registeredClass).toBeDefined(); - expect(registeredClass!.artifactHash.toString()).toEqual(contractClass.artifactHash.toString()); - expect(registeredClass!.privateFunctionsRoot.toString()).toEqual(contractClass.privateFunctionsRoot.toString()); - expect(registeredClass!.packedBytecode.toString('hex')).toEqual(contractClass.packedBytecode.toString('hex')); - expect(registeredClass!.publicFunctions).toEqual(contractClass.publicFunctions); - expect(registeredClass!.privateFunctions).toEqual([]); + // TODO(#10007) Remove this call. Node should get the bytecode from the event broadcast. + expect(await aztecNode.getContractClass(contractClass.id)).toBeUndefined(); + await aztecNode.addContractClass({ ...contractClass, privateFunctions: [], unconstrainedFunctions: [] }); }); - it('broadcasts a private function', async () => { - const constructorArtifact = artifact.functions.find(fn => fn.name == 'constructor'); - if (!constructorArtifact) { - // If this gets thrown you've probably modified the StatefulTestContract to no longer include constructor. - // If that's the case you should update this test to use a private function which fits into the bytecode size - // limit. - throw new Error('No constructor found in the StatefulTestContract artifact. Does it still exist?'); - } - const selector = FunctionSelector.fromNameAndParameters(constructorArtifact.name, constructorArtifact.parameters); - - const tx = await (await broadcastPrivateFunction(wallet, artifact, selector)).send().wait(); - const logs = await pxe.getContractClassLogs({ txHash: tx.txHash }); - const logData = logs.logs[0].log.data; - writeTestData('yarn-project/circuits.js/fixtures/PrivateFunctionBroadcastedEventData.hex', logData); - - const fetchedClass = await aztecNode.getContractClass(contractClass.id); - const fetchedFunction = fetchedClass!.privateFunctions[0]!; - expect(fetchedFunction).toBeDefined(); - expect(fetchedFunction.selector).toEqual(selector); - }); + describe('registering a contract class', () => { + // TODO(#10007) Remove this test. We should always broadcast public bytecode. + it('bypasses broadcast if exceeds bytecode limit for event size', async () => { + const logs = await aztecNode.getContractClassLogs({ txHash: registrationTxReceipt.txHash }); + expect(logs.logs.length).toEqual(0); + }); + + // TODO(#10007) Remove this test as well. + it('starts archiver with pre-registered common contracts', async () => { + const classId = computeContractClassId(getContractClassFromArtifact(TokenContractArtifact)); + expect(await aztecNode.getContractClass(classId)).not.toBeUndefined(); + }); - it('broadcasts an unconstrained function', async () => { - const functionArtifact = artifact.functions.find(fn => fn.functionType === FunctionType.UNCONSTRAINED)!; - const selector = FunctionSelector.fromNameAndParameters(functionArtifact); - const tx = await (await broadcastUnconstrainedFunction(wallet, artifact, selector)).send().wait(); - const logs = await pxe.getContractClassLogs({ txHash: tx.txHash }); - const logData = logs.logs[0].log.data; - writeTestData('yarn-project/circuits.js/fixtures/UnconstrainedFunctionBroadcastedEventData.hex', logData); - - const fetchedClass = await aztecNode.getContractClass(contractClass.id); - const fetchedFunction = fetchedClass!.unconstrainedFunctions[0]!; - expect(fetchedFunction).toBeDefined(); - expect(fetchedFunction.selector).toEqual(selector); + it('registers the contract class on the node', async () => { + const registeredClass = await aztecNode.getContractClass(contractClass.id); + expect(registeredClass).toBeDefined(); + expect(registeredClass!.artifactHash.toString()).toEqual(contractClass.artifactHash.toString()); + expect(registeredClass!.privateFunctionsRoot.toString()).toEqual(contractClass.privateFunctionsRoot.toString()); + expect(registeredClass!.packedBytecode.toString('hex')).toEqual(contractClass.packedBytecode.toString('hex')); + expect(registeredClass!.publicFunctions).toEqual(contractClass.publicFunctions); + expect(registeredClass!.privateFunctions).toEqual([]); + }); + + it('broadcasts a private function', async () => { + const constructorArtifact = artifact.functions.find(fn => fn.name == 'constructor'); + if (!constructorArtifact) { + // If this gets thrown you've probably modified the StatefulTestContract to no longer include constructor. + // If that's the case you should update this test to use a private function which fits into the bytecode size limit. + throw new Error('No constructor found in the StatefulTestContract artifact. Does it still exist?'); + } + const selector = FunctionSelector.fromNameAndParameters(constructorArtifact.name, constructorArtifact.parameters); + + const tx = await (await broadcastPrivateFunction(wallet, artifact, selector)).send().wait(); + const logs = await pxe.getContractClassLogs({ txHash: tx.txHash }); + const logData = logs.logs[0].log.data; + writeTestData('yarn-project/circuits.js/fixtures/PrivateFunctionBroadcastedEventData.hex', logData); + + const fetchedClass = await aztecNode.getContractClass(contractClass.id); + const fetchedFunction = fetchedClass!.privateFunctions[0]!; + expect(fetchedFunction).toBeDefined(); + expect(fetchedFunction.selector).toEqual(selector); + }); + + it('broadcasts an unconstrained function', async () => { + const functionArtifact = artifact.functions.find(fn => fn.functionType === FunctionType.UNCONSTRAINED)!; + const selector = FunctionSelector.fromNameAndParameters(functionArtifact); + const tx = await (await broadcastUnconstrainedFunction(wallet, artifact, selector)).send().wait(); + const logs = await pxe.getContractClassLogs({ txHash: tx.txHash }); + const logData = logs.logs[0].log.data; + writeTestData('yarn-project/circuits.js/fixtures/UnconstrainedFunctionBroadcastedEventData.hex', logData); + + const fetchedClass = await aztecNode.getContractClass(contractClass.id); + const fetchedFunction = fetchedClass!.unconstrainedFunctions[0]!; + expect(fetchedFunction).toBeDefined(); + expect(fetchedFunction.selector).toEqual(selector); + }); }); const testDeployingAnInstance = (how: string, deployFn: (toDeploy: ContractInstanceWithAddress) => Promise) => diff --git a/yarn-project/end-to-end/src/e2e_deploy_contract/legacy.test.ts b/yarn-project/end-to-end/src/e2e_deploy_contract/legacy.test.ts index e712e0e9ebb7..a1f6442a81ff 100644 --- a/yarn-project/end-to-end/src/e2e_deploy_contract/legacy.test.ts +++ b/yarn-project/end-to-end/src/e2e_deploy_contract/legacy.test.ts @@ -84,7 +84,8 @@ describe('e2e_deploy_contract legacy', () => { await expect(deployer.deploy().send({ contractAddressSalt }).wait()).rejects.toThrow(/dropped/); }); - it('should not deploy a contract which failed the public part of the execution', async () => { + // TODO(#10007): Reenable this test. + it.skip('should not deploy a contract which failed the public part of the execution', async () => { // This test requires at least another good transaction to go through in the same block as the bad one. const artifact = TokenContractArtifact; const initArgs = ['TokenName', 'TKN', 18] as const; diff --git a/yarn-project/pxe/src/pxe_service/pxe_service.ts b/yarn-project/pxe/src/pxe_service/pxe_service.ts index 62e960817804..bbc78a4400f9 100644 --- a/yarn-project/pxe/src/pxe_service/pxe_service.ts +++ b/yarn-project/pxe/src/pxe_service/pxe_service.ts @@ -242,7 +242,8 @@ export class PXEService implements PXE { if (artifact) { // If the user provides an artifact, validate it against the expected class id and register it - const contractClassId = computeContractClassId(getContractClassFromArtifact(artifact)); + const contractClass = getContractClassFromArtifact(artifact); + const contractClassId = computeContractClassId(contractClass); if (!contractClassId.equals(instance.contractClassId)) { throw new Error( `Artifact does not match expected class id (computed ${contractClassId} but instance refers to ${instance.contractClassId})`, @@ -256,7 +257,12 @@ export class PXEService implements PXE { } await this.db.addContractArtifact(contractClassId, artifact); + + // TODO: PXE may not want to broadcast the artifact to the network await this.node.addContractArtifact(instance.address, artifact); + + // TODO(#10007): Node should get public contract class from the registration event, not from PXE registration + await this.node.addContractClass({ ...contractClass, privateFunctions: [], unconstrainedFunctions: [] }); } else { // Otherwise, make sure there is an artifact already registered for that class id artifact = await this.db.getContractArtifact(instance.contractClassId); diff --git a/yarn-project/txe/src/util/txe_public_contract_data_source.ts b/yarn-project/txe/src/util/txe_public_contract_data_source.ts index 437bed249cfc..1ae75b00a600 100644 --- a/yarn-project/txe/src/util/txe_public_contract_data_source.ts +++ b/yarn-project/txe/src/util/txe_public_contract_data_source.ts @@ -71,4 +71,10 @@ export class TXEPublicContractDataSource implements ContractDataSource { addContractArtifact(address: AztecAddress, contract: ContractArtifact): Promise { return this.txeOracle.addContractArtifact(contract); } + + // TODO(#10007): Remove this method. + addContractClass(_contractClass: ContractClassPublic): Promise { + // We don't really need to do anything for the txe here + return Promise.resolve(); + } } From 8e6396d3213104c0b24c3b834df451e48995267a Mon Sep 17 00:00:00 2001 From: Santiago Palladino Date: Tue, 19 Nov 2024 11:00:26 -0300 Subject: [PATCH 2/2] Format --- .../src/events/class_registered.nr | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/noir-projects/noir-contracts/contracts/contract_class_registerer_contract/src/events/class_registered.nr b/noir-projects/noir-contracts/contracts/contract_class_registerer_contract/src/events/class_registered.nr index 7537442d287c..34949a5b6919 100644 --- a/noir-projects/noir-contracts/contracts/contract_class_registerer_contract/src/events/class_registered.nr +++ b/noir-projects/noir-contracts/contracts/contract_class_registerer_contract/src/events/class_registered.nr @@ -1,8 +1,5 @@ use dep::aztec::protocol_types::{ - constants::{ - REGISTERER_CONTRACT_CLASS_REGISTERED_MAGIC_VALUE, - }, - contract_class_id::ContractClassId, + constants::REGISTERER_CONTRACT_CLASS_REGISTERED_MAGIC_VALUE, contract_class_id::ContractClassId, traits::Serialize, };