diff --git a/docs/docs/developers/guides/smart_contracts/testing.md b/docs/docs/developers/guides/smart_contracts/testing.md index 59b5e4e9cfe8..a246bcff4e79 100644 --- a/docs/docs/developers/guides/smart_contracts/testing.md +++ b/docs/docs/developers/guides/smart_contracts/testing.md @@ -138,7 +138,7 @@ Unconstrained functions can be directly called from the contract interface. Noti The test environment provides two different ways of creating accounts, depending on the testing needs. For most cases, it is only necessary to obtain a valid `AztecAddress` that represents the user's account contract. For this, is is enough to do: ```rust -let mocked_account_address = env.create_account(); +let mocked_account_address = env.create_account(secret); ``` These accounts also create the necessary keys to ensure notes can be created/nullified, etc. diff --git a/noir-projects/aztec-nr/aztec/src/keys/getters/test.nr b/noir-projects/aztec-nr/aztec/src/keys/getters/test.nr index d38f159ae113..31108e638258 100644 --- a/noir-projects/aztec-nr/aztec/src/keys/getters/test.nr +++ b/noir-projects/aztec-nr/aztec/src/keys/getters/test.nr @@ -7,7 +7,7 @@ global KEY_ORACLE_RESPONSE_LENGTH: u32 = 13; // 12 fields for the keys, one fiel unconstrained fn setup() -> TestAccount { let _ = TestEnvironment::new(); - let account = cheatcodes::create_account(); + let account = cheatcodes::create_account(1); account } diff --git a/noir-projects/aztec-nr/aztec/src/test/helpers/cheatcodes.nr b/noir-projects/aztec-nr/aztec/src/test/helpers/cheatcodes.nr index 89328f4d493d..d273db76f1e6 100644 --- a/noir-projects/aztec-nr/aztec/src/test/helpers/cheatcodes.nr +++ b/noir-projects/aztec-nr/aztec/src/test/helpers/cheatcodes.nr @@ -47,8 +47,8 @@ pub unconstrained fn direct_storage_write( let _hash = direct_storage_write_oracle(contract_address, storage_slot, fields); } -pub unconstrained fn create_account() -> TestAccount { - oracle_create_account() +pub unconstrained fn create_account(secret: Field) -> TestAccount { + oracle_create_account(secret) } pub unconstrained fn add_account(secret: Field) -> TestAccount { @@ -121,7 +121,7 @@ unconstrained fn direct_storage_write_oracle( ) -> [Field; N] {} #[oracle(createAccount)] -unconstrained fn oracle_create_account() -> TestAccount {} +unconstrained fn oracle_create_account(secret: Field) -> TestAccount {} #[oracle(addAccount)] unconstrained fn oracle_add_account(secret: Field) -> TestAccount {} diff --git a/noir-projects/aztec-nr/aztec/src/test/helpers/test_environment.nr b/noir-projects/aztec-nr/aztec/src/test/helpers/test_environment.nr index d5e494cc5a7e..eba711b9f5b6 100644 --- a/noir-projects/aztec-nr/aztec/src/test/helpers/test_environment.nr +++ b/noir-projects/aztec-nr/aztec/src/test/helpers/test_environment.nr @@ -71,8 +71,8 @@ impl TestEnvironment { PrivateContext::new(inputs, 0) } - pub unconstrained fn create_account(_self: Self) -> AztecAddress { - let test_account = cheatcodes::create_account(); + pub unconstrained fn create_account(_self: Self, secret: Field) -> AztecAddress { + let test_account = cheatcodes::create_account(secret); test_account.address } diff --git a/noir-projects/noir-contracts/contracts/auth_contract/src/test/utils.nr b/noir-projects/noir-contracts/contracts/auth_contract/src/test/utils.nr index 3ba53d12941f..aecf74167548 100644 --- a/noir-projects/noir-contracts/contracts/auth_contract/src/test/utils.nr +++ b/noir-projects/noir-contracts/contracts/auth_contract/src/test/utils.nr @@ -5,9 +5,9 @@ use crate::Auth; pub unconstrained fn setup() -> (&mut TestEnvironment, AztecAddress, AztecAddress, AztecAddress, AztecAddress) { let mut env = TestEnvironment::new(); - let admin = env.create_account(); - let to_authorize = env.create_account(); - let other = env.create_account(); + let admin = env.create_account(1); + let to_authorize = env.create_account(2); + let other = env.create_account(3); let initializer_call_interface = Auth::interface().constructor(admin); diff --git a/noir-projects/noir-contracts/contracts/counter_contract/src/main.nr b/noir-projects/noir-contracts/contracts/counter_contract/src/main.nr index acc1d24a8804..efa1471a26be 100644 --- a/noir-projects/noir-contracts/contracts/counter_contract/src/main.nr +++ b/noir-projects/noir-contracts/contracts/counter_contract/src/main.nr @@ -103,8 +103,8 @@ pub contract Counter { unconstrained fn test_increment() { // Setup env, generate keys let mut env = TestEnvironment::new(); - let owner = env.create_account(); - let sender = env.create_account(); + let owner = env.create_account(1); + let sender = env.create_account(2); let initial_value: Field = 5; env.impersonate(owner); // Deploy contract and initialize diff --git a/noir-projects/noir-contracts/contracts/counter_contract/src/test.nr b/noir-projects/noir-contracts/contracts/counter_contract/src/test.nr index 8a3fb107f8ac..c49837a9240a 100644 --- a/noir-projects/noir-contracts/contracts/counter_contract/src/test.nr +++ b/noir-projects/noir-contracts/contracts/counter_contract/src/test.nr @@ -6,8 +6,8 @@ pub unconstrained fn setup( ) -> (&mut TestEnvironment, AztecAddress, AztecAddress, AztecAddress) { // Setup env, generate keys let mut env = TestEnvironment::new(); - let owner = env.create_account(); - let sender = env.create_account(); + let owner = env.create_account(1); + let sender = env.create_account(2); env.impersonate(owner); // Deploy contract and initialize let initializer = Counter::interface().initialize(initial_value as u64, owner); diff --git a/noir-projects/noir-contracts/contracts/easy_private_voting_contract/src/test/first.nr b/noir-projects/noir-contracts/contracts/easy_private_voting_contract/src/test/first.nr index fa1c63cb920c..60349fbac0d2 100644 --- a/noir-projects/noir-contracts/contracts/easy_private_voting_contract/src/test/first.nr +++ b/noir-projects/noir-contracts/contracts/easy_private_voting_contract/src/test/first.nr @@ -44,7 +44,7 @@ unconstrained fn test_end_vote() { #[test(should_fail)] unconstrained fn test_fail_end_vote_by_non_admin() { let (env, voting_contract_address, _) = utils::setup(); - let alice = env.create_account(); + let alice = env.create_account(2); env.impersonate(alice); EasyPrivateVoting::at(voting_contract_address).end_vote().call(&mut env.public()); @@ -53,7 +53,7 @@ unconstrained fn test_fail_end_vote_by_non_admin() { #[test] unconstrained fn test_cast_vote() { let (env, voting_contract_address, _) = utils::setup(); - let alice = env.create_account(); + let alice = env.create_account(2); env.impersonate(alice); let candidate = 1; @@ -72,8 +72,8 @@ unconstrained fn test_cast_vote() { #[test] unconstrained fn test_cast_vote_with_separate_accounts() { let (env, voting_contract_address, _) = utils::setup(); - let alice = env.create_account(); - let bob = env.create_account(); + let alice = env.create_account(2); + let bob = env.create_account(3); let candidate = 101; @@ -97,7 +97,7 @@ unconstrained fn test_cast_vote_with_separate_accounts() { #[test(should_fail)] unconstrained fn test_fail_vote_twice() { let (env, voting_contract_address, _) = utils::setup(); - let alice = env.create_account(); + let alice = env.create_account(2); let candidate = 101; diff --git a/noir-projects/noir-contracts/contracts/easy_private_voting_contract/src/test/utils.nr b/noir-projects/noir-contracts/contracts/easy_private_voting_contract/src/test/utils.nr index 46f3b1d3fe35..7ab492613936 100644 --- a/noir-projects/noir-contracts/contracts/easy_private_voting_contract/src/test/utils.nr +++ b/noir-projects/noir-contracts/contracts/easy_private_voting_contract/src/test/utils.nr @@ -5,7 +5,7 @@ use crate::EasyPrivateVoting; pub unconstrained fn setup() -> (&mut TestEnvironment, AztecAddress, AztecAddress) { let mut env = TestEnvironment::new(); - let admin = env.create_account(); + let admin = env.create_account(1); let initializer_call_interface = EasyPrivateVoting::interface().constructor(admin); let voting_contract = env.deploy_self("EasyPrivateVoting").with_public_void_initializer( diff --git a/noir-projects/noir-contracts/contracts/nft_contract/src/test/transfer_in_private.nr b/noir-projects/noir-contracts/contracts/nft_contract/src/test/transfer_in_private.nr index b6b07566bc9f..dd2056484d28 100644 --- a/noir-projects/noir-contracts/contracts/nft_contract/src/test/transfer_in_private.nr +++ b/noir-projects/noir-contracts/contracts/nft_contract/src/test/transfer_in_private.nr @@ -39,7 +39,7 @@ unconstrained fn transfer_in_private_to_non_deployed_account() { // Setup without account contracts. We are not using authwits here, so dummy accounts are enough let (env, nft_contract_address, sender, _, token_id) = utils::setup_mint_and_transfer_to_private(/* with_account_contracts */ false); - let not_deployed = cheatcodes::create_account(); + let not_deployed = cheatcodes::create_account(999); // Transfer the NFT to the recipient NFT::at(nft_contract_address) diff --git a/noir-projects/noir-contracts/contracts/nft_contract/src/test/utils.nr b/noir-projects/noir-contracts/contracts/nft_contract/src/test/utils.nr index 4bdd77deebf0..dcc3a0b80ad0 100644 --- a/noir-projects/noir-contracts/contracts/nft_contract/src/test/utils.nr +++ b/noir-projects/noir-contracts/contracts/nft_contract/src/test/utils.nr @@ -21,8 +21,8 @@ pub unconstrained fn setup( let recipient = env.create_account_contract(2); (owner, recipient) } else { - let owner = env.create_account(); - let recipient = env.create_account(); + let owner = env.create_account(1); + let recipient = env.create_account(2); (owner, recipient) }; diff --git a/noir-projects/noir-contracts/contracts/parent_contract/src/main.nr b/noir-projects/noir-contracts/contracts/parent_contract/src/main.nr index 30f4320f3a4b..75bb91968d19 100644 --- a/noir-projects/noir-contracts/contracts/parent_contract/src/main.nr +++ b/noir-projects/noir-contracts/contracts/parent_contract/src/main.nr @@ -247,7 +247,7 @@ pub contract Parent { unconstrained fn test_private_call() { // Setup env, generate keys let mut env = TestEnvironment::new(); - let owner = env.create_account(); + let owner = env.create_account(1); // Deploy parent contract let parent_contract = env.deploy_self("Parent").without_initializer(); let parent_contract_address = parent_contract.to_address(); diff --git a/noir-projects/noir-contracts/contracts/token_contract/src/test/transfer.nr b/noir-projects/noir-contracts/contracts/token_contract/src/test/transfer.nr index d01607d2f37f..1b6e40640baf 100644 --- a/noir-projects/noir-contracts/contracts/token_contract/src/test/transfer.nr +++ b/noir-projects/noir-contracts/contracts/token_contract/src/test/transfer.nr @@ -36,7 +36,7 @@ unconstrained fn transfer_private_to_non_deployed_account() { // Setup without account contracts. We are not using authwits here, so dummy accounts are enough let (env, token_contract_address, owner, _, mint_amount) = utils::setup_and_mint_to_private(/* with_account_contracts */ false); - let not_deployed = cheatcodes::create_account(); + let not_deployed = cheatcodes::create_account(999); // Transfer tokens let transfer_amount = U128::from_integer(1000); Token::at(token_contract_address).transfer(not_deployed.address, transfer_amount).call( diff --git a/noir-projects/noir-contracts/contracts/token_contract/src/test/utils.nr b/noir-projects/noir-contracts/contracts/token_contract/src/test/utils.nr index 17fce84a5c8a..056f7e2c8e6f 100644 --- a/noir-projects/noir-contracts/contracts/token_contract/src/test/utils.nr +++ b/noir-projects/noir-contracts/contracts/token_contract/src/test/utils.nr @@ -22,8 +22,8 @@ pub unconstrained fn setup( let recipient = env.create_account_contract(2); (owner, recipient) } else { - let owner = env.create_account(); - let recipient = env.create_account(); + let owner = env.create_account(1); + let recipient = env.create_account(2); (owner, recipient) }; diff --git a/yarn-project/circuit-types/src/mocks.ts b/yarn-project/circuit-types/src/mocks.ts index 79646dbd34b2..67a739c55722 100644 --- a/yarn-project/circuit-types/src/mocks.ts +++ b/yarn-project/circuit-types/src/mocks.ts @@ -14,7 +14,6 @@ import { PrivateToPublicAccumulatedDataBuilder, SerializableContractInstance, computeContractAddressFromInstance, - computeContractClassId, getContractClassFromArtifact, } from '@aztec/circuits.js'; import { computeVarArgsHash } from '@aztec/circuits.js/hash'; @@ -226,7 +225,7 @@ export const randomContractInstanceWithAddress = async ( export const randomDeployedContract = async () => { const artifact = randomContractArtifact(); - const contractClassId = await computeContractClassId(await getContractClassFromArtifact(artifact)); + const { id: contractClassId } = await getContractClassFromArtifact(artifact); return { artifact, instance: await randomContractInstanceWithAddress({ contractClassId }) }; }; diff --git a/yarn-project/circuits.js/src/contract/contract_instance.ts b/yarn-project/circuits.js/src/contract/contract_instance.ts index a64e7e12fa6f..e4cfc8f6ad5e 100644 --- a/yarn-project/circuits.js/src/contract/contract_instance.ts +++ b/yarn-project/circuits.js/src/contract/contract_instance.ts @@ -10,7 +10,6 @@ import { BufferReader, numToUInt8, serializeToBuffer } from '@aztec/foundation/s import { type FieldsOf } from '@aztec/foundation/types'; import { getContractClassFromArtifact } from '../contract/contract_class.js'; -import { computeContractClassId } from '../contract/contract_class_id.js'; import { PublicKeys } from '../types/public_keys.js'; import { computeContractAddressFromInstance, @@ -114,7 +113,6 @@ export async function getContractInstanceFromDeployParams( const constructorArtifact = getConstructorArtifact(artifact, opts.constructorArtifact); const deployer = opts.deployer ?? AztecAddress.ZERO; const contractClass = await getContractClassFromArtifact(artifact); - const contractClassId = await computeContractClassId(contractClass); const initializationHash = constructorArtifact && opts?.skipArgsDecoding ? await computeInitializationHashFromEncodedArgs( @@ -125,7 +123,7 @@ export async function getContractInstanceFromDeployParams( const publicKeys = opts.publicKeys ?? PublicKeys.default(); const instance: ContractInstance = { - contractClassId, + contractClassId: contractClass.id, initializationHash, publicKeys, salt, 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 3798d571b7e8..07b32b6fd7a4 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 @@ -21,7 +21,7 @@ import { deployInstance, registerContractClass, } from '@aztec/aztec.js/deployment'; -import { type ContractClassIdPreimage, PublicKeys, computeContractClassId } from '@aztec/circuits.js'; +import { type ContractClassIdPreimage, PublicKeys } from '@aztec/circuits.js'; import { FunctionSelector, FunctionType } from '@aztec/foundation/abi'; import { writeTestData } from '@aztec/foundation/testing/files'; import { StatefulTestContract } from '@aztec/noir-contracts.js/StatefulTest'; @@ -76,7 +76,7 @@ describe('e2e_deploy_contract contract class registration', () => { // TODO(#10007) Remove this test as well. it('starts archiver with pre-registered common contracts', async () => { - const classId = await computeContractClassId(await getContractClassFromArtifact(TokenContractArtifact)); + const { id: classId } = await getContractClassFromArtifact(TokenContractArtifact); // The node checks the registration nullifier expect(await aztecNode.getContractClass(classId)).toBeUndefined(); // But the archiver does not diff --git a/yarn-project/pxe/src/pxe_service/pxe_service.ts b/yarn-project/pxe/src/pxe_service/pxe_service.ts index 43942bf99fb8..b201b976a0dc 100644 --- a/yarn-project/pxe/src/pxe_service/pxe_service.ts +++ b/yarn-project/pxe/src/pxe_service/pxe_service.ts @@ -42,11 +42,7 @@ import type { PartialAddress, PrivateKernelTailCircuitPublicInputs, } from '@aztec/circuits.js'; -import { - computeContractAddressFromInstance, - computeContractClassId, - getContractClassFromArtifact, -} from '@aztec/circuits.js/contract'; +import { computeContractAddressFromInstance, getContractClassFromArtifact } from '@aztec/circuits.js/contract'; import { computeNoteHashNonce, siloNullifier } from '@aztec/circuits.js/hash'; import { computeAddressSecret } from '@aztec/circuits.js/keys'; import { @@ -240,7 +236,7 @@ export class PXEService implements PXE { } public async registerContractClass(artifact: ContractArtifact): Promise { - const contractClassId = await computeContractClassId(await getContractClassFromArtifact(artifact)); + const { id: contractClassId } = await getContractClassFromArtifact(artifact); await this.db.addContractArtifact(contractClassId, artifact); this.log.info(`Added contract class ${artifact.name} with id ${contractClassId}`); } @@ -252,10 +248,9 @@ export class PXEService implements PXE { if (artifact) { // If the user provides an artifact, validate it against the expected class id and register it const contractClass = await getContractClassFromArtifact(artifact); - const contractClassId = await computeContractClassId(contractClass); - if (!contractClassId.equals(instance.contractClassId)) { + if (!contractClass.id.equals(instance.contractClassId)) { throw new Error( - `Artifact does not match expected class id (computed ${contractClassId} but instance refers to ${instance.contractClassId})`, + `Artifact does not match expected class id (computed ${contractClass.id} but instance refers to ${instance.contractClassId})`, ); } const computedAddress = await computeContractAddressFromInstance(instance); @@ -263,7 +258,7 @@ export class PXEService implements PXE { throw new Error('Added a contract in which the address does not match the contract instance.'); } - await this.db.addContractArtifact(contractClassId, artifact); + await this.db.addContractArtifact(contractClass.id, artifact); const publicFunctionSignatures = artifact.functions .filter(fn => fn.functionType === FunctionType.PUBLIC) diff --git a/yarn-project/txe/package.json b/yarn-project/txe/package.json index d35ab5aceea7..7dcb62ed106b 100644 --- a/yarn-project/txe/package.json +++ b/yarn-project/txe/package.json @@ -69,7 +69,6 @@ "@aztec/protocol-contracts": "workspace:^", "@aztec/pxe": "workspace:^", "@aztec/simulator": "workspace:^", - "@aztec/telemetry-client": "workspace:^", "@aztec/types": "workspace:^", "@aztec/world-state": "workspace:^", "zod": "^3.23.8" diff --git a/yarn-project/txe/src/index.ts b/yarn-project/txe/src/index.ts index 915eb46e09da..51bd29cb8af5 100644 --- a/yarn-project/txe/src/index.ts +++ b/yarn-project/txe/src/index.ts @@ -1,7 +1,17 @@ -import { loadContractArtifact } from '@aztec/aztec.js'; +import { SchnorrAccountContractArtifact } from '@aztec/accounts/schnorr'; +import { + AztecAddress, + type ContractArtifact, + type ContractInstanceWithAddress, + Fr, + PublicKeys, + deriveKeys, + getContractInstanceFromDeployParams, + loadContractArtifact, +} from '@aztec/aztec.js'; +import { createSafeJsonRpcServer } from '@aztec/foundation/json-rpc/server'; import { type Logger } from '@aztec/foundation/log'; import { type ApiSchemaFor, type ZodFor } from '@aztec/foundation/schemas'; -import { createTracedJsonRpcServer } from '@aztec/telemetry-client'; import { readFile, readdir } from 'fs/promises'; import { join } from 'path'; @@ -14,12 +24,16 @@ import { type ForeignCallArray, type ForeignCallResult, ForeignCallResultSchema, + type ForeignCallSingle, fromArray, + fromSingle, toForeignCallResult, } from './util/encoding.js'; const TXESessions = new Map(); +const TXEArtifactsCache = new Map(); + type MethodNames = { [K in keyof T]: T[K] extends (...args: any[]) => any ? K : never; }[keyof T]; @@ -47,36 +61,94 @@ class TXEDispatcher { constructor(private logger: Logger) {} async #processDeployInputs({ inputs, root_path: rootPath, package_name: packageName }: TXEForeignCallInput) { - const pathStr = fromArray(inputs[0] as ForeignCallArray) - .map(char => String.fromCharCode(char.toNumber())) - .join(''); - const contractName = fromArray(inputs[1] as ForeignCallArray) - .map(char => String.fromCharCode(char.toNumber())) - .join(''); - let artifactPath = ''; - // We're deploying the contract under test - // env.deploy_self("contractName") - if (!pathStr) { - artifactPath = join(rootPath, './target', `${packageName}-${contractName}.json`); + const [pathStr, contractName, initializer] = inputs.slice(0, 3).map(input => + fromArray(input as ForeignCallArray) + .map(char => String.fromCharCode(char.toNumber())) + .join(''), + ); + + const decodedArgs = fromArray(inputs[4] as ForeignCallArray); + const publicKeysHashFr = fromSingle(inputs[5] as ForeignCallSingle); + + const cacheKey = `${pathStr}-${contractName}-${initializer}-${decodedArgs + .map(arg => arg.toString()) + .join('-')}-${publicKeysHashFr}`; + + let artifact; + let instance; + + if (TXEArtifactsCache.has(cacheKey)) { + this.logger.debug(`Using cached artifact for ${cacheKey}`); + ({ artifact, instance } = TXEArtifactsCache.get(cacheKey)!); } else { - // We're deploying a contract that belongs in a workspace - // env.deploy("../path/to/workspace/root@packageName", "contractName") - if (pathStr.includes('@')) { - const [workspace, pkg] = pathStr.split('@'); - const targetPath = join(rootPath, workspace, './target'); - this.logger.debug(`Looking for compiled artifact in workspace ${targetPath}`); - artifactPath = join(targetPath, `${pkg}-${contractName}.json`); + let artifactPath = ''; + // We're deploying the contract under test + // env.deploy_self("contractName") + if (!pathStr) { + artifactPath = join(rootPath, './target', `${packageName}-${contractName}.json`); } else { - // We're deploying a standalone contract - // env.deploy("../path/to/contract/root", "contractName") - const targetPath = join(rootPath, pathStr, './target'); - this.logger.debug(`Looking for compiled artifact in ${targetPath}`); - [artifactPath] = (await readdir(targetPath)).filter(file => file.endsWith(`-${contractName}.json`)); + // We're deploying a contract that belongs in a workspace + // env.deploy("../path/to/workspace/root@packageName", "contractName") + if (pathStr.includes('@')) { + const [workspace, pkg] = pathStr.split('@'); + const targetPath = join(rootPath, workspace, './target'); + this.logger.debug(`Looking for compiled artifact in workspace ${targetPath}`); + artifactPath = join(targetPath, `${pkg}-${contractName}.json`); + } else { + // We're deploying a standalone contract + // env.deploy("../path/to/contract/root", "contractName") + const targetPath = join(rootPath, pathStr, './target'); + this.logger.debug(`Looking for compiled artifact in ${targetPath}`); + [artifactPath] = (await readdir(targetPath)).filter(file => file.endsWith(`-${contractName}.json`)); + } } + this.logger.debug(`Loading compiled artifact ${artifactPath}`); + artifact = loadContractArtifact(JSON.parse(await readFile(artifactPath, 'utf-8'))); + this.logger.debug( + `Deploy ${artifact.name} with initializer ${initializer}(${decodedArgs}) and public keys hash ${publicKeysHashFr}`, + ); + instance = await getContractInstanceFromDeployParams(artifact, { + constructorArgs: decodedArgs, + skipArgsDecoding: true, + salt: Fr.ONE, + // TODO: Modify this to allow for passing public keys. + publicKeys: PublicKeys.default(), + constructorArtifact: initializer ? initializer : undefined, + deployer: AztecAddress.ZERO, + }); + TXEArtifactsCache.set(cacheKey, { artifact, instance }); + } + + inputs.splice(0, 2, artifact, instance); + } + + async #processAddAccountInputs({ inputs }: TXEForeignCallInput) { + const secret = fromSingle(inputs[0] as ForeignCallSingle); + + const cacheKey = `SchnorrAccountContract-${secret}`; + + let artifact; + let instance; + + if (TXEArtifactsCache.has(cacheKey)) { + this.logger.debug(`Using cached artifact for ${cacheKey}`); + ({ artifact, instance } = TXEArtifactsCache.get(cacheKey)!); + } else { + const keys = await deriveKeys(secret); + const args = [keys.publicKeys.masterIncomingViewingPublicKey.x, keys.publicKeys.masterIncomingViewingPublicKey.y]; + artifact = SchnorrAccountContractArtifact; + instance = await getContractInstanceFromDeployParams(artifact, { + constructorArgs: args, + skipArgsDecoding: true, + salt: Fr.ONE, + publicKeys: keys.publicKeys, + constructorArtifact: 'constructor', + deployer: AztecAddress.ZERO, + }); + TXEArtifactsCache.set(cacheKey, { artifact, instance }); } - this.logger.debug(`Loading compiled artifact ${artifactPath}`); - const artifact = loadContractArtifact(JSON.parse(await readFile(artifactPath, 'utf-8'))); - inputs.splice(0, 2, artifact); + + inputs.splice(0, 0, artifact, instance); } // eslint-disable-next-line camelcase @@ -96,16 +168,18 @@ class TXEDispatcher { return toForeignCallResult([]); } case 'deploy': { - // Modify inputs and fall through await this.#processDeployInputs(callData); + break; } - // eslint-disable-next-line no-fallthrough - default: { - const txeService = TXESessions.get(sessionId); - const response = await (txeService as any)[functionName](...inputs); - return response; + case 'addAccount': { + await this.#processAddAccountInputs(callData); + break; } } + + const txeService = TXESessions.get(sessionId); + const response = await (txeService as any)[functionName](...inputs); + return response; } } @@ -120,7 +194,7 @@ const TXEDispatcherApiSchema: ApiSchemaFor = { * @returns A TXE RPC server. */ export function createTXERpcServer(logger: Logger) { - return createTracedJsonRpcServer(new TXEDispatcher(logger), TXEDispatcherApiSchema, { + return createSafeJsonRpcServer(new TXEDispatcher(logger), TXEDispatcherApiSchema, { http200OnError: true, }); } diff --git a/yarn-project/txe/src/oracle/txe_oracle.ts b/yarn-project/txe/src/oracle/txe_oracle.ts index a3b1d6527ac0..2d0a3de8b29e 100644 --- a/yarn-project/txe/src/oracle/txe_oracle.ts +++ b/yarn-project/txe/src/oracle/txe_oracle.ts @@ -43,10 +43,8 @@ import { type PublicDataTreeLeafPreimage, PublicDataWrite, type PublicLog, - computeContractClassId, computeTaggingSecretPoint, deriveKeys, - getContractClassFromArtifact, } from '@aztec/circuits.js'; import { Schnorr } from '@aztec/circuits.js/barretenberg'; import { @@ -250,9 +248,8 @@ export class TXE implements TypedOracle { await this.txeDatabase.addContractInstance(contractInstance); } - async addContractArtifact(artifact: ContractArtifact) { - const contractClass = await getContractClassFromArtifact(artifact); - await this.txeDatabase.addContractArtifact(await computeContractClassId(contractClass), artifact); + async addContractArtifact(contractClassId: Fr, artifact: ContractArtifact) { + await this.txeDatabase.addContractArtifact(contractClassId, artifact); } async getPrivateContextInputs( diff --git a/yarn-project/txe/src/txe_service/txe_service.ts b/yarn-project/txe/src/txe_service/txe_service.ts index 2dfe839d3e7a..5f88c20c7bd6 100644 --- a/yarn-project/txe/src/txe_service/txe_service.ts +++ b/yarn-project/txe/src/txe_service/txe_service.ts @@ -1,13 +1,11 @@ -import { SchnorrAccountContractArtifact } from '@aztec/accounts/schnorr'; import { MerkleTreeId, SimulationError } from '@aztec/circuit-types'; import { + type ContractInstanceWithAddress, DEPLOYER_CONTRACT_ADDRESS, Fr, FunctionSelector, PublicDataWrite, - PublicKeys, computePartialAddress, - getContractInstanceFromDeployParams, } from '@aztec/circuits.js'; import { computePublicDataTreeLeafSlot, siloNullifier } from '@aztec/circuits.js/hash'; import { type ContractArtifact, NoteSelector } from '@aztec/foundation/abi'; @@ -90,32 +88,7 @@ export class TXEService { return toForeignCallResult(keys.publicKeys.toFields().map(toSingle)); } - async deploy( - artifact: ContractArtifact, - initializer: ForeignCallArray, - _length: ForeignCallSingle, - args: ForeignCallArray, - publicKeysHash: ForeignCallSingle, - ) { - const initializerStr = fromArray(initializer) - .map(char => String.fromCharCode(char.toNumber())) - .join(''); - const decodedArgs = fromArray(args); - const publicKeysHashFr = fromSingle(publicKeysHash); - this.logger.debug( - `Deploy ${artifact.name} with initializer ${initializerStr}(${decodedArgs}) and public keys hash ${publicKeysHashFr}`, - ); - - const instance = await getContractInstanceFromDeployParams(artifact, { - constructorArgs: decodedArgs, - skipArgsDecoding: true, - salt: Fr.ONE, - // TODO: Modify this to allow for passing public keys. - publicKeys: PublicKeys.default(), - constructorArtifact: initializerStr ? initializerStr : undefined, - deployer: AztecAddress.ZERO, - }); - + async deploy(artifact: ContractArtifact, instance: ContractInstanceWithAddress) { // Emit deployment nullifier (this.typedOracle as TXE).addSiloedNullifiersFromPublic([ await siloNullifier(AztecAddress.fromNumber(DEPLOYER_CONTRACT_ADDRESS), instance.address.toField()), @@ -123,7 +96,7 @@ export class TXEService { this.logger.debug(`Deployed ${artifact.name} at ${instance.address}`); await (this.typedOracle as TXE).addContractInstance(instance); - await (this.typedOracle as TXE).addContractArtifact(artifact); + await (this.typedOracle as TXE).addContractArtifact(instance.contractClassId, artifact); return toForeignCallResult([ toArray([ instance.salt, @@ -157,9 +130,10 @@ export class TXEService { return toForeignCallResult([toArray(publicDataWrites.map(write => write.value))]); } - async createAccount() { + async createAccount(secret: ForeignCallSingle) { const keyStore = (this.typedOracle as TXE).getKeyStore(); - const completeAddress = await keyStore.createAccount(); + const secretFr = fromSingle(secret); + const completeAddress = await keyStore.addAccount(secretFr, secretFr); const accountStore = (this.typedOracle as TXE).getTXEDatabase(); await accountStore.setAccount(completeAddress.address, completeAddress); this.logger.debug(`Created account ${completeAddress.address}`); @@ -169,22 +143,10 @@ export class TXEService { ]); } - async addAccount(secret: ForeignCallSingle) { - const keys = await (this.typedOracle as TXE).deriveKeys(fromSingle(secret)); - const args = [keys.publicKeys.masterIncomingViewingPublicKey.x, keys.publicKeys.masterIncomingViewingPublicKey.y]; - const artifact = SchnorrAccountContractArtifact; - const instance = await getContractInstanceFromDeployParams(artifact, { - constructorArgs: args, - skipArgsDecoding: true, - salt: Fr.ONE, - publicKeys: keys.publicKeys, - constructorArtifact: 'constructor', - deployer: AztecAddress.ZERO, - }); - + async addAccount(artifact: ContractArtifact, instance: ContractInstanceWithAddress, secret: ForeignCallSingle) { this.logger.debug(`Deployed ${artifact.name} at ${instance.address}`); await (this.typedOracle as TXE).addContractInstance(instance); - await (this.typedOracle as TXE).addContractArtifact(artifact); + await (this.typedOracle as TXE).addContractArtifact(instance.contractClassId, artifact); const keyStore = (this.typedOracle as TXE).getKeyStore(); const completeAddress = await keyStore.addAccount(fromSingle(secret), await computePartialAddress(instance)); diff --git a/yarn-project/txe/src/util/encoding.ts b/yarn-project/txe/src/util/encoding.ts index 1853378af766..69719069d8a2 100644 --- a/yarn-project/txe/src/util/encoding.ts +++ b/yarn-project/txe/src/util/encoding.ts @@ -1,4 +1,4 @@ -import { AztecAddress } from '@aztec/circuits.js'; +import { AztecAddress, type ContractInstanceWithAddress, ContractInstanceWithAddressSchema } from '@aztec/circuits.js'; import { type ContractArtifact, ContractArtifactSchema } from '@aztec/foundation/abi'; import { Fr } from '@aztec/foundation/fields'; import { hexToBuffer } from '@aztec/foundation/string'; @@ -9,7 +9,7 @@ export type ForeignCallSingle = string; export type ForeignCallArray = string[]; -export type ForeignCallArgs = (ForeignCallSingle | ForeignCallArray | ContractArtifact)[]; +export type ForeignCallArgs = (ForeignCallSingle | ForeignCallArray | ContractArtifact | ContractInstanceWithAddress)[]; export type ForeignCallResult = { values: (ForeignCallSingle | ForeignCallArray)[]; @@ -44,7 +44,7 @@ export const ForeignCallSingleSchema = z.string(); export const ForeignCallArraySchema = z.array(z.string()); export const ForeignCallArgsSchema = z.array( - z.union([ForeignCallSingleSchema, ForeignCallArraySchema, ContractArtifactSchema]), + z.union([ForeignCallSingleSchema, ForeignCallArraySchema, ContractArtifactSchema, ContractInstanceWithAddressSchema]), ); export const ForeignCallResultSchema = z.object({ diff --git a/yarn-project/txe/tsconfig.json b/yarn-project/txe/tsconfig.json index 175870ac9d69..d9aeb0e4052c 100644 --- a/yarn-project/txe/tsconfig.json +++ b/yarn-project/txe/tsconfig.json @@ -36,9 +36,6 @@ { "path": "../simulator" }, - { - "path": "../telemetry-client" - }, { "path": "../types" }, diff --git a/yarn-project/yarn.lock b/yarn-project/yarn.lock index 5cc77649f10b..b912118d5915 100644 --- a/yarn-project/yarn.lock +++ b/yarn-project/yarn.lock @@ -1398,7 +1398,6 @@ __metadata: "@aztec/protocol-contracts": "workspace:^" "@aztec/pxe": "workspace:^" "@aztec/simulator": "workspace:^" - "@aztec/telemetry-client": "workspace:^" "@aztec/types": "workspace:^" "@aztec/world-state": "workspace:^" "@jest/globals": "npm:^29.5.0"