diff --git a/yarn-project/acir-simulator/src/public/db.ts b/yarn-project/acir-simulator/src/public/db.ts index 6474480f451..dd3f94a2ae7 100644 --- a/yarn-project/acir-simulator/src/public/db.ts +++ b/yarn-project/acir-simulator/src/public/db.ts @@ -37,6 +37,14 @@ export interface PublicContractsDB { */ getBytecode(address: AztecAddress, functionSelector: Buffer): Promise; + /** + * Returns whether a function is internal or not. + * @param address - The contract address that owns this function. + * @param functionSelector - The selector for the function. + * @returns The `isInternal` flag found, undefined if not found. + */ + getIsInternal(address: AztecAddress, functionSelector: Buffer): Promise; + /** * Returns the portal contract address for an L2 address. * @param address - The L2 contract address. diff --git a/yarn-project/acir-simulator/src/public/executor.ts b/yarn-project/acir-simulator/src/public/executor.ts index d32f9029fea..8dd83896606 100644 --- a/yarn-project/acir-simulator/src/public/executor.ts +++ b/yarn-project/acir-simulator/src/public/executor.ts @@ -182,9 +182,15 @@ export class PublicExecutor { globalVariables: GlobalVariables, ) { const portalAddress = (await this.contractsDb.getPortalContractAddress(targetContractAddress)) ?? EthAddress.ZERO; - // @todo @lherskind - Need to make this data accessible (See issue #1200) - //const abi = await this.contractsDb.getFunctionABI(targetContractAddress, targetFunctionSelector); - const isInternal = false; + const isInternal = await this.contractsDb.getIsInternal(targetContractAddress, targetFunctionSelector); + if (isInternal === undefined) { + throw new Error( + `ERR: ContractsDb don't contain isInternal for ${targetContractAddress.toString()}:${targetFunctionSelector.toString( + 'hex', + )}. Defaulting to false.`, + ); + } + const functionData = new FunctionData(targetFunctionSelector, isInternal, false, false); const callContext = CallContext.from({ diff --git a/yarn-project/acir-simulator/src/public/index.test.ts b/yarn-project/acir-simulator/src/public/index.test.ts index b3a2dffeada..0056a166468 100644 --- a/yarn-project/acir-simulator/src/public/index.test.ts +++ b/yarn-project/acir-simulator/src/public/index.test.ts @@ -50,7 +50,7 @@ describe('ACIR public execution simulator', () => { commitmentsDb.getTreeRoots.mockReturnValue(PrivateHistoricTreeRoots.empty()); executor = new PublicExecutor(publicState, publicContracts, commitmentsDb); - }); + }, 10000); describe('PublicToken contract', () => { let recipient: AztecAddress; @@ -187,58 +187,74 @@ describe('ACIR public execution simulator', () => { }); describe('Parent/Child contracts', () => { - it('calls the public entry point in the parent', async () => { - const parentContractAddress = AztecAddress.random(); - const parentEntryPointFn = ParentContractAbi.functions.find(f => f.name === 'pubEntryPoint')!; - const parentEntryPointFnSelector = keccak(Buffer.from(parentEntryPointFn.name)).subarray(0, 4); - - const childContractAddress = AztecAddress.random(); - const childValueFn = ChildContractAbi.functions.find(f => f.name === 'pubValue')!; - const childValueFnSelector = keccak(Buffer.from(childValueFn.name)).subarray(0, 4); - - const initialValue = 3n; - - const functionData = new FunctionData(parentEntryPointFnSelector, false, false, false); - const args = encodeArguments(parentEntryPointFn, [ - childContractAddress.toField().value, - toBigInt(childValueFnSelector), - initialValue, - ]); + it.each([false, true, undefined])( + 'calls the public entry point in the parent', + async isInternal => { + const parentContractAddress = AztecAddress.random(); + const parentEntryPointFn = ParentContractAbi.functions.find(f => f.name === 'pubEntryPoint')!; + const parentEntryPointFnSelector = keccak(Buffer.from(parentEntryPointFn.name)).subarray(0, 4); + + const childContractAddress = AztecAddress.random(); + const childValueFn = ChildContractAbi.functions.find(f => f.name === 'pubValue')!; + const childValueFnSelector = keccak(Buffer.from(childValueFn.name)).subarray(0, 4); + + const initialValue = 3n; + + const functionData = new FunctionData(parentEntryPointFnSelector, isInternal ?? false, false, false); + const args = encodeArguments(parentEntryPointFn, [ + childContractAddress.toField().value, + toBigInt(childValueFnSelector), + initialValue, + ]); - const callContext = CallContext.from({ - msgSender: AztecAddress.random(), - storageContractAddress: parentContractAddress, - portalContractAddress: EthAddress.random(), - isContractDeployment: false, - isDelegateCall: false, - isStaticCall: false, - }); + const callContext = CallContext.from({ + msgSender: AztecAddress.random(), + storageContractAddress: parentContractAddress, + portalContractAddress: EthAddress.random(), + isContractDeployment: false, + isDelegateCall: false, + isStaticCall: false, + }); + + // eslint-disable-next-line require-await + publicContracts.getBytecode.mockImplementation(async (addr: AztecAddress, selector: Buffer) => { + if (addr.equals(parentContractAddress) && selector.equals(parentEntryPointFnSelector)) { + return Buffer.from(parentEntryPointFn.bytecode, 'base64'); + } else if (addr.equals(childContractAddress) && selector.equals(childValueFnSelector)) { + return Buffer.from(childValueFn.bytecode, 'base64'); + } else { + return undefined; + } + }); + + publicContracts.getIsInternal.mockImplementation(() => { + return Promise.resolve(isInternal); + }); + + const execution: PublicExecution = { contractAddress: parentContractAddress, functionData, args, callContext }; + const globalVariables = new GlobalVariables(new Fr(69), new Fr(420), new Fr(1), new Fr(7)); - // eslint-disable-next-line require-await - publicContracts.getBytecode.mockImplementation(async (addr: AztecAddress, selector: Buffer) => { - if (addr.equals(parentContractAddress) && selector.equals(parentEntryPointFnSelector)) { - return Buffer.from(parentEntryPointFn.bytecode, 'base64'); - } else if (addr.equals(childContractAddress) && selector.equals(childValueFnSelector)) { - return Buffer.from(childValueFn.bytecode, 'base64'); + if (isInternal === undefined) { + // The error is don't seem to be propagated, but can see it in the logger. + await expect(executor.execute(execution, globalVariables)).rejects.toBe( + 'Error awaiting `foreign_call_handler`: Unknown', + ); } else { - return undefined; + const result = await executor.execute(execution, globalVariables); + + expect(result.returnValues).toEqual([ + new Fr( + initialValue + + globalVariables.chainId.value + + globalVariables.version.value + + globalVariables.blockNumber.value + + globalVariables.timestamp.value, + ), + ]); } - }); - - const execution: PublicExecution = { contractAddress: parentContractAddress, functionData, args, callContext }; - const globalVariables = new GlobalVariables(new Fr(69), new Fr(420), new Fr(1), new Fr(7)); - const result = await executor.execute(execution, globalVariables); - - expect(result.returnValues).toEqual([ - new Fr( - initialValue + - globalVariables.chainId.value + - globalVariables.version.value + - globalVariables.blockNumber.value + - globalVariables.timestamp.value, - ), - ]); - }, 20000); + }, + 20_000, + ); }); describe('Public -> Private / Cross Chain messaging', () => { diff --git a/yarn-project/sequencer-client/src/simulator/public_executor.ts b/yarn-project/sequencer-client/src/simulator/public_executor.ts index b6f7ae9ae0c..0a96592344e 100644 --- a/yarn-project/sequencer-client/src/simulator/public_executor.ts +++ b/yarn-project/sequencer-client/src/simulator/public_executor.ts @@ -37,6 +37,9 @@ class ContractsDataSourcePublicDB implements PublicContractsDB { async getBytecode(address: AztecAddress, functionSelector: Buffer): Promise { return (await this.db.getPublicFunction(address, functionSelector))?.bytecode; } + async getIsInternal(address: AztecAddress, functionSelector: Buffer): Promise { + return (await this.db.getPublicFunction(address, functionSelector))?.isInternal; + } async getPortalContractAddress(address: AztecAddress): Promise { return (await this.db.getL2ContractInfo(address))?.portalContractAddress; } diff --git a/yarn-project/types/src/contract_dao.ts b/yarn-project/types/src/contract_dao.ts index 8a2c429f251..9b9ebab9c74 100644 --- a/yarn-project/types/src/contract_dao.ts +++ b/yarn-project/types/src/contract_dao.ts @@ -59,6 +59,7 @@ export function getNewContractPublicFunctions(newContract: ContractDao) { fn => new EncodedContractFunction( generateFunctionSelector(fn.name, fn.parameters), + fn.isInternal ?? false, Buffer.from(fn.bytecode, 'base64'), ), ); diff --git a/yarn-project/types/src/contract_data.ts b/yarn-project/types/src/contract_data.ts index 22999400097..d65b7fe6101 100644 --- a/yarn-project/types/src/contract_data.ts +++ b/yarn-project/types/src/contract_data.ts @@ -60,6 +60,10 @@ export class EncodedContractFunction { * The function selector. */ public functionSelector: Buffer, + /** + * Whether the function is internal. + */ + public isInternal: boolean, /** * The function bytecode. */ @@ -72,7 +76,7 @@ export class EncodedContractFunction { */ toBuffer(): Buffer { const bytecodeBuf = Buffer.concat([numToInt32BE(this.bytecode.length), this.bytecode]); - return serializeToBuffer(this.functionSelector, bytecodeBuf); + return serializeToBuffer(this.functionSelector, this.isInternal, bytecodeBuf); } /** @@ -83,7 +87,8 @@ export class EncodedContractFunction { static fromBuffer(buffer: Buffer | BufferReader): EncodedContractFunction { const reader = BufferReader.asReader(buffer); const fnSelector = reader.readBytes(FUNCTION_SELECTOR_NUM_BYTES); - return new EncodedContractFunction(fnSelector, reader.readBuffer()); + const isInternal = reader.readBoolean(); + return new EncodedContractFunction(fnSelector, isInternal, reader.readBuffer()); } /** @@ -91,7 +96,7 @@ export class EncodedContractFunction { * @returns A random contract function. */ static random(): EncodedContractFunction { - return new EncodedContractFunction(randomBytes(4), randomBytes(64)); + return new EncodedContractFunction(randomBytes(4), false, randomBytes(64)); } }