diff --git a/yarn-project/aztec.js/src/contract/base_contract_interaction.ts b/yarn-project/aztec.js/src/contract/base_contract_interaction.ts index f643b48fea7c..967e80c31049 100644 --- a/yarn-project/aztec.js/src/contract/base_contract_interaction.ts +++ b/yarn-project/aztec.js/src/contract/base_contract_interaction.ts @@ -61,7 +61,7 @@ export abstract class BaseContractInteraction { public async prove(options: SendMethodOptions = {}): Promise { // docs:end:prove const txProvingResult = await this.proveInternal(options); - return new ProvenTx(this.wallet, txProvingResult.toTx(), txProvingResult.timings); + return new ProvenTx(this.wallet, txProvingResult.toTx(), txProvingResult.stats); } // docs:start:send diff --git a/yarn-project/aztec.js/src/contract/contract_function_interaction.ts b/yarn-project/aztec.js/src/contract/contract_function_interaction.ts index e0e848d893ff..869246de4c36 100644 --- a/yarn-project/aztec.js/src/contract/contract_function_interaction.ts +++ b/yarn-project/aztec.js/src/contract/contract_function_interaction.ts @@ -2,7 +2,7 @@ import { ExecutionPayload } from '@aztec/entrypoints/payload'; import { type FunctionAbi, FunctionSelector, FunctionType, decodeFromAbi, encodeArguments } from '@aztec/stdlib/abi'; import type { AuthWitness } from '@aztec/stdlib/auth-witness'; import { AztecAddress } from '@aztec/stdlib/aztec-address'; -import type { Capsule, HashedValues, SimulationTimings, TxExecutionRequest, TxProfileResult } from '@aztec/stdlib/tx'; +import type { Capsule, HashedValues, SimulationStats, TxExecutionRequest, TxProfileResult } from '@aztec/stdlib/tx'; import type { Wallet } from '../wallet/wallet.js'; import { BaseContractInteraction } from './base_contract_interaction.js'; @@ -16,20 +16,15 @@ import type { /** * Represents the result type of a simulation. * By default, it will just be the return value of the simulated function - * so contract interfaces behave as plain functions. If `includeMetadata` is set to true, + * so contract interfaces behave as plain functions. If `includeStats` is set to true, * it will provide extra information. */ type SimulationReturn = T extends true ? { /** - * Additional metadata about the simulation + * Additional stats about the simulation */ - meta: { - /** - * Timings of the different operations performed, including per function breakdown - */ - timings?: SimulationTimings; - }; + stats: SimulationStats; /** * Return value of the function */ @@ -117,11 +112,9 @@ export class ContractFunctionInteraction extends BaseContractInteraction { * @param options - An optional object containing additional configuration for the transaction. * @returns The result of the transaction as returned by the contract function. */ - public async simulate(options?: T): Promise>; + public async simulate(options?: T): Promise>; // eslint-disable-next-line jsdoc/require-jsdoc - public async simulate( - options: SimulateMethodOptions = {}, - ): Promise> { + public async simulate(options: SimulateMethodOptions = {}): Promise> { // docs:end:simulate if (this.functionDao.functionType == FunctionType.UTILITY) { const utilityResult = await this.wallet.simulateUtility( @@ -132,9 +125,9 @@ export class ContractFunctionInteraction extends BaseContractInteraction { options?.from, ); - if (options.includeMetadata) { + if (options.includeStats) { return { - meta: { timings: utilityResult.timings }, + stats: utilityResult.stats, result: utilityResult.result, }; } else { @@ -168,8 +161,8 @@ export class ContractFunctionInteraction extends BaseContractInteraction { const returnValue = rawReturnValues ? decodeFromAbi(this.functionDao.returnTypes, rawReturnValues) : []; - if (options.includeMetadata) { - return { meta: { timings: simulatedTx.timings }, result: returnValue }; + if (options.includeStats) { + return { stats: simulatedTx.stats, result: returnValue }; } else { return returnValue; } diff --git a/yarn-project/aztec.js/src/contract/deploy_method.ts b/yarn-project/aztec.js/src/contract/deploy_method.ts index cda427bc8a4c..a182686aca91 100644 --- a/yarn-project/aztec.js/src/contract/deploy_method.ts +++ b/yarn-project/aztec.js/src/contract/deploy_method.ts @@ -250,7 +250,7 @@ export class DeployMethod extends Bas txProvingResult.toTx(), this.postDeployCtor, () => this.getInstance(options), - txProvingResult.timings, + txProvingResult.stats, ); } diff --git a/yarn-project/aztec.js/src/contract/deploy_proven_tx.ts b/yarn-project/aztec.js/src/contract/deploy_proven_tx.ts index 59ce97eae176..9073f8f780b9 100644 --- a/yarn-project/aztec.js/src/contract/deploy_proven_tx.ts +++ b/yarn-project/aztec.js/src/contract/deploy_proven_tx.ts @@ -1,6 +1,6 @@ import type { AztecAddress } from '@aztec/stdlib/aztec-address'; import type { ContractInstanceWithAddress } from '@aztec/stdlib/contract'; -import type { ProvingTimings, Tx } from '@aztec/stdlib/tx'; +import type { ProvingStats, Tx } from '@aztec/stdlib/tx'; import type { Wallet } from '../wallet/wallet.js'; import type { Contract } from './contract.js'; @@ -16,9 +16,9 @@ export class DeployProvenTx extends Prove tx: Tx, private postDeployCtor: (address: AztecAddress, wallet: Wallet) => Promise, private instanceGetter: () => Promise, - timings?: ProvingTimings, + stats?: ProvingStats, ) { - super(wallet, tx, timings); + super(wallet, tx, stats); } /** diff --git a/yarn-project/aztec.js/src/contract/interaction_options.ts b/yarn-project/aztec.js/src/contract/interaction_options.ts index feb9645d3a0d..e9be53debc71 100644 --- a/yarn-project/aztec.js/src/contract/interaction_options.ts +++ b/yarn-project/aztec.js/src/contract/interaction_options.ts @@ -42,8 +42,9 @@ export type SimulateMethodOptions = Pick< skipTxValidation?: boolean; /** Whether to ensure the fee payer is not empty and has enough balance to pay for the fee. */ skipFeeEnforcement?: boolean; - /** Whether to include the metadata in the simulation result, instead of just the return of the function */ - includeMetadata?: boolean; + /** Whether to include performance statistics (e.g. timing information of the different circuits and oracles) in + * the simulation result, instead of just the return value of the function */ + includeStats?: boolean; }; /** diff --git a/yarn-project/aztec.js/src/contract/proven_tx.ts b/yarn-project/aztec.js/src/contract/proven_tx.ts index 3983bddbdf5c..ee5e266968fd 100644 --- a/yarn-project/aztec.js/src/contract/proven_tx.ts +++ b/yarn-project/aztec.js/src/contract/proven_tx.ts @@ -1,4 +1,4 @@ -import { type ProvingTimings, Tx } from '@aztec/stdlib/tx'; +import { type ProvingStats, Tx } from '@aztec/stdlib/tx'; import type { Wallet } from '../wallet/wallet.js'; import { SentTx } from './sent_tx.js'; @@ -11,7 +11,7 @@ export class ProvenTx extends Tx { protected wallet: Wallet, tx: Tx, // eslint-disable-next-line jsdoc/require-jsdoc - public timings?: ProvingTimings, + public stats?: ProvingStats, ) { super(tx.data, tx.clientIvcProof, tx.contractClassLogs, tx.publicFunctionCalldata); } diff --git a/yarn-project/cli-wallet/src/cmds/create_account.ts b/yarn-project/cli-wallet/src/cmds/create_account.ts index cada0b8e8552..795d90b58dab 100644 --- a/yarn-project/cli-wallet/src/cmds/create_account.ts +++ b/yarn-project/cli-wallet/src/cmds/create_account.ts @@ -108,7 +108,7 @@ export async function createAccount( } else { const provenTx = await deployMethod.prove({ ...deployOpts, universalDeploy: true, contractAddressSalt: salt }); if (verbose) { - printProfileResult(provenTx.timings!, log); + printProfileResult(provenTx.stats!, log); } tx = provenTx.send(); diff --git a/yarn-project/cli-wallet/src/cmds/deploy.ts b/yarn-project/cli-wallet/src/cmds/deploy.ts index 373ed48e3519..742de482781a 100644 --- a/yarn-project/cli-wallet/src/cmds/deploy.ts +++ b/yarn-project/cli-wallet/src/cmds/deploy.ts @@ -64,7 +64,7 @@ export async function deploy( const provenTx = await deploy.prove(deployOpts); if (verbose) { - printProfileResult(provenTx.timings!, log); + printProfileResult(provenTx.stats!, log); } const tx = provenTx.send(); diff --git a/yarn-project/cli-wallet/src/cmds/deploy_account.ts b/yarn-project/cli-wallet/src/cmds/deploy_account.ts index 23b005e985c3..d9cc066d6734 100644 --- a/yarn-project/cli-wallet/src/cmds/deploy_account.ts +++ b/yarn-project/cli-wallet/src/cmds/deploy_account.ts @@ -85,7 +85,7 @@ export async function deployAccount( } else { const provenTx = await deployMethod.prove({ ...deployOpts, universalDeploy: true, contractAddressSalt: salt }); if (verbose) { - printProfileResult(provenTx.timings!, log); + printProfileResult(provenTx.stats!, log); } tx = provenTx.send(); diff --git a/yarn-project/cli-wallet/src/cmds/profile.ts b/yarn-project/cli-wallet/src/cmds/profile.ts index d31a3b711fdd..1ac9666ebcbf 100644 --- a/yarn-project/cli-wallet/src/cmds/profile.ts +++ b/yarn-project/cli-wallet/src/cmds/profile.ts @@ -31,7 +31,7 @@ export async function profile( authWitnesses, skipProofGeneration: false, }); - printProfileResult(result.timings, log, result.executionSteps); + printProfileResult(result.stats, log, result.executionSteps); if (debugOutputPath) { const ivcInputsPath = path.join(debugOutputPath, 'ivc-inputs.msgpack'); log(`Debug output written to ${ivcInputsPath}.`); diff --git a/yarn-project/cli-wallet/src/cmds/send.ts b/yarn-project/cli-wallet/src/cmds/send.ts index cca4d7912613..347fa3a5de4e 100644 --- a/yarn-project/cli-wallet/src/cmds/send.ts +++ b/yarn-project/cli-wallet/src/cmds/send.ts @@ -50,7 +50,7 @@ export async function send( const provenTx = await call.prove(sendOptions); if (verbose) { - printProfileResult(provenTx.timings!, log); + printProfileResult(provenTx.stats!, log); } const tx = provenTx.send(); diff --git a/yarn-project/cli-wallet/src/cmds/simulate.ts b/yarn-project/cli-wallet/src/cmds/simulate.ts index e1a847033746..e2a0026a6356 100644 --- a/yarn-project/cli-wallet/src/cmds/simulate.ts +++ b/yarn-project/cli-wallet/src/cmds/simulate.ts @@ -25,10 +25,10 @@ export async function simulate( const simulationResult = await call.simulate({ ...(await feeOpts.toSendOpts(wallet)), authWitnesses, - includeMetadata: true, + includeStats: true, }); if (verbose) { - printProfileResult(simulationResult.meta.timings!, log); + printProfileResult(simulationResult.stats!, log); } log(format('\nSimulation result: ', simulationResult.result, '\n')); } diff --git a/yarn-project/cli-wallet/src/utils/profiling.ts b/yarn-project/cli-wallet/src/utils/profiling.ts index 942c63d3a4f0..3b030a846b62 100644 --- a/yarn-project/cli-wallet/src/utils/profiling.ts +++ b/yarn-project/cli-wallet/src/utils/profiling.ts @@ -1,6 +1,6 @@ import type { LogFn } from '@aztec/foundation/log'; import type { PrivateExecutionStep } from '@aztec/stdlib/kernel'; -import type { ProvingTimings, SimulationTimings } from '@aztec/stdlib/tx'; +import type { ProvingStats, ProvingTimings, SimulationStats, SimulationTimings } from '@aztec/stdlib/tx'; import { format } from 'util'; @@ -11,7 +11,7 @@ const COLUMN_MAX_WIDTH = 15; const ORACLE_NAME_PADDING = 50; export function printProfileResult( - timings: ProvingTimings | SimulationTimings, + stats: ProvingStats | SimulationStats, log: LogFn, executionSteps?: PrivateExecutionStep[], ) { @@ -39,6 +39,7 @@ export function printProfileResult( let acc = 0; let biggest: PrivateExecutionStep | undefined = executionSteps?.[0]; + const timings = stats.timings; timings.perFunction.forEach((fn, i) => { const currentExecutionStep = executionSteps?.[i]; if (currentExecutionStep && biggest && currentExecutionStep.gateCount! > biggest.gateCount!) { @@ -91,6 +92,27 @@ export function printProfileResult( ); } + if (stats.nodeRPCCalls) { + log(format('\nRPC calls:\n')); + for (const [method, { times }] of Object.entries(stats.nodeRPCCalls)) { + const calls = times.length; + const total = times.reduce((acc, time) => acc + time, 0); + const avg = total / calls; + const min = Math.min(...times); + const max = Math.max(...times); + log( + format( + method.padEnd(ORACLE_NAME_PADDING), + `${calls} calls`.padStart(COLUMN_MIN_WIDTH).padEnd(COLUMN_MAX_WIDTH), + `${total.toFixed(2)}ms`.padStart(COLUMN_MIN_WIDTH).padEnd(COLUMN_MAX_WIDTH), + `min: ${min.toFixed(2)}ms`.padStart(COLUMN_MIN_WIDTH).padEnd(COLUMN_MAX_WIDTH), + `avg: ${avg.toFixed(2)}ms`.padStart(COLUMN_MIN_WIDTH).padEnd(COLUMN_MAX_WIDTH), + `max: ${max.toFixed(2)}ms`.padStart(COLUMN_MIN_WIDTH).padEnd(COLUMN_MAX_WIDTH), + ), + ); + } + } + log(format('\nSync time:'.padEnd(25), `${timings.sync?.toFixed(2)}ms`.padStart(16))); log( format( diff --git a/yarn-project/end-to-end/src/bench/client_flows/benchmark.ts b/yarn-project/end-to-end/src/bench/client_flows/benchmark.ts index a0d38e340aae..6a560e8305d1 100644 --- a/yarn-project/end-to-end/src/bench/client_flows/benchmark.ts +++ b/yarn-project/end-to-end/src/bench/client_flows/benchmark.ts @@ -7,7 +7,7 @@ import type { } from '@aztec/aztec.js'; import { createLogger } from '@aztec/foundation/log'; import { type PrivateExecutionStep, serializePrivateExecutionSteps } from '@aztec/stdlib/kernel'; -import type { ProvingTimings, SimulationTimings } from '@aztec/stdlib/tx'; +import type { ProvingStats, ProvingTimings, SimulationStats, SimulationTimings } from '@aztec/stdlib/tx'; import assert from 'node:assert'; import { mkdir, writeFile } from 'node:fs/promises'; @@ -94,24 +94,30 @@ export class ProxyLogger { export type ProverType = 'wasm' | 'native'; -type OracleRecording = { +type CallRecording = { + // Number of times the function has been called calls: number; + // Maximum time taken by the function (in ms) max: number; + // Minimum time taken by the function (in ms) min: number; + // Average time taken by the function (in ms) avg: number; + // Total time spent in the function, computed as sum of all calls (in ms) total: number; }; type Step = Pick & { time: number; accGateCount?: number; - oracles: Record; + oracles: Record; }; type ClientFlowBenchmark = { name: string; timings: Omit & { witgen: number }; maxMemory: number; + rpc: Record; proverType: ProverType; minimumTrace: StructuredTrace; totalGateCount: number; @@ -158,7 +164,7 @@ function getMaxMemory(logs: Log[]): number { export function generateBenchmark( flow: string, logs: Log[], - timings: ProvingTimings | SimulationTimings, + stats: ProvingStats | SimulationStats, privateExecutionSteps: PrivateExecutionStep[], proverType: ProverType, error: string | undefined, @@ -194,11 +200,12 @@ export function generateBenchmark( }; return acc; }, - {} as Record, + {} as Record, ), }, ]; }, []); + const timings = stats.timings; const totalGateCount = steps[steps.length - 1].accGateCount; return { name: flow, @@ -209,6 +216,21 @@ export function generateBenchmark( unaccounted: timings.unaccounted, witgen: timings.perFunction.reduce((acc, fn) => acc + fn.time, 0), }, + rpc: Object.entries(stats.nodeRPCCalls ?? {}).reduce( + (acc, [RPCName, RPCCalls]) => { + const total = RPCCalls.times.reduce((sum, time) => sum + time, 0); + const calls = RPCCalls.times.length; + acc[RPCName] = { + calls, + max: Math.max(...RPCCalls.times), + min: Math.min(...RPCCalls.times), + total, + avg: total / calls, + }; + return acc; + }, + {} as Record, + ), maxMemory, proverType, minimumTrace: minimumTrace!, @@ -219,6 +241,7 @@ export function generateBenchmark( } export function convertProfileToGHBenchmark(benchmark: ClientFlowBenchmark): GithubActionBenchmarkResult[] { + const totalRPCCalls = Object.values(benchmark.rpc).reduce((acc, call) => acc + call.calls, 0); const benches = [ { name: `${benchmark.name}/witgen`, @@ -247,6 +270,11 @@ export function convertProfileToGHBenchmark(benchmark: ClientFlowBenchmark): Git value: benchmark.totalGateCount, unit: 'gates', }, + { + name: `${benchmark.name}/rpc`, + value: totalRPCCalls, + unit: 'calls', + }, ]; if (benchmark.timings.proving) { benches.push({ @@ -278,7 +306,7 @@ export async function captureProfile( if (expectedSteps !== undefined && result.executionSteps.length !== expectedSteps) { throw new Error(`Expected ${expectedSteps} execution steps, got ${result.executionSteps.length}`); } - const benchmark = generateBenchmark(label, logs, result.timings, result.executionSteps, 'wasm', undefined); + const benchmark = generateBenchmark(label, logs, result.stats, result.executionSteps, 'wasm', undefined); const ivcFolder = process.env.CAPTURE_IVC_FOLDER; if (ivcFolder) { diff --git a/yarn-project/end-to-end/src/bench/client_flows/data_extractor.ts b/yarn-project/end-to-end/src/bench/client_flows/data_extractor.ts index 4b7b58287317..2ac71528c40b 100644 --- a/yarn-project/end-to-end/src/bench/client_flows/data_extractor.ts +++ b/yarn-project/end-to-end/src/bench/client_flows/data_extractor.ts @@ -5,7 +5,7 @@ import { createLogger, logger } from '@aztec/foundation/log'; import { Timer } from '@aztec/foundation/timer'; import { WASMSimulator } from '@aztec/simulator/client'; import type { PrivateExecutionStep } from '@aztec/stdlib/kernel'; -import type { ProvingTimings, SimulationTimings } from '@aztec/stdlib/tx'; +import type { ProvingStats, ProvingTimings, SimulationStats } from '@aztec/stdlib/tx'; import { Decoder } from 'msgpackr'; import { readFile, readdir, writeFile } from 'node:fs/promises'; @@ -55,7 +55,7 @@ async function main() { }); const profileFile = await readFile(join(ivcFolder, flow, 'profile.json')); const profile = JSON.parse(profileFile.toString()) as { - timings: ProvingTimings | SimulationTimings; + stats: ProvingStats | SimulationStats; steps: { functionName: string; gateCount: number; @@ -71,7 +71,7 @@ async function main() { vk: stepsFromFile[i].vk, timings: { witgen: step.timings.witgen, - gateCount: step.timings.witgen, + gateCount: step.timings.gateCount, }, })); @@ -89,10 +89,10 @@ async function main() { // Extract logs from this run from the proxy and write them to disk unconditionally currentLogs = proxyLogger.getLogs(); await writeFile(join(ivcFolder, flow, 'logs.json'), JSON.stringify(currentLogs, null, 2)); - if (!(profile.timings as ProvingTimings).proving) { - (profile.timings as ProvingTimings).proving = provingTime; + if (!(profile.stats.timings as ProvingTimings).proving) { + (profile.stats.timings as ProvingTimings).proving = provingTime; } - const benchmark = generateBenchmark(flow, currentLogs, profile.timings, privateExecutionSteps, proverType, error); + const benchmark = generateBenchmark(flow, currentLogs, profile.stats, privateExecutionSteps, proverType, error); await writeFile(join(ivcFolder, flow, 'benchmark.json'), JSON.stringify(benchmark, null, 2)); proxyLogger.flushLogs(); } diff --git a/yarn-project/pxe/src/contract_function_simulator/contract_function_simulator.ts b/yarn-project/pxe/src/contract_function_simulator/contract_function_simulator.ts index 2942cb22cd63..ee5a7c7119df 100644 --- a/yarn-project/pxe/src/contract_function_simulator/contract_function_simulator.ts +++ b/yarn-project/pxe/src/contract_function_simulator/contract_function_simulator.ts @@ -1,5 +1,6 @@ import { Fr } from '@aztec/foundation/fields'; import { type Logger, createLogger } from '@aztec/foundation/log'; +import { Timer } from '@aztec/foundation/timer'; import { type CircuitSimulator, ExecutionError, @@ -52,6 +53,7 @@ export class ContractFunctionSimulator { msgSender = AztecAddress.fromField(Fr.MAX_FIELD_VALUE), scopes?: AztecAddress[], ): Promise { + const simulatorSetupTimer = new Timer(); const header = await this.executionDataProvider.getBlockHeader(); await verifyCurrentClassId(contractAddress, this.executionDataProvider); @@ -97,6 +99,8 @@ export class ContractFunctionSimulator { scopes, ); + const setupTime = simulatorSetupTimer.ms(); + try { const executionResult = await executePrivateFunction( this.simulator, @@ -105,6 +109,7 @@ export class ContractFunctionSimulator { contractAddress, request.functionSelector, ); + const simulatorTeardownTimer = new Timer(); const { usedTxRequestHashForNonces } = noteCache.finish(); const firstNullifierHint = usedTxRequestHashForNonces ? Fr.ZERO : noteCache.getAllNullifiers()[0]; @@ -119,6 +124,13 @@ export class ContractFunctionSimulator { }), ); + const teardownTime = simulatorTeardownTimer.ms(); + + // Add simulator overhead to topmost call in the stack + if (executionResult.profileResult) { + executionResult.profileResult.timings.witgen += setupTime + teardownTime; + } + return new PrivateExecutionResult(executionResult, firstNullifierHint, publicFunctionsCalldata); } catch (err) { throw createSimulationError(err instanceof Error ? err : new Error('Unknown error during private execution')); @@ -174,4 +186,8 @@ export class ContractFunctionSimulator { } } // docs:end:execute_utility_function + + getStats() { + return this.executionDataProvider.getStats(); + } } diff --git a/yarn-project/pxe/src/contract_function_simulator/execution_data_provider.ts b/yarn-project/pxe/src/contract_function_simulator/execution_data_provider.ts index 1593753f2b4e..20864f5c7058 100644 --- a/yarn-project/pxe/src/contract_function_simulator/execution_data_provider.ts +++ b/yarn-project/pxe/src/contract_function_simulator/execution_data_provider.ts @@ -13,7 +13,7 @@ import type { KeyValidationRequest } from '@aztec/stdlib/kernel'; import { IndexedTaggingSecret, LogWithTxData } from '@aztec/stdlib/logs'; import type { NoteStatus } from '@aztec/stdlib/note'; import { type MerkleTreeId, type NullifierMembershipWitness, PublicDataWitness } from '@aztec/stdlib/trees'; -import type { BlockHeader, TxHash } from '@aztec/stdlib/tx'; +import type { BlockHeader, NodeStats, TxHash } from '@aztec/stdlib/tx'; import type { MessageLoadOracleInputs } from './oracle/message_load_oracle_inputs.js'; import type { NoteData } from './oracle/typed_oracle.js'; @@ -36,6 +36,16 @@ export class ContractClassNotFoundError extends Error { } } +/* + * Collected stats during the execution of a transaction. + */ +export type ExecutionStats = { + /** + * Contains an entry for each RPC call performed during the execution + */ + nodeRPCCalls: NodeStats; +}; + /** * The interface for the data layer required to perform private and utility execution. */ @@ -372,4 +382,10 @@ export interface ExecutionDataProvider { logIndexInTx: number, txIndexInBlock: number, ): Promise; + + /** + * Returns the execution statistics collected during the simulator run. + * @returns The execution statistics. + */ + getStats(): ExecutionStats; } diff --git a/yarn-project/pxe/src/contract_function_simulator/oracle/private_execution.ts b/yarn-project/pxe/src/contract_function_simulator/oracle/private_execution.ts index d45de3a26564..ca730461b811 100644 --- a/yarn-project/pxe/src/contract_function_simulator/oracle/private_execution.ts +++ b/yarn-project/pxe/src/contract_function_simulator/oracle/private_execution.ts @@ -83,6 +83,15 @@ export async function executePrivateFunction( const noteHashNullifierCounterMap = privateExecutionOracle.getNoteHashNullifierCounterMap(); const nestedExecutions = privateExecutionOracle.getNestedExecutions(); + let timerSubtractionList = nestedExecutions; + let witgenTime = duration; + + // Due to the recursive nature of execution, we have to subtract the time taken by nested calls + while (timerSubtractionList.length > 0) { + witgenTime -= timerSubtractionList.reduce((acc, nested) => acc + (nested.profileResult?.timings.witgen ?? 0), 0); + timerSubtractionList = timerSubtractionList.flatMap(nested => nested.nestedExecutions ?? []); + } + log.debug(`Returning from call to ${contractAddress.toString()}:${functionSelector}`); return new PrivateCallExecutionResult( @@ -98,10 +107,7 @@ export async function executePrivateFunction( contractClassLogs, { timings: { - witgen: - // Due to the recursive nature of execution, we have to subtract the time taken by the first level of - // child executions - duration - nestedExecutions.reduce((acc, nested) => acc + (nested.profileResult?.timings.witgen ?? 0), 0), + witgen: witgenTime, oracles: acirExecutionResult.oracles, }, }, diff --git a/yarn-project/pxe/src/contract_function_simulator/oracle/private_execution_oracle.ts b/yarn-project/pxe/src/contract_function_simulator/oracle/private_execution_oracle.ts index c30832e2c01b..c640ff25e900 100644 --- a/yarn-project/pxe/src/contract_function_simulator/oracle/private_execution_oracle.ts +++ b/yarn-project/pxe/src/contract_function_simulator/oracle/private_execution_oracle.ts @@ -1,6 +1,7 @@ import { MAX_FR_CALLDATA_TO_ALL_ENQUEUED_CALLS, PRIVATE_CONTEXT_INPUTS_LENGTH } from '@aztec/constants'; import { Fr } from '@aztec/foundation/fields'; import { createLogger } from '@aztec/foundation/log'; +import { Timer } from '@aztec/foundation/timer'; import { type CircuitSimulator, toACVMWitness } from '@aztec/simulator/client'; import { type FunctionAbi, @@ -363,6 +364,7 @@ export class PrivateExecutionOracle extends UtilityExecutionOracle { sideEffectCounter: number, isStaticCall: boolean, ) { + const simulatorSetupTimer = new Timer(); this.log.debug( `Calling private function ${targetContractAddress}:${functionSelector} from ${this.callContext.contractAddress}`, ); @@ -401,6 +403,8 @@ export class PrivateExecutionOracle extends UtilityExecutionOracle { this.scopes, ); + const setupTime = simulatorSetupTimer.ms(); + const childExecutionResult = await executePrivateFunction( this.simulator, context, @@ -416,6 +420,12 @@ export class PrivateExecutionOracle extends UtilityExecutionOracle { this.nestedExecutions.push(childExecutionResult); const publicInputs = childExecutionResult.publicInputs; + + // Add simulator overhead to this call + if (childExecutionResult.profileResult) { + childExecutionResult.profileResult.timings.witgen += setupTime; + } + return { endSideEffectCounter: publicInputs.endSideEffectCounter, returnsHash: publicInputs.returnsHash, diff --git a/yarn-project/pxe/src/contract_function_simulator/proxied_node.ts b/yarn-project/pxe/src/contract_function_simulator/proxied_node.ts new file mode 100644 index 000000000000..dc09a64cf081 --- /dev/null +++ b/yarn-project/pxe/src/contract_function_simulator/proxied_node.ts @@ -0,0 +1,33 @@ +import { Timer } from '@aztec/foundation/timer'; +import type { AztecNode } from '@aztec/stdlib/interfaces/client'; +import type { NodeStats } from '@aztec/stdlib/tx'; + +/* + * Proxy for an AztecNode that tracks the time taken for each RPC call. + */ +export type ProxiedNode = AztecNode & { getStats(): NodeStats }; + +export class ProxiedNodeFactory { + static create(node: AztecNode) { + const stats: Partial> = {}; + return new Proxy(node, { + get(target, prop: keyof ProxiedNode) { + if (prop === 'getStats') { + return () => { + return stats; + }; + } else { + return async function (...args: any[]) { + if (!stats[prop]) { + stats[prop] = { times: [] }; + } + const timer = new Timer(); + const result = await (target[prop] as any).apply(target, args); + stats[prop].times.push(timer.ms()); + return result; + }; + } + }, + }) as ProxiedNode; + } +} diff --git a/yarn-project/pxe/src/pxe_oracle_interface/pxe_oracle_interface.test.ts b/yarn-project/pxe/src/contract_function_simulator/pxe_oracle_interface.test.ts similarity index 100% rename from yarn-project/pxe/src/pxe_oracle_interface/pxe_oracle_interface.test.ts rename to yarn-project/pxe/src/contract_function_simulator/pxe_oracle_interface.test.ts diff --git a/yarn-project/pxe/src/pxe_oracle_interface/pxe_oracle_interface.ts b/yarn-project/pxe/src/contract_function_simulator/pxe_oracle_interface.ts similarity index 98% rename from yarn-project/pxe/src/pxe_oracle_interface/pxe_oracle_interface.ts rename to yarn-project/pxe/src/contract_function_simulator/pxe_oracle_interface.ts index ea729ad43f3a..ac081286b970 100644 --- a/yarn-project/pxe/src/pxe_oracle_interface/pxe_oracle_interface.ts +++ b/yarn-project/pxe/src/contract_function_simulator/pxe_oracle_interface.ts @@ -30,7 +30,7 @@ import { MerkleTreeId, type NullifierMembershipWitness, PublicDataWitness } from import type { BlockHeader } from '@aztec/stdlib/tx'; import { TxHash } from '@aztec/stdlib/tx'; -import type { ExecutionDataProvider } from '../contract_function_simulator/execution_data_provider.js'; +import type { ExecutionDataProvider, ExecutionStats } from '../contract_function_simulator/execution_data_provider.js'; import { MessageLoadOracleInputs } from '../contract_function_simulator/oracle/message_load_oracle_inputs.js'; import type { AddressDataProvider } from '../storage/address_data_provider/address_data_provider.js'; import type { CapsuleDataProvider } from '../storage/capsule_data_provider/capsule_data_provider.js'; @@ -40,6 +40,7 @@ import type { NoteDataProvider } from '../storage/note_data_provider/note_data_p import type { PrivateEventDataProvider } from '../storage/private_event_data_provider/private_event_data_provider.js'; import type { SyncDataProvider } from '../storage/sync_data_provider/sync_data_provider.js'; import type { TaggingDataProvider } from '../storage/tagging_data_provider/tagging_data_provider.js'; +import type { ProxiedNode } from './proxied_node.js'; import { WINDOW_HALF_SIZE, getIndexedTaggingSecretsForTheWindow, getInitialIndexesMap } from './tagging_utils.js'; /** @@ -47,7 +48,7 @@ import { WINDOW_HALF_SIZE, getIndexedTaggingSecretsForTheWindow, getInitialIndex */ export class PXEOracleInterface implements ExecutionDataProvider { constructor( - private aztecNode: AztecNode, + private aztecNode: AztecNode | ProxiedNode, private keyStore: KeyStore, private contractDataProvider: ContractDataProvider, private noteDataProvider: NoteDataProvider, @@ -823,4 +824,11 @@ export class PXEOracleInterface implements ExecutionDataProvider { blockNumber, ); } + + getStats(): ExecutionStats { + const nodeRPCCalls = + typeof (this.aztecNode as ProxiedNode).getStats === 'function' ? (this.aztecNode as ProxiedNode).getStats() : {}; + + return { nodeRPCCalls }; + } } diff --git a/yarn-project/pxe/src/pxe_oracle_interface/tagging_utils.ts b/yarn-project/pxe/src/contract_function_simulator/tagging_utils.ts similarity index 100% rename from yarn-project/pxe/src/pxe_oracle_interface/tagging_utils.ts rename to yarn-project/pxe/src/contract_function_simulator/tagging_utils.ts diff --git a/yarn-project/pxe/src/entrypoints/client/bundle/index.ts b/yarn-project/pxe/src/entrypoints/client/bundle/index.ts index 46e26b7bd029..bdf6dcfcb8e4 100644 --- a/yarn-project/pxe/src/entrypoints/client/bundle/index.ts +++ b/yarn-project/pxe/src/entrypoints/client/bundle/index.ts @@ -2,4 +2,4 @@ export * from '../../../pxe_service/index.js'; export * from '../../../config/index.js'; export * from '../../../storage/index.js'; export * from './utils.js'; -export { PXEOracleInterface } from '../../../pxe_oracle_interface/pxe_oracle_interface.js'; +export { PXEOracleInterface } from '../../../contract_function_simulator/pxe_oracle_interface.js'; diff --git a/yarn-project/pxe/src/entrypoints/client/lazy/index.ts b/yarn-project/pxe/src/entrypoints/client/lazy/index.ts index 46e26b7bd029..bdf6dcfcb8e4 100644 --- a/yarn-project/pxe/src/entrypoints/client/lazy/index.ts +++ b/yarn-project/pxe/src/entrypoints/client/lazy/index.ts @@ -2,4 +2,4 @@ export * from '../../../pxe_service/index.js'; export * from '../../../config/index.js'; export * from '../../../storage/index.js'; export * from './utils.js'; -export { PXEOracleInterface } from '../../../pxe_oracle_interface/pxe_oracle_interface.js'; +export { PXEOracleInterface } from '../../../contract_function_simulator/pxe_oracle_interface.js'; diff --git a/yarn-project/pxe/src/entrypoints/server/index.ts b/yarn-project/pxe/src/entrypoints/server/index.ts index aa939b193464..abd5fe499127 100644 --- a/yarn-project/pxe/src/entrypoints/server/index.ts +++ b/yarn-project/pxe/src/entrypoints/server/index.ts @@ -3,4 +3,4 @@ export * from '../../pxe_http/index.js'; export * from '../../config/index.js'; export * from '../../storage/index.js'; export * from './utils.js'; -export { PXEOracleInterface } from '../../pxe_oracle_interface/pxe_oracle_interface.js'; +export { PXEOracleInterface } from '../../contract_function_simulator/pxe_oracle_interface.js'; diff --git a/yarn-project/pxe/src/private_kernel/private_kernel_execution_prover.ts b/yarn-project/pxe/src/private_kernel/private_kernel_execution_prover.ts index 2b82d0f5c886..e6f540b435ee 100644 --- a/yarn-project/pxe/src/private_kernel/private_kernel_execution_prover.ts +++ b/yarn-project/pxe/src/private_kernel/private_kernel_execution_prover.ts @@ -115,8 +115,8 @@ export class PrivateKernelExecutionProver { validationRequestsSplitCounter, ); while (resetBuilder.needsReset()) { - const privateInputs = await resetBuilder.build(this.oracle, noteHashLeafIndexMap); const witgenTimer = new Timer(); + const privateInputs = await resetBuilder.build(this.oracle, noteHashLeafIndexMap); output = generateWitnesses ? await this.proofCreator.generateResetOutput(privateInputs) : await this.proofCreator.simulateReset(privateInputs); @@ -161,6 +161,8 @@ export class PrivateKernelExecutionProver { const privateCallData = await this.createPrivateCallData(currentExecution); if (firstIteration) { + const witgenTimer = new Timer(); + const proofInput = new PrivateKernelInitCircuitPrivateInputs( txRequest, getVKTreeRoot(), @@ -175,7 +177,6 @@ export class PrivateKernelExecutionProver { pushTestData('private-kernel-inputs-init', proofInput); - const witgenTimer = new Timer(); output = generateWitnesses ? await this.proofCreator.generateInitOutput(proofInput) : await this.proofCreator.simulateInit(proofInput); @@ -190,6 +191,7 @@ export class PrivateKernelExecutionProver { }, }); } else { + const witgenTimer = new Timer(); const previousVkMembershipWitness = await this.oracle.getVkMembershipWitness( output.verificationKey.keyAsFields, ); @@ -202,7 +204,6 @@ export class PrivateKernelExecutionProver { const proofInput = new PrivateKernelInnerCircuitPrivateInputs(previousKernelData, privateCallData); pushTestData('private-kernel-inputs-inner', proofInput); - const witgenTimer = new Timer(); output = generateWitnesses ? await this.proofCreator.generateInnerOutput(proofInput) : await this.proofCreator.simulateInner(proofInput); @@ -228,8 +229,8 @@ export class PrivateKernelExecutionProver { validationRequestsSplitCounter, ); while (resetBuilder.needsReset()) { - const privateInputs = await resetBuilder.build(this.oracle, noteHashLeafIndexMap); const witgenTimer = new Timer(); + const privateInputs = await resetBuilder.build(this.oracle, noteHashLeafIndexMap); output = generateWitnesses ? await this.proofCreator.generateResetOutput(privateInputs) : await this.proofCreator.simulateReset(privateInputs); diff --git a/yarn-project/pxe/src/pxe_service/pxe_service.ts b/yarn-project/pxe/src/pxe_service/pxe_service.ts index 70e073f7024a..1809ae159e30 100644 --- a/yarn-project/pxe/src/pxe_service/pxe_service.ts +++ b/yarn-project/pxe/src/pxe_service/pxe_service.ts @@ -75,12 +75,13 @@ import type { PXEServiceConfig } from '../config/index.js'; import { getPackageInfo } from '../config/package_info.js'; import { ContractFunctionSimulator } from '../contract_function_simulator/contract_function_simulator.js'; import { readCurrentClassId } from '../contract_function_simulator/oracle/private_execution.js'; +import { ProxiedNodeFactory } from '../contract_function_simulator/proxied_node.js'; +import { PXEOracleInterface } from '../contract_function_simulator/pxe_oracle_interface.js'; import { PrivateKernelExecutionProver, type PrivateKernelExecutionProverConfig, } from '../private_kernel/private_kernel_execution_prover.js'; import { PrivateKernelOracleImpl } from '../private_kernel/private_kernel_oracle_impl.js'; -import { PXEOracleInterface } from '../pxe_oracle_interface/pxe_oracle_interface.js'; import { AddressDataProvider } from '../storage/address_data_provider/address_data_provider.js'; import { CapsuleDataProvider } from '../storage/capsule_data_provider/capsule_data_provider.js'; import { ContractDataProvider } from '../storage/contract_data_provider/contract_data_provider.js'; @@ -108,7 +109,7 @@ export class PXEService implements PXE { private taggingDataProvider: TaggingDataProvider, private addressDataProvider: AddressDataProvider, private privateEventDataProvider: PrivateEventDataProvider, - private contractFunctionSimulator: ContractFunctionSimulator, + private simulator: CircuitSimulator, private packageVersion: string, private proverEnabled: boolean, private proofCreator: PrivateKernelProver, @@ -158,19 +159,7 @@ export class PXEService implements PXE { config, loggerOrSuffix, ); - const pxeOracleInterface = new PXEOracleInterface( - node, - keyStore, - contractDataProvider, - noteDataProvider, - capsuleDataProvider, - syncDataProvider, - taggingDataProvider, - addressDataProvider, - privateEventDataProvider, - log, - ); - const contractFunctionSimulator = new ContractFunctionSimulator(pxeOracleInterface, simulator); + const jobQueue = new SerialQueue(); const pxeService = new PXEService( @@ -184,7 +173,7 @@ export class PXEService implements PXE { taggingDataProvider, addressDataProvider, privateEventDataProvider, - contractFunctionSimulator, + simulator, packageVersion, proverEnabled, proofCreator, @@ -249,6 +238,22 @@ export class PXEService implements PXE { // Internal methods + #getSimulatorForTx(): ContractFunctionSimulator { + const pxeOracleInterface = new PXEOracleInterface( + ProxiedNodeFactory.create(this.node), + this.keyStore, + this.contractDataProvider, + this.noteDataProvider, + this.capsuleDataProvider, + this.syncDataProvider, + this.taggingDataProvider, + this.addressDataProvider, + this.privateEventDataProvider, + this.log, + ); + return new ContractFunctionSimulator(pxeOracleInterface, this.simulator); + } + #contextualizeError(err: Error, ...context: string[]): Error { let contextStr = ''; if (context.length > 0) { @@ -330,6 +335,7 @@ export class PXEService implements PXE { } async #executePrivate( + contractFunctionSimulator: ContractFunctionSimulator, txRequest: TxExecutionRequest, msgSender?: AztecAddress, scopes?: AztecAddress[], @@ -337,7 +343,7 @@ export class PXEService implements PXE { const { origin: contractAddress, functionSelector } = txRequest; try { - const result = await this.contractFunctionSimulator.run( + const result = await contractFunctionSimulator.run( txRequest, contractAddress, functionSelector, @@ -356,15 +362,21 @@ export class PXEService implements PXE { /** * Simulate a utility function call on the given contract. + * @param contractFunctionSimulator - The simulator to use for the function call. * @param call - The function call to execute. * @param authWitnesses - Authentication witnesses required for the function call. * @param scopes - Optional array of account addresses whose notes can be accessed in this call. Defaults to all * accounts if not specified. * @returns The simulation result containing the outputs of the utility function. */ - async #simulateUtility(call: FunctionCall, authWitnesses?: AuthWitness[], scopes?: AztecAddress[]) { + async #simulateUtility( + contractFunctionSimulator: ContractFunctionSimulator, + call: FunctionCall, + authWitnesses?: AuthWitness[], + scopes?: AztecAddress[], + ) { try { - return this.contractFunctionSimulator.runUtility(call, authWitnesses ?? [], scopes); + return contractFunctionSimulator.runUtility(call, authWitnesses ?? [], scopes); } catch (err) { if (err instanceof SimulationError) { await enrichSimulationError(err, this.contractDataProvider, this.log); @@ -668,11 +680,13 @@ export class PXEService implements PXE { const totalTimer = new Timer(); try { let syncTime: number | undefined; + let contractFunctionSimulator: ContractFunctionSimulator | undefined; if (!privateExecutionResult) { const syncTimer = new Timer(); await this.synchronizer.sync(); syncTime = syncTimer.ms(); - privateExecutionResult = await this.#executePrivate(txRequest); + contractFunctionSimulator = this.#getSimulatorForTx(); + privateExecutionResult = await this.#executePrivate(contractFunctionSimulator, txRequest); } const { publicInputs, @@ -705,8 +719,10 @@ export class PXEService implements PXE { this.log.info(`Proving completed in ${totalTime}ms`, { timings, }); - - return new TxProvingResult(privateExecutionResult, publicInputs, clientIvcProof!, timings); + return new TxProvingResult(privateExecutionResult, publicInputs, clientIvcProof!, { + timings, + nodeRPCCalls: contractFunctionSimulator?.getStats().nodeRPCCalls, + }); } catch (err: any) { throw this.#contextualizeError(err, inspect(txRequest), inspect(privateExecutionResult)); } @@ -740,7 +756,8 @@ export class PXEService implements PXE { await this.synchronizer.sync(); const syncTime = syncTimer.ms(); - const privateExecutionResult = await this.#executePrivate(txRequest, msgSender); + const contractFunctionSimulator = this.#getSimulatorForTx(); + const privateExecutionResult = await this.#executePrivate(contractFunctionSimulator, txRequest, msgSender); const { executionSteps, timings: { proving } = {} } = await this.#prove( txRequest, @@ -767,20 +784,19 @@ export class PXEService implements PXE { const gateCountComputationTime = executionSteps.reduce((acc, { timings }) => acc + (timings.gateCount ?? 0), 0) ?? 0; + const total = totalTime - gateCountComputationTime; + const timings: ProvingTimings = { - total: totalTime - gateCountComputationTime, + total, sync: syncTime, proving, perFunction, unaccounted: - totalTime - - ((syncTime ?? 0) + - (proving ?? 0) + - perFunction.reduce((acc, { time }) => acc + time, 0) + - gateCountComputationTime), + total - ((syncTime ?? 0) + (proving ?? 0) + perFunction.reduce((acc, { time }) => acc + time, 0)), }; - return new TxProfileResult(executionSteps, timings); + const simulatorStats = contractFunctionSimulator.getStats(); + return new TxProfileResult(executionSteps, { timings, nodeRPCCalls: simulatorStats.nodeRPCCalls }); } catch (err: any) { throw this.#contextualizeError( err, @@ -824,7 +840,13 @@ export class PXEService implements PXE { await this.synchronizer.sync(); const syncTime = syncTimer.ms(); - const privateExecutionResult = await this.#executePrivate(txRequest, msgSender, scopes); + const contractFunctionSimulator = this.#getSimulatorForTx(); + const privateExecutionResult = await this.#executePrivate( + contractFunctionSimulator, + txRequest, + msgSender, + scopes, + ); const { publicInputs, executionSteps } = await this.#prove( txRequest, @@ -891,14 +913,13 @@ export class PXEService implements PXE { revertReason: publicOutput.revertReason, } : {}), - timings, }); - return TxSimulationResult.fromPrivateSimulationResultAndPublicOutput( - privateSimulationResult, - publicOutput, + const simulatorStats = contractFunctionSimulator.getStats(); + return TxSimulationResult.fromPrivateSimulationResultAndPublicOutput(privateSimulationResult, publicOutput, { timings, - ); + nodeRPCCalls: simulatorStats.nodeRPCCalls, + }); } catch (err: any) { throw this.#contextualizeError( err, @@ -945,7 +966,13 @@ export class PXEService implements PXE { // TODO - Should check if `from` has the permission to call the view function. const functionCall = await this.#getFunctionCall(functionName, args, to); const functionTimer = new Timer(); - const executionResult = await this.#simulateUtility(functionCall, authwits ?? [], scopes); + const contractFunctionSimulator = this.#getSimulatorForTx(); + const executionResult = await this.#simulateUtility( + contractFunctionSimulator, + functionCall, + authwits ?? [], + scopes, + ); const functionTime = functionTimer.ms(); const totalTime = totalTimer.ms(); @@ -959,7 +986,8 @@ export class PXEService implements PXE { unaccounted: totalTime - (syncTime + perFunction.reduce((acc, { time }) => acc + time, 0)), }; - return { result: executionResult, timings }; + const simulationStats = contractFunctionSimulator.getStats(); + return { result: executionResult, stats: { timings, nodeRPCCalls: simulationStats.nodeRPCCalls } }; } catch (err: any) { const stringifiedArgs = args.map(arg => arg.toString()).join(', '); throw this.#contextualizeError( diff --git a/yarn-project/stdlib/src/interfaces/pxe.test.ts b/yarn-project/stdlib/src/interfaces/pxe.test.ts index fee94a88116a..6daca706665b 100644 --- a/yarn-project/stdlib/src/interfaces/pxe.test.ts +++ b/yarn-project/stdlib/src/interfaces/pxe.test.ts @@ -371,10 +371,13 @@ class MockPXE implements PXE { const provingTime = skipProofGeneration ? 1 : undefined; return Promise.resolve( new TxProfileResult([], { - perFunction: [{ functionName: 'something', time: 1 }], - proving: provingTime, - unaccounted: 1, - total: 2, + nodeRPCCalls: { getBlockNumber: { times: [1] } }, + timings: { + perFunction: [{ functionName: 'something', time: 1 }], + proving: provingTime, + unaccounted: 1, + total: 2, + }, }), ); } diff --git a/yarn-project/stdlib/src/tx/profiling.ts b/yarn-project/stdlib/src/tx/profiling.ts index f641cd012ddd..8459ff9242ee 100644 --- a/yarn-project/stdlib/src/tx/profiling.ts +++ b/yarn-project/stdlib/src/tx/profiling.ts @@ -4,9 +4,14 @@ import { type ZodFor, optional } from '@aztec/foundation/schemas'; import { z } from 'zod'; import type { AbiDecoded } from '../abi/decoder.js'; +import type { AztecNode } from '../interfaces/aztec-node.js'; import { type PrivateExecutionStep, PrivateExecutionStepSchema } from '../kernel/private_kernel_prover_output.js'; import { AbiDecodedSchema } from '../schemas/schemas.js'; +export type NodeStats = Partial>; + +const NodeStatsSchema = z.record(z.string(), z.object({ times: z.array(z.number()) })); + type FunctionTiming = { functionName: string; time: number; @@ -35,6 +40,16 @@ export const ProvingTimingsSchema = z.object({ total: z.number(), }); +export interface ProvingStats { + timings: ProvingTimings; + nodeRPCCalls?: NodeStats; +} + +export const ProvingStatsSchema = z.object({ + timings: ProvingTimingsSchema, + nodeRPCCalls: optional(NodeStatsSchema), +}); + export interface SimulationTimings { sync: number; publicSimulation?: number; @@ -53,19 +68,29 @@ export const SimulationTimingsSchema = z.object({ total: z.number(), }); +export interface SimulationStats { + timings: SimulationTimings; + nodeRPCCalls: NodeStats; +} + +export const SimulationStatsSchema = z.object({ + timings: SimulationTimingsSchema, + nodeRPCCalls: NodeStatsSchema, +}); + export class TxProfileResult { constructor( public executionSteps: PrivateExecutionStep[], - public timings: ProvingTimings, + public stats: ProvingStats, ) {} static get schema(): ZodFor { return z .object({ executionSteps: z.array(PrivateExecutionStepSchema), - timings: ProvingTimingsSchema, + stats: ProvingStatsSchema, }) - .transform(({ executionSteps, timings }) => new TxProfileResult(executionSteps, timings)); + .transform(({ executionSteps, stats }) => new TxProfileResult(executionSteps, stats)); } static random(): TxProfileResult { @@ -83,16 +108,19 @@ export class TxProfileResult { }, ], { - sync: 1, - proving: 1, - perFunction: [ - { - functionName: 'random', - time: 1, - }, - ], - unaccounted: 1, - total: 4, + nodeRPCCalls: { getBlockHeader: { times: [1] } }, + timings: { + sync: 1, + proving: 1, + perFunction: [ + { + functionName: 'random', + time: 1, + }, + ], + unaccounted: 1, + total: 4, + }, }, ); } @@ -101,31 +129,34 @@ export class TxProfileResult { export class UtilitySimulationResult { constructor( public result: AbiDecoded, - public timings?: SimulationTimings, + public stats?: SimulationStats, ) {} static get schema(): ZodFor { return z .object({ result: AbiDecodedSchema, - timings: optional(SimulationTimingsSchema), + stats: optional(SimulationStatsSchema), }) - .transform(({ result, timings }) => new UtilitySimulationResult(result, timings)); + .transform(({ result, stats }) => new UtilitySimulationResult(result, stats)); } static random(): UtilitySimulationResult { return new UtilitySimulationResult(Fr.random().toBigInt(), { - sync: 1, - publicSimulation: 1, - validation: 1, - perFunction: [ - { - functionName: 'random', - time: 1, - }, - ], - unaccounted: 1, - total: 5, + nodeRPCCalls: { getBlockHeader: { times: [1] } }, + timings: { + sync: 1, + publicSimulation: 1, + validation: 1, + perFunction: [ + { + functionName: 'random', + time: 1, + }, + ], + unaccounted: 1, + total: 5, + }, }); } } diff --git a/yarn-project/stdlib/src/tx/proven_tx.ts b/yarn-project/stdlib/src/tx/proven_tx.ts index 43babf8b68f7..1d80b681de99 100644 --- a/yarn-project/stdlib/src/tx/proven_tx.ts +++ b/yarn-project/stdlib/src/tx/proven_tx.ts @@ -6,7 +6,7 @@ import { z } from 'zod'; import { PrivateKernelTailCircuitPublicInputs } from '../kernel/private_kernel_tail_circuit_public_inputs.js'; import { ClientIvcProof } from '../proofs/client_ivc_proof.js'; import { PrivateExecutionResult, collectSortedContractClassLogs } from './private_execution_result.js'; -import { type ProvingTimings, ProvingTimingsSchema } from './profiling.js'; +import { type ProvingStats, ProvingTimingsSchema } from './profiling.js'; import { Tx } from './tx.js'; export class TxProvingResult { @@ -14,7 +14,7 @@ export class TxProvingResult { public privateExecutionResult: PrivateExecutionResult, public publicInputs: PrivateKernelTailCircuitPublicInputs, public clientIvcProof: ClientIvcProof, - public timings?: ProvingTimings, + public stats?: ProvingStats, ) {} toTx(): Tx { diff --git a/yarn-project/stdlib/src/tx/simulated_tx.ts b/yarn-project/stdlib/src/tx/simulated_tx.ts index 08408757c711..370c7ecaee88 100644 --- a/yarn-project/stdlib/src/tx/simulated_tx.ts +++ b/yarn-project/stdlib/src/tx/simulated_tx.ts @@ -12,7 +12,7 @@ import { PrivateExecutionResult, collectSortedContractClassLogs, } from './private_execution_result.js'; -import { type SimulationTimings, SimulationTimingsSchema } from './profiling.js'; +import { type SimulationStats, SimulationStatsSchema } from './profiling.js'; import { NestedProcessReturnValues, PublicSimulationOutput } from './public_simulation_output.js'; import { Tx } from './tx.js'; @@ -44,7 +44,7 @@ export class TxSimulationResult { public privateExecutionResult: PrivateExecutionResult, public publicInputs: PrivateKernelTailCircuitPublicInputs, public publicOutput?: PublicSimulationOutput, - public timings?: SimulationTimings, + public stats?: SimulationStats, ) {} get gasUsed(): GasUsed { @@ -64,7 +64,7 @@ export class TxSimulationResult { privateExecutionResult: PrivateExecutionResult.schema, publicInputs: PrivateKernelTailCircuitPublicInputs.schema, publicOutput: PublicSimulationOutput.schema.optional(), - timings: optional(SimulationTimingsSchema), + stats: optional(SimulationStatsSchema), }) .transform(TxSimulationResult.from); } @@ -74,20 +74,20 @@ export class TxSimulationResult { fields.privateExecutionResult, fields.publicInputs, fields.publicOutput, - fields.timings, + fields.stats, ); } static fromPrivateSimulationResultAndPublicOutput( privateSimulationResult: PrivateSimulationResult, publicOutput?: PublicSimulationOutput, - timings?: SimulationTimings, + stats?: SimulationStats, ) { return new TxSimulationResult( privateSimulationResult.privateExecutionResult, privateSimulationResult.publicInputs, publicOutput, - timings, + stats, ); }