From 7b4c6e769e0b52f61930de085d8e95fe5fdadec2 Mon Sep 17 00:00:00 2001 From: Maddiaa <47148561+Maddiaa0@users.noreply.github.com> Date: Fri, 9 Feb 2024 17:11:43 +0000 Subject: [PATCH] feat(avm): introduce small e2e test (#4470) --- .circleci/config.yml | 13 +++ build-system/scripts/remote_run_script | 3 + .../end-to-end/scripts/docker-compose.yml | 1 + .../end-to-end/src/e2e_avm_simulator.test.ts | 35 ++++++ .../src/sequencer/abstract_phase_manager.ts | 12 +- .../src/avm/avm_execution_environment.ts | 7 +- .../simulator/src/avm/avm_simulator.ts | 4 +- .../simulator/src/avm/fixtures/index.ts | 2 + .../simulator/src/avm/journal/host_storage.ts | 2 +- .../src/avm/temporary_executor_migration.ts | 110 ++++++++++++++++++ .../src/client/unconstrained_execution.ts | 2 +- yarn-project/simulator/src/public/executor.ts | 34 ++++++ .../types/src/abi/contract_artifact.ts | 15 ++- 13 files changed, 233 insertions(+), 7 deletions(-) create mode 100644 yarn-project/end-to-end/src/e2e_avm_simulator.test.ts create mode 100644 yarn-project/simulator/src/avm/temporary_executor_migration.ts diff --git a/.circleci/config.yml b/.circleci/config.yml index b036b70684a..b7f5bffcb17 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -830,6 +830,17 @@ jobs: name: "Test" command: cond_spot_run_compose end-to-end 4 ./scripts/docker-compose.yml TEST=e2e_card_game.test.ts + e2e-avm-simulator: + docker: + - image: aztecprotocol/alpine-build-image + resource_class: small + steps: + - *checkout + - *setup_env + - run: + name: "Test" + command: AVM_ENABLED=1 cond_spot_run_compose end-to-end 4 ./scripts/docker-compose.yml TEST=e2e_avm_simulator.test.ts + pxe: docker: - image: aztecprotocol/alpine-build-image @@ -1275,6 +1286,7 @@ workflows: - e2e-persistence: *e2e_test - e2e-browser: *e2e_test - e2e-card-game: *e2e_test + - e2e-avm-simulator: *e2e_test - pxe: *e2e_test - cli-docs-sandbox: *e2e_test - guides-writing-an-account-contract: *e2e_test @@ -1313,6 +1325,7 @@ workflows: - e2e-persistence - e2e-browser - e2e-card-game + - e2e-avm-simulator - pxe - boxes-blank - boxes-blank-react diff --git a/build-system/scripts/remote_run_script b/build-system/scripts/remote_run_script index 8e1d8adf38c..059417cced0 100755 --- a/build-system/scripts/remote_run_script +++ b/build-system/scripts/remote_run_script @@ -31,5 +31,8 @@ ssh -A -F $SSH_CONFIG_PATH $IP " export ECR_DEPLOY_URL=$ECR_DEPLOY_URL export ECR_URL=$ECR_URL export BUILD_SYSTEM_DEBUG=${BUILD_SYSTEM_DEBUG:-} + + # temp while we transitioning to avm + export AVM_ENABLED=${AVM_ENABLED:-} ./remote_runner $@ " diff --git a/yarn-project/end-to-end/scripts/docker-compose.yml b/yarn-project/end-to-end/scripts/docker-compose.yml index 6a05e652777..58feb709bc2 100644 --- a/yarn-project/end-to-end/scripts/docker-compose.yml +++ b/yarn-project/end-to-end/scripts/docker-compose.yml @@ -25,6 +25,7 @@ services: WS_BLOCK_CHECK_INTERVAL_MS: 50 PXE_BLOCK_POLLING_INTERVAL_MS: 50 ARCHIVER_VIEM_POLLING_INTERVAL_MS: 500 + AVM_ENABLED: ${AVM_ENABLED:-} ports: - '8080:8080' diff --git a/yarn-project/end-to-end/src/e2e_avm_simulator.test.ts b/yarn-project/end-to-end/src/e2e_avm_simulator.test.ts new file mode 100644 index 00000000000..299994d2ae7 --- /dev/null +++ b/yarn-project/end-to-end/src/e2e_avm_simulator.test.ts @@ -0,0 +1,35 @@ +import { DebugLogger, Fr, Wallet } from '@aztec/aztec.js'; +import { AvmTestContract } from '@aztec/noir-contracts'; + +import { setup } from './fixtures/utils.js'; + +process.env.AVM_ENABLED = 'absofrigginlutely'; + +describe('e2e_nested_contract', () => { + let wallet: Wallet; + let logger: DebugLogger; + let teardown: () => Promise; + + beforeEach(async () => { + ({ teardown, wallet, logger } = await setup()); + }, 100_000); + + afterEach(() => teardown()); + + describe('Call succeeds through AVM', () => { + let avmContact: AvmTestContract; + + beforeEach(async () => { + avmContact = await AvmTestContract.deploy(wallet).send().deployed(); + }, 50_000); + + it('Calls an avm contract', async () => { + const a = new Fr(1); + const b = new Fr(2); + + logger('Calling avm_addArgsReturn...'); + await avmContact.methods.avm_addArgsReturn(a, b).send().wait(); + logger('Success'); + }); + }); +}); diff --git a/yarn-project/sequencer-client/src/sequencer/abstract_phase_manager.ts b/yarn-project/sequencer-client/src/sequencer/abstract_phase_manager.ts index abedccab59f..548337abd5a 100644 --- a/yarn-project/sequencer-client/src/sequencer/abstract_phase_manager.ts +++ b/yarn-project/sequencer-client/src/sequencer/abstract_phase_manager.ts @@ -47,6 +47,8 @@ import { } from '@aztec/simulator'; import { MerkleTreeOperations } from '@aztec/world-state'; +import { env } from 'process'; + import { getVerificationKeys } from '../mocks/verification_keys.js'; import { PublicProver } from '../prover/index.js'; import { PublicKernelCircuitSimulator } from '../simulator/index.js'; @@ -157,7 +159,15 @@ export abstract class AbstractPhaseManager { while (executionStack.length) { const current = executionStack.pop()!; const isExecutionRequest = !isPublicExecutionResult(current); - const result = isExecutionRequest ? await this.publicExecutor.simulate(current, this.globalVariables) : current; + + // NOTE: temporary glue to incorporate avm execution calls + const simulator = (execution: PublicExecution, globalVariables: GlobalVariables) => + env.AVM_ENABLED + ? this.publicExecutor.simulateAvm(execution, globalVariables) + : this.publicExecutor.simulate(execution, globalVariables); + + const result = isExecutionRequest ? await simulator(current, this.globalVariables) : current; + newUnencryptedFunctionLogs.push(result.unencryptedLogs); const functionSelector = result.execution.functionData.selector.toString(); this.log( diff --git a/yarn-project/simulator/src/avm/avm_execution_environment.ts b/yarn-project/simulator/src/avm/avm_execution_environment.ts index aa3bbb4672c..1317cc71fdd 100644 --- a/yarn-project/simulator/src/avm/avm_execution_environment.ts +++ b/yarn-project/simulator/src/avm/avm_execution_environment.ts @@ -1,4 +1,4 @@ -import { GlobalVariables } from '@aztec/circuits.js'; +import { FunctionSelector, GlobalVariables } from '@aztec/circuits.js'; import { AztecAddress } from '@aztec/foundation/aztec-address'; import { EthAddress } from '@aztec/foundation/eth-address'; import { Fr } from '@aztec/foundation/fields'; @@ -35,6 +35,8 @@ export class AvmExecutionEnvironment { public readonly isDelegateCall: boolean, public readonly calldata: Fr[], + + public readonly temporaryFunctionSelector: FunctionSelector, ) {} public deriveEnvironmentForNestedCall(address: AztecAddress, calldata: Fr[]): AvmExecutionEnvironment { @@ -52,6 +54,7 @@ export class AvmExecutionEnvironment { this.isStaticCall, this.isDelegateCall, /*calldata=*/ calldata, + this.temporaryFunctionSelector, ); } @@ -70,6 +73,7 @@ export class AvmExecutionEnvironment { /*isStaticCall=*/ true, this.isDelegateCall, /*calldata=*/ calldata, + this.temporaryFunctionSelector, ); } @@ -88,6 +92,7 @@ export class AvmExecutionEnvironment { this.isStaticCall, /*isDelegateCall=*/ true, /*calldata=*/ calldata, + this.temporaryFunctionSelector, ); } } diff --git a/yarn-project/simulator/src/avm/avm_simulator.ts b/yarn-project/simulator/src/avm/avm_simulator.ts index a3c35012d00..48b801e78f6 100644 --- a/yarn-project/simulator/src/avm/avm_simulator.ts +++ b/yarn-project/simulator/src/avm/avm_simulator.ts @@ -1,4 +1,3 @@ -import { FunctionSelector } from '@aztec/circuits.js'; import { DebugLogger, createDebugLogger } from '@aztec/foundation/log'; import { strict as assert } from 'assert'; @@ -72,7 +71,8 @@ export class AvmSimulator { */ private async fetchAndDecodeBytecode(): Promise { // NOTE: the following is mocked as getPublicBytecode does not exist yet - const selector = new FunctionSelector(0); + + const selector = this.context.environment.temporaryFunctionSelector; const bytecode = await this.context.worldState.hostStorage.contractsDb.getBytecode( this.context.environment.address, selector, diff --git a/yarn-project/simulator/src/avm/fixtures/index.ts b/yarn-project/simulator/src/avm/fixtures/index.ts index bcaf1ff779f..15dc2206acf 100644 --- a/yarn-project/simulator/src/avm/fixtures/index.ts +++ b/yarn-project/simulator/src/avm/fixtures/index.ts @@ -1,4 +1,5 @@ import { GlobalVariables } from '@aztec/circuits.js'; +import { FunctionSelector } from '@aztec/foundation/abi'; import { AztecAddress } from '@aztec/foundation/aztec-address'; import { EthAddress } from '@aztec/foundation/eth-address'; import { Fr } from '@aztec/foundation/fields'; @@ -52,6 +53,7 @@ export function initExecutionEnvironment(overrides?: Partial new SideEffect(noteHash, Fr.zero())); + + const contractStorageReads: ContractStorageRead[] = []; + const reduceStorageReadRequests = (contractAddress: bigint, storageReads: Map) => { + return storageReads.forEach((innerArray, key) => { + innerArray.forEach(value => { + contractStorageReads.push(new ContractStorageRead(new Fr(key), new Fr(value), 0)); + }); + }); + }; + newWorldState.storageReads.forEach((storageMap: Map, address: bigint) => + reduceStorageReadRequests(address, storageMap), + ); + + const contractStorageUpdateRequests: ContractStorageUpdateRequest[] = []; + const reduceStorageUpdateRequests = (contractAddress: bigint, storageUpdateRequests: Map) => { + return storageUpdateRequests.forEach((innerArray, key) => { + innerArray.forEach(value => { + contractStorageUpdateRequests.push( + new ContractStorageUpdateRequest(new Fr(key), /*TODO: old value not supported */ Fr.zero(), new Fr(value), 0), + ); + }); + }); + }; + newWorldState.storageWrites.forEach((storageMap: Map, address: bigint) => + reduceStorageUpdateRequests(address, storageMap), + ); + + const returnValues = result.output; + + // TODO(follow up in pr tree): NOT SUPPORTED YET, make sure hashing and log resolution is done correctly + // Disabled. + const nestedExecutions: PublicExecutionResult[] = []; + const newNullifiers: SideEffectLinkedToNoteHash[] = []; + const unencryptedLogs = FunctionL2Logs.empty(); + const newL2ToL1Messages = newWorldState.newL1Messages.map(() => Fr.zero()); + + return { + execution, + newCommitments, + newL2ToL1Messages, + newNullifiers, + contractStorageReads, + contractStorageUpdateRequests, + returnValues, + nestedExecutions, + unencryptedLogs, + }; +} diff --git a/yarn-project/simulator/src/client/unconstrained_execution.ts b/yarn-project/simulator/src/client/unconstrained_execution.ts index 62f934d41eb..0ecfb7e5538 100644 --- a/yarn-project/simulator/src/client/unconstrained_execution.ts +++ b/yarn-project/simulator/src/client/unconstrained_execution.ts @@ -7,7 +7,7 @@ import { createDebugLogger } from '@aztec/foundation/log'; import { extractReturnWitness } from '../acvm/deserialize.js'; import { Oracle, acvm, extractCallStack, toACVMWitness } from '../acvm/index.js'; import { ExecutionError } from '../common/errors.js'; -import { AcirSimulator } from '../index.js'; +import { AcirSimulator } from './simulator.js'; import { ViewDataOracle } from './view_data_oracle.js'; // docs:start:execute_unconstrained_function diff --git a/yarn-project/simulator/src/public/executor.ts b/yarn-project/simulator/src/public/executor.ts index e5d3b935403..e678e8c0628 100644 --- a/yarn-project/simulator/src/public/executor.ts +++ b/yarn-project/simulator/src/public/executor.ts @@ -2,6 +2,15 @@ import { GlobalVariables, Header, PublicCircuitPublicInputs } from '@aztec/circu import { createDebugLogger } from '@aztec/foundation/log'; import { Oracle, acvm, extractCallStack, extractReturnWitness } from '../acvm/index.js'; +import { AvmContext } from '../avm/avm_context.js'; +import { AvmMachineState } from '../avm/avm_machine_state.js'; +import { AvmSimulator } from '../avm/avm_simulator.js'; +import { HostStorage } from '../avm/journal/host_storage.js'; +import { AvmWorldStateJournal } from '../avm/journal/index.js'; +import { + temporaryConvertAvmResults, + temporaryCreateAvmExecutionEnvironment, +} from '../avm/temporary_executor_migration.js'; import { ExecutionError, createSimulationError } from '../common/errors.js'; import { SideEffectCounter } from '../common/index.js'; import { PackedArgsCache } from '../common/packed_args_cache.js'; @@ -121,4 +130,29 @@ export class PublicExecutor { throw createSimulationError(err instanceof Error ? err : new Error('Unknown error during public execution')); } } + + /** + * Executes a public execution request in the avm. + * @param execution - The execution to run. + * @param globalVariables - The global variables to use. + * @returns The result of the run plus all nested runs. + */ + public async simulateAvm( + execution: PublicExecution, + globalVariables: GlobalVariables, + ): Promise { + // Temporary code to construct the AVM context + // These data structures will permiate across the simulator when the public executor is phased out + const hostStorage = new HostStorage(this.stateDb, this.contractsDb, this.commitmentsDb); + const worldStateJournal = new AvmWorldStateJournal(hostStorage); + const executionEnv = temporaryCreateAvmExecutionEnvironment(execution, globalVariables); + const machineState = new AvmMachineState(0, 0, 0); + + const context = new AvmContext(worldStateJournal, executionEnv, machineState); + const simulator = new AvmSimulator(context); + + const result = await simulator.execute(); + const newWorldState = context.worldState.flush(); + return temporaryConvertAvmResults(execution, newWorldState, result); + } } diff --git a/yarn-project/types/src/abi/contract_artifact.ts b/yarn-project/types/src/abi/contract_artifact.ts index 221fb7c80c4..8faf80a682b 100644 --- a/yarn-project/types/src/abi/contract_artifact.ts +++ b/yarn-project/types/src/abi/contract_artifact.ts @@ -103,7 +103,7 @@ function generateFunctionArtifact(fn: NoirCompiledContractFunction): FunctionArt // If the function is not unconstrained, the first item is inputs or CallContext which we should omit let parameters = fn.abi.parameters.map(generateFunctionParameter); - if (functionType !== 'unconstrained') { + if (hasKernelFunctionInputs(parameters)) { parameters = parameters.slice(1); } @@ -125,6 +125,19 @@ function generateFunctionArtifact(fn: NoirCompiledContractFunction): FunctionArt }; } +/** + * Returns true if the first parameter is kernel function inputs. + * + * Noir macros #[aztec(private|public)] inject the following code + * fn (inputs: ContextInputs, ...otherparams) {} + * + * Return true if this injected parameter is found + */ +function hasKernelFunctionInputs(params: ABIParameter[]): boolean { + const firstParam = params[0]; + return firstParam?.type.kind === 'struct' && firstParam.type.path.includes('ContextInputs'); +} + /** Validates contract artifact instance, throwing on error. */ function validateContractArtifact(contract: ContractArtifact) { const constructorArtifact = contract.functions.find(({ name }) => name === 'constructor');