diff --git a/yarn-project/bb-prover/src/avm_proving_tests/avm_proving_tester.ts b/yarn-project/bb-prover/src/avm_proving_tests/avm_proving_tester.ts index 99e3257085aa..dca36314e2d9 100644 --- a/yarn-project/bb-prover/src/avm_proving_tests/avm_proving_tester.ts +++ b/yarn-project/bb-prover/src/avm_proving_tests/avm_proving_tester.ts @@ -1,5 +1,8 @@ -import { PublicTxSimulationTester, type TestEnqueuedCall } from '@aztec/simulator/public/fixtures'; -import { SimpleContractDataSource } from '@aztec/simulator/server'; +import { + PublicTxSimulationTester, + SimpleContractDataSource, + type TestEnqueuedCall, +} from '@aztec/simulator/public/fixtures'; import type { AvmCircuitInputs } from '@aztec/stdlib/avm'; import { AztecAddress } from '@aztec/stdlib/aztec-address'; import type { MerkleTreeWriteOperations } from '@aztec/stdlib/interfaces/server'; diff --git a/yarn-project/ivc-integration/src/avm_integration.test.ts b/yarn-project/ivc-integration/src/avm_integration.test.ts index 5b3645ad18c4..46bec4fbe959 100644 --- a/yarn-project/ivc-integration/src/avm_integration.test.ts +++ b/yarn-project/ivc-integration/src/avm_integration.test.ts @@ -1,5 +1,6 @@ import { BB_RESULT, verifyClientIvcProof, writeClientIVCProofToOutputDirectory } from '@aztec/bb-prover'; import { ROLLUP_HONK_VERIFICATION_KEY_LENGTH_IN_FIELDS, TUBE_PROOF_LENGTH } from '@aztec/constants'; +import { Fr } from '@aztec/foundation/fields'; import { createLogger } from '@aztec/foundation/log'; import { AvmTestContractArtifact } from '@aztec/noir-contracts.js/AvmTest'; import { PublicTxSimulationTester } from '@aztec/simulator/public/fixtures'; @@ -20,7 +21,6 @@ import { mapAvmVerificationKeyToNoir, mapRecursiveProofToNoir, mapVerificationKeyToNoir, - simulateAvmBulkTesting, witnessGenMockPublicBaseCircuit, } from './index.js'; import { proveAvm, proveClientIVC, proveRollupHonk, proveTube } from './prove_native.js'; @@ -77,7 +77,23 @@ describe('AVM Integration', () => { // for it to use as "expected" values when testing contract instance retrieval. const expectContractInstance = avmTestContractInstance; - const simRes = await simulateAvmBulkTesting(simTester, expectContractInstance); + const argsField = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10].map(x => new Fr(x)); + const argsU8 = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10].map(x => new Fr(x)); + const args = [ + argsField, + argsU8, + /*getInstanceForAddress=*/ expectContractInstance.address.toField(), + /*expectedDeployer=*/ expectContractInstance.deployer.toField(), + /*expectedClassId=*/ expectContractInstance.currentContractClassId.toField(), + /*expectedInitializationHash=*/ expectContractInstance.initializationHash.toField(), + ]; + + const simRes = await simTester.simulateTx( + /*sender=*/ AztecAddress.fromNumber(42), + /*setupCalls=*/ [], + /*appCalls=*/ [{ address: expectContractInstance.address, fnName: 'bulk_testing', args }], + /*teardownCall=*/ undefined, + ); const avmCircuitInputs = simRes.avmProvingRequest.inputs; const { vk, proof, publicInputs } = await proveAvm(avmCircuitInputs, bbWorkingDirectory, logger); diff --git a/yarn-project/ivc-integration/src/rollup_ivc_integration.test.ts b/yarn-project/ivc-integration/src/rollup_ivc_integration.test.ts index cbadd8481f49..6020be0140ce 100644 --- a/yarn-project/ivc-integration/src/rollup_ivc_integration.test.ts +++ b/yarn-project/ivc-integration/src/rollup_ivc_integration.test.ts @@ -1,9 +1,9 @@ import { BB_RESULT, verifyClientIvcProof, writeClientIVCProofToOutputDirectory } from '@aztec/bb-prover'; import { ROLLUP_HONK_VERIFICATION_KEY_LENGTH_IN_FIELDS, TUBE_PROOF_LENGTH } from '@aztec/constants'; -import type { Fr } from '@aztec/foundation/fields'; +import { Fr } from '@aztec/foundation/fields'; import { createLogger } from '@aztec/foundation/log'; import { AvmTestContractArtifact } from '@aztec/noir-contracts.js/AvmTest'; -import { PublicTxSimulationTester } from '@aztec/simulator/server'; +import { PublicTxSimulationTester } from '@aztec/simulator/public/fixtures'; import type { AvmCircuitPublicInputs } from '@aztec/stdlib/avm'; import { AztecAddress } from '@aztec/stdlib/aztec-address'; import type { ProofAndVerificationKey } from '@aztec/stdlib/interfaces/server'; @@ -23,7 +23,6 @@ import { mapAvmVerificationKeyToNoir, mapRecursiveProofToNoir, mapVerificationKeyToNoir, - simulateAvmBulkTesting, witnessGenMockPublicBaseCircuit, witnessGenMockRollupBasePrivateCircuit, witnessGenMockRollupMergeCircuit, @@ -77,7 +76,24 @@ describe('Rollup IVC Integration', () => { /*deployer=*/ AztecAddress.fromNumber(420), AvmTestContractArtifact, ); - const avmSimulationResult = await simulateAvmBulkTesting(simTester, avmTestContractInstance); + const argsField = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10].map(x => new Fr(x)); + const argsU8 = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10].map(x => new Fr(x)); + const args = [ + argsField, + argsU8, + /*getInstanceForAddress=*/ avmTestContractInstance.address.toField(), + /*expectedDeployer=*/ avmTestContractInstance.deployer.toField(), + /*expectedClassId=*/ avmTestContractInstance.currentContractClassId.toField(), + /*expectedInitializationHash=*/ avmTestContractInstance.initializationHash.toField(), + ]; + + const avmSimulationResult = await simTester.simulateTx( + /*sender=*/ AztecAddress.fromNumber(42), + /*setupCalls=*/ [], + /*appCalls=*/ [{ address: avmTestContractInstance.address, fnName: 'bulk_testing', args }], + /*teardownCall=*/ undefined, + ); + const avmCircuitInputs = avmSimulationResult.avmProvingRequest.inputs; ({ diff --git a/yarn-project/ivc-integration/src/witgen.ts b/yarn-project/ivc-integration/src/witgen.ts index 273f292d7da1..e4c4fbdc5989 100644 --- a/yarn-project/ivc-integration/src/witgen.ts +++ b/yarn-project/ivc-integration/src/witgen.ts @@ -6,10 +6,7 @@ import { } from '@aztec/constants'; import { Fr } from '@aztec/foundation/fields'; import { type ForeignCallOutput, Noir } from '@aztec/noir-noir_js'; -import type { PublicTxSimulationTester } from '@aztec/simulator/server'; import type { AvmCircuitPublicInputs } from '@aztec/stdlib/avm'; -import { AztecAddress } from '@aztec/stdlib/aztec-address'; -import type { ContractInstanceWithAddress } from '@aztec/stdlib/contract'; import type { RecursiveProof } from '@aztec/stdlib/proofs'; import type { VerificationKeyAsFields } from '@aztec/stdlib/vks'; @@ -348,26 +345,3 @@ export function mapAvmPublicInputsToNoir( } return serialized.map(x => x.toString()) as FixedLengthArray; } - -export async function simulateAvmBulkTesting( - simTester: PublicTxSimulationTester, - contractInstance: ContractInstanceWithAddress, -) { - const argsField = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10].map(x => new Fr(x)); - const argsU8 = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10].map(x => new Fr(x)); - const args = [ - argsField, - argsU8, - /*getInstanceForAddress=*/ contractInstance.address.toField(), - /*expectedDeployer=*/ contractInstance.deployer.toField(), - /*expectedClassId=*/ contractInstance.currentContractClassId.toField(), - /*expectedInitializationHash=*/ contractInstance.initializationHash.toField(), - ]; - - return await simTester.simulateTx( - /*sender=*/ AztecAddress.fromNumber(42), - /*setupCalls=*/ [], - /*appCalls=*/ [{ address: contractInstance.address, fnName: 'bulk_testing', args }], - /*teardownCall=*/ undefined, - ); -} diff --git a/yarn-project/prover-client/src/mocks/test_context.ts b/yarn-project/prover-client/src/mocks/test_context.ts index e06337630cb7..2d5b8e33b594 100644 --- a/yarn-project/prover-client/src/mocks/test_context.ts +++ b/yarn-project/prover-client/src/mocks/test_context.ts @@ -6,12 +6,8 @@ import { TestDateProvider } from '@aztec/foundation/timer'; import { getVKTreeRoot } from '@aztec/noir-protocol-circuits-types/vk-tree'; import { protocolContractTreeRoot } from '@aztec/protocol-contracts'; import { computeFeePayerBalanceLeafSlot } from '@aztec/protocol-contracts/fee-juice'; -import { - PublicProcessor, - PublicProcessorFactory, - PublicTxSimulationTester, - SimpleContractDataSource, -} from '@aztec/simulator/server'; +import { PublicTxSimulationTester, SimpleContractDataSource } from '@aztec/simulator/public/fixtures'; +import { PublicProcessor, PublicProcessorFactory } from '@aztec/simulator/server'; import { PublicDataWrite } from '@aztec/stdlib/avm'; import { AztecAddress } from '@aztec/stdlib/aztec-address'; import type { L2Block } from '@aztec/stdlib/block'; diff --git a/yarn-project/simulator/src/public/avm/fixtures/index.ts b/yarn-project/simulator/src/public/avm/fixtures/index.ts index 12003bc2ee94..45df51966dff 100644 --- a/yarn-project/simulator/src/public/avm/fixtures/index.ts +++ b/yarn-project/simulator/src/public/avm/fixtures/index.ts @@ -1,308 +1,2 @@ -import { DEPLOYER_CONTRACT_ADDRESS, MAX_L2_GAS_PER_TX_PUBLIC_PORTION } from '@aztec/constants'; -import { EthAddress } from '@aztec/foundation/eth-address'; -import { Fr } from '@aztec/foundation/fields'; -import { AvmGadgetsTestContract } from '@aztec/noir-contracts.js/AvmGadgetsTest'; -import { AvmTestContract } from '@aztec/noir-contracts.js/AvmTest'; -import { - type ContractArtifact, - type FunctionAbi, - type FunctionArtifact, - FunctionSelector, - getAllFunctionAbis, -} from '@aztec/stdlib/abi'; -import { AztecAddress } from '@aztec/stdlib/aztec-address'; -import { - type ContractClassPublic, - type ContractInstanceWithAddress, - computeInitializationHash, -} from '@aztec/stdlib/contract'; -import { isNoirCallStackUnresolved } from '@aztec/stdlib/errors'; -import { GasFees } from '@aztec/stdlib/gas'; -import { siloNullifier } from '@aztec/stdlib/hash'; -import { deriveKeys } from '@aztec/stdlib/keys'; -import { makeContractClassPublic, makeContractInstanceFromClassId } from '@aztec/stdlib/testing'; -import { GlobalVariables } from '@aztec/stdlib/tx'; - -import { strict as assert } from 'assert'; -import { mock } from 'jest-mock-extended'; -import merge from 'lodash.merge'; - -import { resolveAssertionMessageFromRevertData, traverseCauseChain } from '../../../common/index.js'; -import type { PublicContractsDB, PublicTreesDB } from '../../public_db_sources.js'; -import type { PublicSideEffectTraceInterface } from '../../side_effect_trace_interface.js'; -import { NullifierManager } from '../../state_manager/nullifiers.js'; -import { PublicStorage } from '../../state_manager/public_storage.js'; -import { PublicPersistableStateManager } from '../../state_manager/state_manager.js'; -import { AvmContext } from '../avm_context.js'; -import { AvmExecutionEnvironment } from '../avm_execution_environment.js'; -import { AvmMachineState } from '../avm_machine_state.js'; -import { Field, Uint8, Uint32, Uint64 } from '../avm_memory_types.js'; -import { AvmSimulator } from '../avm_simulator.js'; -import type { AvmRevertReason } from '../errors.js'; - -export const PUBLIC_DISPATCH_FN_NAME = 'public_dispatch'; -export const DEFAULT_BLOCK_NUMBER = 42; - -/** - * Create a new AVM context with default values. - */ -export function initContext(overrides?: { - persistableState?: PublicPersistableStateManager; - env?: AvmExecutionEnvironment; - machineState?: AvmMachineState; -}): AvmContext { - const ctx = new AvmContext( - overrides?.persistableState || initPersistableStateManager(), - overrides?.env || initExecutionEnvironment(), - overrides?.machineState || initMachineState(), - ); - ctx.provideSimulator = AvmSimulator.build; - return ctx; -} - -/** Creates an empty state manager with mocked host storage. */ -export function initPersistableStateManager(overrides?: { - treesDB?: PublicTreesDB; - contractsDB?: PublicContractsDB; - trace?: PublicSideEffectTraceInterface; - publicStorage?: PublicStorage; - nullifiers?: NullifierManager; - doMerkleOperations?: boolean; - firstNullifier?: Fr; - blockNumber?: number; -}): PublicPersistableStateManager { - const treesDB = overrides?.treesDB || mock(); - return new PublicPersistableStateManager( - treesDB, - overrides?.contractsDB || mock(), - overrides?.trace || mock(), - overrides?.firstNullifier || new Fr(27), - overrides?.blockNumber || DEFAULT_BLOCK_NUMBER, - overrides?.doMerkleOperations || false, - overrides?.publicStorage, - overrides?.nullifiers, - ); -} - -/** - * Create an empty instance of the Execution Environment where all values are zero, unless overridden in the overrides object - */ -export function initExecutionEnvironment(overrides?: Partial): AvmExecutionEnvironment { - return new AvmExecutionEnvironment( - overrides?.address ?? AztecAddress.zero(), - overrides?.sender ?? AztecAddress.zero(), - overrides?.contractCallDepth ?? Fr.zero(), - overrides?.transactionFee ?? Fr.zero(), - overrides?.globals ?? GlobalVariables.empty(), - overrides?.isStaticCall ?? false, - overrides?.calldata ?? [], - ); -} - -/** - * Create an empty instance of the Execution Environment where all values are zero, unless overridden in the overrides object - */ -export function initGlobalVariables(overrides?: Partial): GlobalVariables { - return new GlobalVariables( - overrides?.chainId ?? Fr.zero(), - overrides?.version ?? Fr.zero(), - overrides?.blockNumber ?? Fr.zero(), - overrides?.slotNumber ?? Fr.zero(), - overrides?.timestamp ?? Fr.zero(), - overrides?.coinbase ?? EthAddress.ZERO, - overrides?.feeRecipient ?? AztecAddress.zero(), - overrides?.gasFees ?? GasFees.empty(), - ); -} - -/** - * Create an empty instance of the Machine State where all values are set to a large enough amount, unless overridden in the overrides object - */ -export function initMachineState(overrides?: Partial): AvmMachineState { - return AvmMachineState.fromState({ - l2GasLeft: overrides?.l2GasLeft ?? MAX_L2_GAS_PER_TX_PUBLIC_PORTION, - daGasLeft: overrides?.daGasLeft ?? 1e8, - }); -} - -/** - * Create a new object with all the same properties as the original, except for the ones in the overrides object. - */ -export function allSameExcept(original: any, overrides: any): any { - return merge({}, original, overrides); -} - -export function randomMemoryBytes(length: number): Uint8[] { - return [...Array(length)].map(_ => new Uint8(Math.floor(Math.random() * 255))); -} - -export function randomMemoryUint32s(length: number): Uint32[] { - return [...Array(length)].map(_ => new Uint32(Math.floor(Math.random() * 255))); -} - -export function randomMemoryUint64s(length: number): Uint64[] { - return [...Array(length)].map(_ => new Uint64(Math.floor(Math.random() * 255))); -} - -export function randomMemoryFields(length: number): Field[] { - return [...Array(length)].map(_ => new Field(Fr.random())); -} - -export function getFunctionSelector( - functionName: string, - contractArtifact: ContractArtifact, -): Promise { - const fnArtifact = getAllFunctionAbis(contractArtifact).find(f => f.name === functionName)!; - assert(!!fnArtifact, `Function ${functionName} not found in ${contractArtifact.name}`); - const params = fnArtifact.parameters; - return FunctionSelector.fromNameAndParameters(fnArtifact.name, params); -} - -export function getContractFunctionArtifact( - functionName: string, - contractArtifact: ContractArtifact, -): FunctionArtifact | undefined { - return contractArtifact.functions.find(f => f.name === functionName); -} - -export function getContractFunctionAbi( - functionName: string, - contractArtifact: ContractArtifact, -): FunctionAbi | undefined { - return ( - contractArtifact.functions.find(f => f.name === functionName) ?? - contractArtifact.nonDispatchPublicFunctions.find(f => f.name === functionName) - ); -} - -export function resolveContractAssertionMessage( - functionName: string, - revertReason: AvmRevertReason, - output: Fr[], - contractArtifact: ContractArtifact, -): string | undefined { - traverseCauseChain(revertReason, cause => { - revertReason = cause as AvmRevertReason; - }); - - const functionArtifact = getAllFunctionAbis(contractArtifact).find(f => f.name === functionName); - if (!functionArtifact || !revertReason.noirCallStack || !isNoirCallStackUnresolved(revertReason.noirCallStack)) { - return undefined; - } - - return resolveAssertionMessageFromRevertData(output, functionArtifact); -} - -export function getAvmTestContractFunctionSelector(functionName: string): Promise { - return getFunctionSelector(functionName, AvmTestContract.artifactForPublic); -} - -export function getAvmGadgetsTestContractFunctionSelector(functionName: string): Promise { - const artifact = getAllFunctionAbis(AvmGadgetsTestContract.artifactForPublic).find(f => f.name === functionName)!; - assert(!!artifact, `Function ${functionName} not found in AvmGadgetsTestContractArtifact`); - const params = artifact.parameters; - return FunctionSelector.fromNameAndParameters(artifact.name, params); -} - -export function getAvmTestContractArtifact(functionName: string): FunctionArtifact { - const artifact = getContractFunctionArtifact(functionName, AvmTestContract.artifactForPublic) as FunctionArtifact; - assert( - !!artifact?.bytecode, - `No bytecode found for function ${functionName}. Try re-running bootstrap.sh on the repository root.`, - ); - return artifact; -} - -export function getAvmGadgetsTestContractArtifact(functionName: string): FunctionArtifact { - const artifact = AvmGadgetsTestContract.artifactForPublic.functions.find(f => f.name === functionName)!; - assert( - !!artifact?.bytecode, - `No bytecode found for function ${functionName}. Try re-running bootstrap.sh on the repository root.`, - ); - return artifact; -} - -export function getAvmTestContractBytecode(functionName: string): Buffer { - const artifact = getAvmTestContractArtifact(functionName); - return artifact.bytecode; -} - -export function getAvmGadgetsTestContractBytecode(functionName: string): Buffer { - const artifact = getAvmGadgetsTestContractArtifact(functionName); - return artifact.bytecode; -} - -export function resolveAvmTestContractAssertionMessage( - functionName: string, - revertReason: AvmRevertReason, - output: Fr[], -): string | undefined { - return resolveContractAssertionMessage(functionName, revertReason, output, AvmTestContract.artifactForPublic); -} - -export function resolveAvmGadgetsTestContractAssertionMessage( - functionName: string, - revertReason: AvmRevertReason, - output: Fr[], -): string | undefined { - traverseCauseChain(revertReason, cause => { - revertReason = cause as AvmRevertReason; - }); - - const functionArtifact = AvmGadgetsTestContract.artifactForPublic.functions.find(f => f.name === functionName); - if (!functionArtifact || !revertReason.noirCallStack || !isNoirCallStackUnresolved(revertReason.noirCallStack)) { - return undefined; - } - - return resolveAssertionMessageFromRevertData(output, functionArtifact); -} - -/** - * Create a contract class and instance given constructor args, artifact, etc. - * NOTE: This is useful for testing real-ish contract class registration and instance deployment TXs (via logs) - * @param constructorArgs - The constructor arguments for the contract. - * @param deployer - The deployer of the contract. - * @param contractArtifact - The contract artifact for the contract. - * @param seed - The seed for the contract. - * @param originalContractClassId - The original contract class ID (if upgraded) - * @returns The contract class, instance, and contract address nullifier. - */ -export async function createContractClassAndInstance( - constructorArgs: any[], - deployer: AztecAddress, - contractArtifact: ContractArtifact, - seed = 0, - originalContractClassId?: Fr, // if previously upgraded -): Promise<{ - contractClass: ContractClassPublic; - contractInstance: ContractInstanceWithAddress; - contractAddressNullifier: Fr; -}> { - const bytecode = (getContractFunctionArtifact(PUBLIC_DISPATCH_FN_NAME, contractArtifact) as FunctionArtifact)! - .bytecode; - const contractClass = await makeContractClassPublic(seed, bytecode); - - const constructorAbi = getContractFunctionAbi('constructor', contractArtifact); - const { publicKeys } = await deriveKeys(Fr.random()); - const initializationHash = await computeInitializationHash(constructorAbi, constructorArgs); - const contractInstance = - originalContractClassId === undefined - ? await makeContractInstanceFromClassId(contractClass.id, seed, { - deployer, - initializationHash, - publicKeys, - }) - : await makeContractInstanceFromClassId(originalContractClassId, seed, { - deployer, - initializationHash, - currentClassId: contractClass.id, - publicKeys, - }); - - const contractAddressNullifier = await siloNullifier( - AztecAddress.fromNumber(DEPLOYER_CONTRACT_ADDRESS), - contractInstance.address.toField(), - ); - - return { contractClass, contractInstance, contractAddressNullifier }; -} +export * from './initializers.js'; +export * from './utils.js'; diff --git a/yarn-project/simulator/src/public/avm/fixtures/initializers.ts b/yarn-project/simulator/src/public/avm/fixtures/initializers.ts new file mode 100644 index 000000000000..d5e8ea5fb82a --- /dev/null +++ b/yarn-project/simulator/src/public/avm/fixtures/initializers.ts @@ -0,0 +1,101 @@ +import { MAX_L2_GAS_PER_TX_PUBLIC_PORTION } from '@aztec/constants'; +import { EthAddress } from '@aztec/foundation/eth-address'; +import { Fr } from '@aztec/foundation/fields'; +import { AztecAddress } from '@aztec/stdlib/aztec-address'; +import { GasFees } from '@aztec/stdlib/gas'; +import { GlobalVariables } from '@aztec/stdlib/tx'; + +import { mock } from 'jest-mock-extended'; + +import type { PublicContractsDB, PublicTreesDB } from '../../public_db_sources.js'; +import type { PublicSideEffectTraceInterface } from '../../side_effect_trace_interface.js'; +import { NullifierManager } from '../../state_manager/nullifiers.js'; +import { PublicStorage } from '../../state_manager/public_storage.js'; +import { PublicPersistableStateManager } from '../../state_manager/state_manager.js'; +import { AvmContext } from '../avm_context.js'; +import { AvmExecutionEnvironment } from '../avm_execution_environment.js'; +import { AvmMachineState } from '../avm_machine_state.js'; +import { AvmSimulator } from '../avm_simulator.js'; +import { DEFAULT_BLOCK_NUMBER } from './utils.js'; + +/** + * Create a new AVM context with default values. + */ +export function initContext(overrides?: { + persistableState?: PublicPersistableStateManager; + env?: AvmExecutionEnvironment; + machineState?: AvmMachineState; +}): AvmContext { + const ctx = new AvmContext( + overrides?.persistableState || initPersistableStateManager(), + overrides?.env || initExecutionEnvironment(), + overrides?.machineState || initMachineState(), + ); + ctx.provideSimulator = AvmSimulator.build; + return ctx; +} + +/** Creates an empty state manager with mocked host storage. */ +export function initPersistableStateManager(overrides?: { + treesDB?: PublicTreesDB; + contractsDB?: PublicContractsDB; + trace?: PublicSideEffectTraceInterface; + publicStorage?: PublicStorage; + nullifiers?: NullifierManager; + doMerkleOperations?: boolean; + firstNullifier?: Fr; + blockNumber?: number; +}): PublicPersistableStateManager { + const treesDB = overrides?.treesDB || mock(); + return new PublicPersistableStateManager( + treesDB, + overrides?.contractsDB || mock(), + overrides?.trace || mock(), + overrides?.firstNullifier || new Fr(27), + overrides?.blockNumber || DEFAULT_BLOCK_NUMBER, + overrides?.doMerkleOperations || false, + overrides?.publicStorage, + overrides?.nullifiers, + ); +} + +/** + * Create an empty instance of the Execution Environment where all values are zero, unless overridden in the overrides object + */ +export function initExecutionEnvironment(overrides?: Partial): AvmExecutionEnvironment { + return new AvmExecutionEnvironment( + overrides?.address ?? AztecAddress.zero(), + overrides?.sender ?? AztecAddress.zero(), + overrides?.contractCallDepth ?? Fr.zero(), + overrides?.transactionFee ?? Fr.zero(), + overrides?.globals ?? GlobalVariables.empty(), + overrides?.isStaticCall ?? false, + overrides?.calldata ?? [], + ); +} + +/** + * Create an empty instance of the Execution Environment where all values are zero, unless overridden in the overrides object + */ +export function initGlobalVariables(overrides?: Partial): GlobalVariables { + return new GlobalVariables( + overrides?.chainId ?? Fr.zero(), + overrides?.version ?? Fr.zero(), + overrides?.blockNumber ?? Fr.zero(), + overrides?.slotNumber ?? Fr.zero(), + overrides?.timestamp ?? Fr.zero(), + overrides?.coinbase ?? EthAddress.ZERO, + overrides?.feeRecipient ?? AztecAddress.zero(), + overrides?.gasFees ?? GasFees.empty(), + ); +} + +/** + * Create an empty instance of the Machine State where all values are set to a large enough amount, unless overridden in the overrides object + */ +export function initMachineState(overrides?: Partial): AvmMachineState { + return AvmMachineState.fromState({ + l2GasLeft: overrides?.l2GasLeft ?? MAX_L2_GAS_PER_TX_PUBLIC_PORTION, + daGasLeft: overrides?.daGasLeft ?? 1e8, + }); +} diff --git a/yarn-project/simulator/src/public/avm/fixtures/utils.ts b/yarn-project/simulator/src/public/avm/fixtures/utils.ts new file mode 100644 index 000000000000..389ee5b69dd4 --- /dev/null +++ b/yarn-project/simulator/src/public/avm/fixtures/utils.ts @@ -0,0 +1,213 @@ +import { DEPLOYER_CONTRACT_ADDRESS } from '@aztec/constants'; +import { Fr } from '@aztec/foundation/fields'; +import { AvmGadgetsTestContract } from '@aztec/noir-contracts.js/AvmGadgetsTest'; +import { AvmTestContract } from '@aztec/noir-contracts.js/AvmTest'; +import { + type ContractArtifact, + type FunctionAbi, + type FunctionArtifact, + FunctionSelector, + getAllFunctionAbis, +} from '@aztec/stdlib/abi'; +import { AztecAddress } from '@aztec/stdlib/aztec-address'; +import { + type ContractClassPublic, + type ContractInstanceWithAddress, + computeInitializationHash, +} from '@aztec/stdlib/contract'; +import { isNoirCallStackUnresolved } from '@aztec/stdlib/errors'; +import { siloNullifier } from '@aztec/stdlib/hash'; +import { deriveKeys } from '@aztec/stdlib/keys'; +import { makeContractClassPublic, makeContractInstanceFromClassId } from '@aztec/stdlib/testing'; + +import { strict as assert } from 'assert'; +import merge from 'lodash.merge'; + +import { resolveAssertionMessageFromRevertData, traverseCauseChain } from '../../../common/index.js'; +import { Field, Uint8, Uint32, Uint64 } from '../avm_memory_types.js'; +import type { AvmRevertReason } from '../errors.js'; + +export const PUBLIC_DISPATCH_FN_NAME = 'public_dispatch'; +export const DEFAULT_BLOCK_NUMBER = 42; + +/** + * Create a new object with all the same properties as the original, except for the ones in the overrides object. + */ +export function allSameExcept(original: any, overrides: any): any { + return merge({}, original, overrides); +} + +export function randomMemoryBytes(length: number): Uint8[] { + return [...Array(length)].map(_ => new Uint8(Math.floor(Math.random() * 255))); +} + +export function randomMemoryUint32s(length: number): Uint32[] { + return [...Array(length)].map(_ => new Uint32(Math.floor(Math.random() * 255))); +} + +export function randomMemoryUint64s(length: number): Uint64[] { + return [...Array(length)].map(_ => new Uint64(Math.floor(Math.random() * 255))); +} + +export function randomMemoryFields(length: number): Field[] { + return [...Array(length)].map(_ => new Field(Fr.random())); +} + +export function getFunctionSelector( + functionName: string, + contractArtifact: ContractArtifact, +): Promise { + const fnArtifact = getAllFunctionAbis(contractArtifact).find(f => f.name === functionName)!; + assert(!!fnArtifact, `Function ${functionName} not found in ${contractArtifact.name}`); + const params = fnArtifact.parameters; + return FunctionSelector.fromNameAndParameters(fnArtifact.name, params); +} + +export function getContractFunctionArtifact( + functionName: string, + contractArtifact: ContractArtifact, +): FunctionArtifact | undefined { + return contractArtifact.functions.find(f => f.name === functionName); +} + +export function getContractFunctionAbi( + functionName: string, + contractArtifact: ContractArtifact, +): FunctionAbi | undefined { + return ( + contractArtifact.functions.find(f => f.name === functionName) ?? + contractArtifact.nonDispatchPublicFunctions.find(f => f.name === functionName) + ); +} + +export function resolveContractAssertionMessage( + functionName: string, + revertReason: AvmRevertReason, + output: Fr[], + contractArtifact: ContractArtifact, +): string | undefined { + traverseCauseChain(revertReason, cause => { + revertReason = cause as AvmRevertReason; + }); + + const functionArtifact = getAllFunctionAbis(contractArtifact).find(f => f.name === functionName); + if (!functionArtifact || !revertReason.noirCallStack || !isNoirCallStackUnresolved(revertReason.noirCallStack)) { + return undefined; + } + + return resolveAssertionMessageFromRevertData(output, functionArtifact); +} + +export function getAvmTestContractFunctionSelector(functionName: string): Promise { + return getFunctionSelector(functionName, AvmTestContract.artifactForPublic); +} + +export function getAvmGadgetsTestContractFunctionSelector(functionName: string): Promise { + const artifact = getAllFunctionAbis(AvmGadgetsTestContract.artifactForPublic).find(f => f.name === functionName)!; + assert(!!artifact, `Function ${functionName} not found in AvmGadgetsTestContractArtifact`); + const params = artifact.parameters; + return FunctionSelector.fromNameAndParameters(artifact.name, params); +} + +export function getAvmTestContractArtifact(functionName: string): FunctionArtifact { + const artifact = getContractFunctionArtifact(functionName, AvmTestContract.artifactForPublic) as FunctionArtifact; + assert( + !!artifact?.bytecode, + `No bytecode found for function ${functionName}. Try re-running bootstrap.sh on the repository root.`, + ); + return artifact; +} + +export function getAvmGadgetsTestContractArtifact(functionName: string): FunctionArtifact { + const artifact = AvmGadgetsTestContract.artifactForPublic.functions.find(f => f.name === functionName)!; + assert( + !!artifact?.bytecode, + `No bytecode found for function ${functionName}. Try re-running bootstrap.sh on the repository root.`, + ); + return artifact; +} + +export function getAvmTestContractBytecode(functionName: string): Buffer { + const artifact = getAvmTestContractArtifact(functionName); + return artifact.bytecode; +} + +export function getAvmGadgetsTestContractBytecode(functionName: string): Buffer { + const artifact = getAvmGadgetsTestContractArtifact(functionName); + return artifact.bytecode; +} + +export function resolveAvmTestContractAssertionMessage( + functionName: string, + revertReason: AvmRevertReason, + output: Fr[], +): string | undefined { + return resolveContractAssertionMessage(functionName, revertReason, output, AvmTestContract.artifactForPublic); +} + +export function resolveAvmGadgetsTestContractAssertionMessage( + functionName: string, + revertReason: AvmRevertReason, + output: Fr[], +): string | undefined { + traverseCauseChain(revertReason, cause => { + revertReason = cause as AvmRevertReason; + }); + + const functionArtifact = AvmGadgetsTestContract.artifactForPublic.functions.find(f => f.name === functionName); + if (!functionArtifact || !revertReason.noirCallStack || !isNoirCallStackUnresolved(revertReason.noirCallStack)) { + return undefined; + } + + return resolveAssertionMessageFromRevertData(output, functionArtifact); +} + +/** + * Create a contract class and instance given constructor args, artifact, etc. + * NOTE: This is useful for testing real-ish contract class registration and instance deployment TXs (via logs) + * @param constructorArgs - The constructor arguments for the contract. + * @param deployer - The deployer of the contract. + * @param contractArtifact - The contract artifact for the contract. + * @param seed - The seed for the contract. + * @param originalContractClassId - The original contract class ID (if upgraded) + * @returns The contract class, instance, and contract address nullifier. + */ +export async function createContractClassAndInstance( + constructorArgs: any[], + deployer: AztecAddress, + contractArtifact: ContractArtifact, + seed = 0, + originalContractClassId?: Fr, // if previously upgraded +): Promise<{ + contractClass: ContractClassPublic; + contractInstance: ContractInstanceWithAddress; + contractAddressNullifier: Fr; +}> { + const bytecode = (getContractFunctionArtifact(PUBLIC_DISPATCH_FN_NAME, contractArtifact) as FunctionArtifact)! + .bytecode; + const contractClass = await makeContractClassPublic(seed, bytecode); + + const constructorAbi = getContractFunctionAbi('constructor', contractArtifact); + const { publicKeys } = await deriveKeys(Fr.random()); + const initializationHash = await computeInitializationHash(constructorAbi, constructorArgs); + const contractInstance = + originalContractClassId === undefined + ? await makeContractInstanceFromClassId(contractClass.id, seed, { + deployer, + initializationHash, + publicKeys, + }) + : await makeContractInstanceFromClassId(originalContractClassId, seed, { + deployer, + initializationHash, + currentClassId: contractClass.id, + publicKeys, + }); + + const contractAddressNullifier = await siloNullifier( + AztecAddress.fromNumber(DEPLOYER_CONTRACT_ADDRESS), + contractInstance.address.toField(), + ); + + return { contractClass, contractInstance, contractAddressNullifier }; +} diff --git a/yarn-project/simulator/src/public/avm/index.ts b/yarn-project/simulator/src/public/avm/index.ts index ac112e51021b..5f426c8aa7db 100644 --- a/yarn-project/simulator/src/public/avm/index.ts +++ b/yarn-project/simulator/src/public/avm/index.ts @@ -1,2 +1 @@ export * from './avm_simulator.js'; -export * from '../fixtures/simple_contract_data_source.js'; diff --git a/yarn-project/simulator/src/public/index.ts b/yarn-project/simulator/src/public/index.ts index 6fb7f1151d9d..1bd4d959f060 100644 --- a/yarn-project/simulator/src/public/index.ts +++ b/yarn-project/simulator/src/public/index.ts @@ -1,6 +1,4 @@ export { PublicContractsDB } from './public_db_sources.js'; export { type PublicTxResult, PublicTxSimulator, TelemetryPublicTxSimulator } from './public_tx_simulator/index.js'; export { PublicProcessor, PublicProcessorFactory } from './public_processor/public_processor.js'; -export { PublicTxSimulationTester } from './fixtures/index.js'; export { getCallRequestsWithCalldataByPhase } from './utils.js'; -export { SimpleContractDataSource } from './fixtures/simple_contract_data_source.js'; diff --git a/yarn-project/simulator/src/public/public_processor/apps_tests/deployments.test.ts b/yarn-project/simulator/src/public/public_processor/apps_tests/deployments.test.ts index aece9618ca23..9e36e04b4adc 100644 --- a/yarn-project/simulator/src/public/public_processor/apps_tests/deployments.test.ts +++ b/yarn-project/simulator/src/public/public_processor/apps_tests/deployments.test.ts @@ -9,9 +9,9 @@ import { GlobalVariables } from '@aztec/stdlib/tx'; import { getTelemetryClient } from '@aztec/telemetry-client'; import { NativeWorldStateService } from '@aztec/world-state'; -import { PublicContractsDB, PublicTxSimulationTester } from '../../../server.js'; +import { PublicContractsDB } from '../../../server.js'; import { createContractClassAndInstance } from '../../avm/fixtures/index.js'; -import { SimpleContractDataSource } from '../../fixtures/simple_contract_data_source.js'; +import { PublicTxSimulationTester, SimpleContractDataSource } from '../../fixtures/index.js'; import { addNewContractClassToTx, addNewContractInstanceToTx, createTxForPrivateOnly } from '../../fixtures/utils.js'; import { PublicTxSimulator } from '../../public_tx_simulator/public_tx_simulator.js'; import { PublicProcessor } from '../public_processor.js'; diff --git a/yarn-project/simulator/src/public/public_processor/apps_tests/token.test.ts b/yarn-project/simulator/src/public/public_processor/apps_tests/token.test.ts index 605c2a193cff..85f4e589da02 100644 --- a/yarn-project/simulator/src/public/public_processor/apps_tests/token.test.ts +++ b/yarn-project/simulator/src/public/public_processor/apps_tests/token.test.ts @@ -9,8 +9,7 @@ import { GlobalVariables } from '@aztec/stdlib/tx'; import { getTelemetryClient } from '@aztec/telemetry-client'; import { NativeWorldStateService } from '@aztec/world-state'; -import { PublicTxSimulationTester } from '../../../server.js'; -import { SimpleContractDataSource } from '../../fixtures/simple_contract_data_source.js'; +import { PublicTxSimulationTester, SimpleContractDataSource } from '../../fixtures/index.js'; import { PublicContractsDB } from '../../public_db_sources.js'; import { PublicTxSimulator } from '../../public_tx_simulator/public_tx_simulator.js'; import { PublicProcessor } from '../public_processor.js';