From 33dc4633cad5883c97c5f1d9ff6bb18c9408c369 Mon Sep 17 00:00:00 2001
From: benesjan <13470840+benesjan@users.noreply.github.com>
Date: Fri, 16 Jan 2026 16:20:34 +0000
Subject: [PATCH] feat: measuring node round trips
In this PR I implement round trip tracking and I use this info as a basis for a roundtrip regression test. This check was placed in `yarn-project/end-to-end/src/bench/client_flows/amm.test.ts`. This test was a good candidate for this because we already use it for benchmarking and it doesn't communicate with node over `http` (instead we have a direct access to the instance) - this is great as it makes the dest more deterministic and hence the roundtrip counts should not be brittle (when communicating over network I can imagine that latency or something could result in different counts e.g. when syncing PXE or something - not super sure if this is still the case though but it was the case in the past when we were polling node for new blocks)
In that test I also implemented a debugging functionality that allows for debugging the regression by nicely printing out round trip trace.
To track the round trips I updated `ProxiedNode` by having an `inFlightCount` counter tracking num current RPC calls. This allows us to group parallel rpc calls into the same round trip.
Fixes https://linear.app/aztec-labs/issue/F-230/improve-benchmarks
The linked issue seems to have a wider scope than just the round trip measurements so not sure if we want to fully close it with this PR.
---
.../contract/components/FunctionCard.tsx | 22 ++-
.../cli-wallet/src/utils/profiling.ts | 16 +-
.../src/bench/client_flows/amm.test.ts | 184 +++++++++++++++++-
.../src/bench/client_flows/benchmark.ts | 26 ++-
.../contract_function_simulator.ts | 7 +-
.../proxied_node.ts | 96 +++++++--
yarn-project/stdlib/src/tx/profiling.ts | 50 ++++-
7 files changed, 374 insertions(+), 27 deletions(-)
diff --git a/playground/src/components/contract/components/FunctionCard.tsx b/playground/src/components/contract/components/FunctionCard.tsx
index fb84bf14dfe7..cfb87fbf585c 100644
--- a/playground/src/components/contract/components/FunctionCard.tsx
+++ b/playground/src/components/contract/components/FunctionCard.tsx
@@ -148,8 +148,8 @@ export function FunctionCard({ fn, contract, contractArtifact, onSendTxRequested
return { ...step, subtotal: acc };
});
- const totalRPCCalls = Object.values(profileResult.stats.nodeRPCCalls ?? {}).reduce(
- (acc, calls) => acc + calls.times.length,
+ const totalRPCCalls = Object.values(profileResult.stats.nodeRPCCalls?.perMethod ?? {}).reduce(
+ (acc, calls) => acc + (calls?.times.length ?? 0),
0,
);
@@ -211,7 +211,7 @@ export function FunctionCard({ fn, contract, contractArtifact, onSendTxRequested
'& .MuiBadge-badge': {
backgroundColor: colors.secondary.main,
color: colors.text.primary,
- }
+ },
}}
>
@@ -263,7 +263,15 @@ export function FunctionCard({ fn, contract, contractArtifact, onSendTxRequested
Simulation results:
-
+
{simulationResults?.success ? (
{simulationResults?.data ?? 'No return value'}
) : (
@@ -281,7 +289,11 @@ export function FunctionCard({ fn, contract, contractArtifact, onSendTxRequested
<>
diff --git a/yarn-project/cli-wallet/src/utils/profiling.ts b/yarn-project/cli-wallet/src/utils/profiling.ts
index e14be66ca1e3..eb94f22f0390 100644
--- a/yarn-project/cli-wallet/src/utils/profiling.ts
+++ b/yarn-project/cli-wallet/src/utils/profiling.ts
@@ -95,7 +95,7 @@ export function printProfileResult(
if (stats.nodeRPCCalls) {
log(format('\nRPC calls:\n'));
- for (const [method, { times }] of Object.entries(stats.nodeRPCCalls)) {
+ for (const [method, { times }] of Object.entries(stats.nodeRPCCalls.perMethod)) {
const calls = times.length;
const total = times.reduce((acc, time) => acc + time, 0);
const avg = total / calls;
@@ -112,6 +112,20 @@ export function printProfileResult(
),
);
}
+
+ const { roundTrips } = stats.nodeRPCCalls;
+ log(format('\nRound trips (actual blocking waits):\n'));
+ log(format('Round trips:'.padEnd(25), `${roundTrips.roundTrips}`.padStart(COLUMN_MAX_WIDTH)));
+ log(
+ format(
+ 'Total blocking time:'.padEnd(25),
+ `${roundTrips.totalBlockingTime.toFixed(2)}ms`.padStart(COLUMN_MAX_WIDTH),
+ ),
+ );
+ if (roundTrips.roundTrips > 0) {
+ const avgRoundTrip = roundTrips.totalBlockingTime / roundTrips.roundTrips;
+ log(format('Avg round trip:'.padEnd(25), `${avgRoundTrip.toFixed(2)}ms`.padStart(COLUMN_MAX_WIDTH)));
+ }
}
log(format('\nSync time:'.padEnd(25), `${timings.sync?.toFixed(2)}ms`.padStart(16)));
diff --git a/yarn-project/end-to-end/src/bench/client_flows/amm.test.ts b/yarn-project/end-to-end/src/bench/client_flows/amm.test.ts
index 79283b631bdd..8e057945b9de 100644
--- a/yarn-project/end-to-end/src/bench/client_flows/amm.test.ts
+++ b/yarn-project/end-to-end/src/bench/client_flows/amm.test.ts
@@ -6,6 +6,7 @@ import { AMMContract } from '@aztec/noir-contracts.js/AMM';
import { FPCContract } from '@aztec/noir-contracts.js/FPC';
import { SponsoredFPCContract } from '@aztec/noir-contracts.js/SponsoredFPC';
import { TokenContract } from '@aztec/noir-contracts.js/Token';
+import type { RoundTripStats } from '@aztec/stdlib/tx';
import type { TestWallet } from '@aztec/test-wallet/server';
import { jest } from '@jest/globals';
@@ -20,7 +21,25 @@ const AMOUNT_PER_NOTE = 1_000_000;
const MINIMUM_NOTES_FOR_RECURSION_LEVEL = [0, 2, 10];
+// Set to true to print out the round trip information to the console.
+const DEBUG_ROUND_TRIPS = true;
+
+// Expected number of node round trips per account contract and payment method.
+const EXPECTED_ROUND_TRIPS: Record = {
+ 'ecdsar1+private_fpc': 118,
+ 'ecdsar1+sponsored_fpc': 80,
+ 'schnorr+private_fpc': 118,
+ 'schnorr+sponsored_fpc': 82,
+};
+
+interface RoundTripData {
+ accountType: AccountType;
+ paymentMethod: BenchmarkingFeePaymentMethod;
+ roundTrips: RoundTripStats | undefined;
+}
+
describe('AMM benchmark', () => {
+ const roundTripData: RoundTripData[] = [];
const t = new ClientFlowsBenchmark('amm');
// The wallet used by the admin to interact
let adminWallet: Wallet;
@@ -70,6 +89,9 @@ describe('AMM benchmark', () => {
});
afterAll(async () => {
+ if (DEBUG_ROUND_TRIPS) {
+ printRoundTripDebuggingInfo(roundTripData);
+ }
await t.teardown();
});
@@ -158,7 +180,7 @@ describe('AMM benchmark', () => {
.methods.add_liquidity(amountToSend, amountToSend, amountToSend, amountToSend, nonceForAuthwits)
.with({ authWitnesses: [token0Authwit, token1Authwit] });
- await captureProfile(
+ const profileResult = await captureProfile(
`${accountType}+amm_add_liquidity_1_recursions+${benchmarkingPaymentMethod}`,
addLiquidityInteraction,
options,
@@ -180,6 +202,36 @@ describe('AMM benchmark', () => {
const tx = await addLiquidityInteraction.send({ from: benchysAddress }).wait();
expect(tx.transactionFee!).toBeGreaterThan(0n);
}
+
+ if (DEBUG_ROUND_TRIPS) {
+ roundTripData.push({
+ accountType,
+ paymentMethod: benchmarkingPaymentMethod,
+ roundTrips: profileResult.stats.nodeRPCCalls?.roundTrips,
+ });
+ } else {
+ const roundTripsKey = `${accountType}+${benchmarkingPaymentMethod}`;
+ const actualRoundTrips = profileResult.stats.nodeRPCCalls?.roundTrips.roundTrips ?? 0;
+ const expectedRoundTrips = EXPECTED_ROUND_TRIPS[roundTripsKey];
+ if (expectedRoundTrips === undefined) {
+ throw new Error(
+ `Missing expected round trips for ${roundTripsKey}. ` +
+ `Add '${roundTripsKey}': ${actualRoundTrips} to EXPECTED_ROUND_TRIPS.`,
+ );
+ }
+
+ // The following check serves as a regression test. If you get a failure and it is expected, just update
+ // the EXPECTED_ROUND_TRIPS constants. If the failure is unexpected, it needs to be investigated. To
+ // investigate, set the DEBUG_ROUND_TRIPS environment variable to true on both the `next` branch and
+ // here, then compare the outputs (look for any newly appearing or missing round trips). To compare the
+ // outputs I recommend using a diff checker like https://www.diffchecker.com/.
+ //
+ // Note that the round trip values depend on this test suite being run in its entirety and in the correct
+ // order.
+ //
+ // If you encounter this failure in CI and are unsure of the cause, contact @benesjan.
+ expect(actualRoundTrips).toBe(expectedRoundTrips);
+ }
});
});
}
@@ -189,4 +241,134 @@ describe('AMM benchmark', () => {
}
});
}
+
+ /**
+ * Prints out the round trip information that can be used to debug unexpected round trip changes.
+ */
+ function printRoundTripDebuggingInfo(data: RoundTripData[]) {
+ for (const trip of data) {
+ if (!trip.roundTrips) {
+ throw new Error(
+ `Round trip stats are undefined for ${trip.accountType}+${trip.paymentMethod}. This should not happen.`,
+ );
+ }
+ }
+
+ const width = 120; // Fixed standard width
+ const title = ' ROUND TRIP DEBUGGING INFORMATION';
+
+ // Helper function to wrap long lines, breaking at commas when possible
+ function wrapLine(content: string, maxContentWidth: number): string[] {
+ if (content.length <= maxContentWidth) {
+ return [content];
+ }
+
+ const lines: string[] = [];
+ let remaining = content;
+
+ while (remaining.length > 0) {
+ if (remaining.length <= maxContentWidth) {
+ lines.push(remaining);
+ break;
+ }
+
+ // Try to find a good break point (comma followed by space)
+ let breakPoint = maxContentWidth;
+ const commaIndex = remaining.lastIndexOf(', ', maxContentWidth);
+ if (commaIndex > maxContentWidth * 0.4) {
+ // Use comma break if it's not too early (at least 40% into the line)
+ breakPoint = commaIndex + 2; // Include the comma and space
+ }
+
+ const line = remaining.substring(0, breakPoint);
+ lines.push(line);
+ remaining = remaining.substring(breakPoint).trim();
+ }
+
+ return lines;
+ }
+
+ // Group by account type for better organization
+ const groupedByAccount = data.reduce(
+ (acc, item) => {
+ if (!acc[item.accountType]) {
+ acc[item.accountType] = [];
+ }
+ acc[item.accountType].push(item);
+ return acc;
+ },
+ {} as Record,
+ );
+
+ const accountEntries = Object.entries(groupedByAccount) as [AccountType, RoundTripData[]][];
+ const topBorder = '╔' + '═'.repeat(width - 2) + '╗';
+ const bottomBorder = '╚' + '═'.repeat(width - 2) + '╝';
+ const sectionTop = '╠' + '═'.repeat(width - 2) + '╣';
+ const sectionDivider = '╠' + '─'.repeat(width - 2) + '╣';
+ const sectionBottom = '╠' + '─'.repeat(width - 2) + '╣';
+ const leftBorder = '║';
+ const rightBorder = '║';
+
+ let output = '\n' + topBorder + '\n';
+ output += leftBorder + title.padEnd(width - 2) + rightBorder + '\n';
+ output += sectionTop + '\n';
+
+ for (let accountIdx = 0; accountIdx < accountEntries.length; accountIdx++) {
+ const [accountType, items] = accountEntries[accountIdx];
+
+ if (accountIdx > 0) {
+ output += sectionDivider + '\n';
+ }
+
+ output += leftBorder + ` Account Type: ${accountType.toUpperCase()}`.padEnd(width - 2) + rightBorder + '\n';
+ output += sectionDivider + '\n';
+
+ for (let itemIdx = 0; itemIdx < items.length; itemIdx++) {
+ const item = items[itemIdx];
+
+ if (!item.roundTrips) {
+ throw new Error(
+ `Round trip stats are undefined for ${item.accountType}+${item.paymentMethod}. This should not happen.`,
+ );
+ }
+ const roundTrips = item.roundTrips; // TypeScript now knows it's defined
+
+ if (itemIdx > 0) {
+ output += leftBorder + ' '.repeat(width - 2) + rightBorder + '\n';
+ }
+
+ const key = `${item.accountType}+${item.paymentMethod}`;
+ output += leftBorder + ` Configuration: ${key}`.padEnd(width - 2) + rightBorder + '\n';
+ output += leftBorder + ` Total Round Trips: ${roundTrips.roundTrips}`.padEnd(width - 2) + rightBorder + '\n';
+ output += leftBorder + ` Payment Method: ${item.paymentMethod}`.padEnd(width - 2) + rightBorder + '\n';
+ output += leftBorder + ' '.repeat(width - 2) + rightBorder + '\n';
+ output += leftBorder + ' Per Round Trip:'.padEnd(width - 2) + rightBorder + '\n';
+
+ for (let i = 0; i < roundTrips.roundTripMethods.length; i++) {
+ const methods = roundTrips.roundTripMethods[i];
+ const methodsStr = methods.length > 0 ? methods.join(', ') : '(empty)';
+ const rtNum = String(i + 1).padStart(3, ' ');
+ const prefix = ` RT ${rtNum}: `;
+ const content = `[${methodsStr}]`;
+
+ // Calculate available width for content (account for borders and prefix)
+ // width - 2 for borders, then subtract prefix length
+ const maxContentWidth = width - 2 - prefix.length;
+ const wrappedLines = wrapLine(content, maxContentWidth);
+
+ for (let lineIdx = 0; lineIdx < wrappedLines.length; lineIdx++) {
+ const lineContent = lineIdx === 0 ? prefix + wrappedLines[lineIdx] : ' ' + wrappedLines[lineIdx]; // Continuation line with extra indentation
+ output += leftBorder + lineContent.padEnd(width - 2) + rightBorder + '\n';
+ }
+ }
+ }
+ }
+
+ output += sectionBottom + '\n';
+ output += leftBorder + ` Total Test Runs: ${data.length}`.padEnd(width - 2) + rightBorder + '\n';
+ output += bottomBorder + '\n';
+
+ // eslint-disable-next-line no-console
+ console.log(output);
+ }
});
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 44d82bdf5fdc..34aa59e74fc5 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,13 @@ import {
import type { Logger } from '@aztec/aztec.js/log';
import { createLogger } from '@aztec/foundation/log';
import { type PrivateExecutionStep, serializePrivateExecutionSteps } from '@aztec/stdlib/kernel';
-import type { ProvingStats, ProvingTimings, SimulationStats, SimulationTimings } from '@aztec/stdlib/tx';
+import type {
+ ProvingStats,
+ ProvingTimings,
+ RoundTripStats,
+ SimulationStats,
+ SimulationTimings,
+} from '@aztec/stdlib/tx';
import assert from 'node:assert';
import { mkdir, writeFile } from 'node:fs/promises';
@@ -119,6 +125,7 @@ type ClientFlowBenchmark = {
timings: Omit & { witgen: number };
maxMemory: number;
rpc: Record;
+ roundTrips: RoundTripStats;
proverType: ProverType;
minimumTrace: StructuredTrace;
totalGateCount: number;
@@ -212,6 +219,10 @@ export function generateBenchmark(
}, []);
const timings = stats.timings;
const totalGateCount = steps[steps.length - 1].accGateCount;
+ const nodeRPCCalls = stats.nodeRPCCalls ?? {
+ perMethod: {},
+ roundTrips: { roundTrips: 0, totalBlockingTime: 0, roundTripDurations: [], roundTripMethods: [] },
+ };
return {
name: flow,
timings: {
@@ -221,7 +232,7 @@ export function generateBenchmark(
unaccounted: timings.unaccounted,
witgen: timings.perFunction.reduce((acc, fn) => acc + fn.time, 0),
},
- rpc: Object.entries(stats.nodeRPCCalls ?? {}).reduce(
+ rpc: Object.entries(nodeRPCCalls.perMethod).reduce(
(acc, [RPCName, RPCCalls]) => {
const total = RPCCalls.times.reduce((sum, time) => sum + time, 0);
const calls = RPCCalls.times.length;
@@ -236,6 +247,7 @@ export function generateBenchmark(
},
{} as Record,
),
+ roundTrips: nodeRPCCalls.roundTrips,
maxMemory,
proverType,
minimumTrace: minimumTrace!,
@@ -280,6 +292,16 @@ export function convertProfileToGHBenchmark(benchmark: ClientFlowBenchmark): Git
value: totalRPCCalls,
unit: 'calls',
},
+ {
+ name: `${benchmark.name}/round_trips`,
+ value: benchmark.roundTrips.roundTrips,
+ unit: 'round_trips',
+ },
+ {
+ name: `${benchmark.name}/round_trips_blocking_time`,
+ value: benchmark.roundTrips.totalBlockingTime,
+ unit: 'ms',
+ },
];
if (benchmark.timings.proving) {
benches.push({
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 3b17d54f1553..01e2fe9f82e2 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
@@ -331,7 +331,12 @@ export class ContractFunctionSimulator {
*/
getStats() {
const nodeRPCCalls =
- typeof (this.aztecNode as ProxiedNode).getStats === 'function' ? (this.aztecNode as ProxiedNode).getStats() : {};
+ typeof (this.aztecNode as ProxiedNode).getStats === 'function'
+ ? (this.aztecNode as ProxiedNode).getStats()
+ : {
+ perMethod: {},
+ roundTrips: { roundTrips: 0, totalBlockingTime: 0, roundTripDurations: [], roundTripMethods: [] },
+ };
return { nodeRPCCalls };
}
diff --git a/yarn-project/pxe/src/contract_function_simulator/proxied_node.ts b/yarn-project/pxe/src/contract_function_simulator/proxied_node.ts
index 65ea8cfb80ac..039bad515e08 100644
--- a/yarn-project/pxe/src/contract_function_simulator/proxied_node.ts
+++ b/yarn-project/pxe/src/contract_function_simulator/proxied_node.ts
@@ -1,30 +1,100 @@
import { Timer } from '@aztec/foundation/timer';
import type { AztecNode } from '@aztec/stdlib/interfaces/client';
-import type { NodeStats } from '@aztec/stdlib/tx';
+import type { NodeStats, RoundTripStats } from '@aztec/stdlib/tx';
/*
- * Proxy generator for an AztecNode that tracks the time taken for each RPC call.
+ * Proxy generator for an AztecNode that tracks the time taken for each RPC call and the number of round trips (actual
+ * blocking waits for node responses).
+ *
+ * A round trip is counted when we transition from 0 to 1 in-flight calls, and ends when all concurrent calls complete.
+ * This means parallel calls in Promise.all count as a single round trip.
+ *
+ * Note that batching of RPC calls in `safe_json_rpc_client.ts` could affect the round trip counts but in places we
+ * currently use this information we do not even use HTTP as we have direct access to the Aztec Node instance in TS
+ * (i.e. not running against external node) so this is not a problem for now.
+ *
+ * If you want to use this against external node and the info gets skewed by batching you can set the `maxBatchSize`
+ * value in `safe_json_rpc_client.ts` to 1 (the main motivation for batching was to get around parallel http requests
+ * limits in web browsers which is not a problem when debugging in node.js).
*/
export type ProxiedNode = AztecNode & { getStats(): NodeStats };
export class ProxiedNodeFactory {
- static create(node: AztecNode) {
- const stats: Partial> = {};
+ static create(node: AztecNode): ProxiedNode {
+ // Per-method call stats
+ const perMethod: Partial> = {};
+
+ // Round trip tracking
+ let inFlightCount = 0;
+ let currentRoundTripTimer: Timer | null = null;
+ let currentRoundTripMethods: string[] = [];
+ const roundTrips: RoundTripStats = {
+ roundTrips: 0,
+ totalBlockingTime: 0,
+ roundTripDurations: [],
+ roundTripMethods: [],
+ };
+
return new Proxy(node, {
get(target, prop: keyof ProxiedNode) {
if (prop === 'getStats') {
- return () => {
- return stats;
+ return (): NodeStats => {
+ return { perMethod, roundTrips };
};
} else {
- return async function (...args: any[]) {
- if (!stats[prop]) {
- stats[prop] = { times: [] };
+ return function (...args: any[]) {
+ // Track per-method stats
+ if (!perMethod[prop]) {
+ perMethod[prop] = { times: [] };
+ }
+
+ // Start of a new round trip batch?
+ if (inFlightCount === 0) {
+ roundTrips.roundTrips++;
+ currentRoundTripTimer = new Timer();
+ currentRoundTripMethods = [];
+ }
+ inFlightCount++;
+ currentRoundTripMethods.push(prop);
+
+ const callTimer = new Timer();
+ const result = (target[prop] as any).apply(target, args);
+
+ // Handle completion - called when the call finishes (after Promise resolves)
+ const handleCompletion = () => {
+ const callTime = callTimer.ms();
+ perMethod[prop]!.times.push(callTime);
+
+ inFlightCount--;
+
+ // End of round trip batch - all concurrent calls completed
+ if (inFlightCount === 0 && currentRoundTripTimer) {
+ const roundTripTime = currentRoundTripTimer.ms();
+ roundTrips.totalBlockingTime += roundTripTime;
+ roundTrips.roundTripDurations.push(roundTripTime);
+ roundTrips.roundTripMethods.push(currentRoundTripMethods);
+ currentRoundTripTimer = null;
+ currentRoundTripMethods = [];
+ }
+ };
+
+ // If the result is a Promise, chain the completion handler
+ if (result && typeof result.then === 'function') {
+ return result.then(
+ (value: any) => {
+ handleCompletion();
+ return value;
+ },
+ (error: any) => {
+ handleCompletion();
+ throw error;
+ },
+ );
+ } else {
+ // Synchronous method - handle completion immediately
+ handleCompletion();
+ return result;
}
- const timer = new Timer();
- const result = await (target[prop] as any).apply(target, args);
- stats[prop].times.push(timer.ms());
- return result;
};
}
},
diff --git a/yarn-project/stdlib/src/tx/profiling.ts b/yarn-project/stdlib/src/tx/profiling.ts
index be2271654f4b..093e7af471ed 100644
--- a/yarn-project/stdlib/src/tx/profiling.ts
+++ b/yarn-project/stdlib/src/tx/profiling.ts
@@ -6,9 +6,35 @@ import { z } from 'zod';
import type { AztecNode } from '../interfaces/aztec-node.js';
import { type PrivateExecutionStep, PrivateExecutionStepSchema } from '../kernel/private_kernel_prover_output.js';
-export type NodeStats = Partial>;
+export type RoundTripStats = {
+ /** Number of round trips (times we blocked waiting for node responses) */
+ roundTrips: number;
+ /** Total wall-clock time spent waiting on node (excludes parallel overlap) */
+ totalBlockingTime: number;
+ /** Individual round trip durations */
+ roundTripDurations: number[];
+ /** Methods called in each round trip (parallel calls grouped together) */
+ roundTripMethods: string[][];
+};
+
+const RoundTripStatsSchema = z.object({
+ roundTrips: z.number(),
+ totalBlockingTime: z.number(),
+ roundTripDurations: z.array(z.number()),
+ roundTripMethods: z.array(z.array(z.string())),
+});
+
+export type NodeStats = {
+ /** Per-method call stats */
+ perMethod: Partial>;
+ /** Round trip stats tracking actual blocking waits */
+ roundTrips: RoundTripStats;
+};
-const NodeStatsSchema = z.record(z.string(), z.object({ times: z.array(z.number()) }));
+const NodeStatsSchema = z.object({
+ perMethod: z.record(z.string(), z.object({ times: z.array(z.number()) })),
+ roundTrips: RoundTripStatsSchema,
+});
type FunctionTiming = {
functionName: string;
@@ -105,7 +131,15 @@ export class TxProfileResult {
},
],
{
- nodeRPCCalls: { getBlockHeader: { times: [1] } },
+ nodeRPCCalls: {
+ perMethod: { getBlockHeader: { times: [1] } },
+ roundTrips: {
+ roundTrips: 1,
+ totalBlockingTime: 1,
+ roundTripDurations: [1],
+ roundTripMethods: [['getBlockHeader']],
+ },
+ },
timings: {
sync: 1,
proving: 1,
@@ -140,7 +174,15 @@ export class UtilitySimulationResult {
static random(): UtilitySimulationResult {
return new UtilitySimulationResult([Fr.random()], {
- nodeRPCCalls: { getBlockHeader: { times: [1] } },
+ nodeRPCCalls: {
+ perMethod: { getBlockHeader: { times: [1] } },
+ roundTrips: {
+ roundTrips: 1,
+ totalBlockingTime: 1,
+ roundTripDurations: [1],
+ roundTripMethods: [['getBlockHeader']],
+ },
+ },
timings: {
sync: 1,
publicSimulation: 1,