From a375ce5737ab790c3db479aa12ab7127cd8bf058 Mon Sep 17 00:00:00 2001 From: Alex Gherghisan Date: Thu, 20 Jun 2024 09:58:04 +0000 Subject: [PATCH 1/4] feat: trace block building --- cspell.json | 1 + docker-compose.yml | 15 ++ .../aztec-node/src/aztec-node/server.ts | 2 + yarn-project/bb-prover/src/instrumentation.ts | 5 + .../bb-prover/src/prover/bb_prover.ts | 16 +- yarn-project/bb-prover/src/stats.ts | 16 +- .../bb-prover/src/test/test_circuit_prover.ts | 13 +- .../circuit-types/src/tx/processed_tx.ts | 17 ++ .../src/fixtures/snapshot_manager.ts | 7 +- yarn-project/foundation/.prettierrc.json | 2 +- .../prover-client/src/mocks/test_context.ts | 4 +- .../src/orchestrator/orchestrator.ts | 197 ++++++++++++++---- .../orchestrator_failures.test.ts | 2 +- .../orchestrator_lifecycle.test.ts | 2 +- .../orchestrator_workflow.test.ts | 3 +- .../prover-client/src/tx-prover/tx-prover.ts | 7 +- yarn-project/sequencer-client/package.json | 1 + .../src/client/sequencer-client.ts | 10 +- .../src/sequencer/sequencer.test.ts | 2 + .../src/sequencer/sequencer.ts | 182 +++++++++------- yarn-project/sequencer-client/tsconfig.json | 3 + yarn-project/simulator/package.json | 1 + .../src/public/public_processor.test.ts | 3 + .../simulator/src/public/public_processor.ts | 13 +- yarn-project/simulator/tsconfig.json | 3 + yarn-project/telemetry-client/package.json | 2 + .../telemetry-client/src/attributes.ts | 13 ++ yarn-project/telemetry-client/src/noop.ts | 72 ++++++- yarn-project/telemetry-client/src/otel.ts | 24 ++- .../telemetry-client/src/telemetry.ts | 97 ++++++++- yarn-project/yarn.lock | 75 +++++++ 31 files changed, 652 insertions(+), 158 deletions(-) diff --git a/cspell.json b/cspell.json index e7e5c5a5447..9a41fb4eb43 100644 --- a/cspell.json +++ b/cspell.json @@ -222,6 +222,7 @@ "rushstack", "schnorr", "secp", + "SEMRESATTRS", "sigchld", "Signerless", "siloes", diff --git a/docker-compose.yml b/docker-compose.yml index f0001adcebd..7c44b906b5c 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -143,6 +143,13 @@ services: - source: grafana-sources target: /etc/grafana/provisioning/datasources/default.yml + jaeger: + image: jaegertracing/all-in-one + ports: + - 16686:16686 + profiles: + - metrics + volumes: aztec: grafana: @@ -187,9 +194,17 @@ configs: prometheus: endpoint: 0.0.0.0:8889 metric_expiration: 5m + otlp/jaeger: + endpoint: "jaeger:4317" + tls: + insecure: true service: pipelines: + traces: + receivers: [otlp] + processors: [batch] + exporters: [otlp/jaeger] metrics: receivers: [otlp] processors: [batch] diff --git a/yarn-project/aztec-node/src/aztec-node/server.ts b/yarn-project/aztec-node/src/aztec-node/server.ts index 9afb6ed9ddb..4e1eb36c75d 100644 --- a/yarn-project/aztec-node/src/aztec-node/server.ts +++ b/yarn-project/aztec-node/src/aztec-node/server.ts @@ -206,6 +206,7 @@ export class AztecNodeService implements AztecNode { archiver, prover!, simulationProvider, + telemetry, ); return new AztecNodeService( @@ -763,6 +764,7 @@ export class AztecNodeService implements AztecNode { merkleTrees.asLatest(), this.contractDataSource, new WASMSimulator(), + this.telemetry, ); const processor = await publicProcessorFactory.create(prevHeader, newGlobalVariables); // REFACTOR: Consider merging ProcessReturnValues into ProcessedTx diff --git a/yarn-project/bb-prover/src/instrumentation.ts b/yarn-project/bb-prover/src/instrumentation.ts index c3adeebc345..a510388a428 100644 --- a/yarn-project/bb-prover/src/instrumentation.ts +++ b/yarn-project/bb-prover/src/instrumentation.ts @@ -6,6 +6,7 @@ import { type Histogram, Metrics, type TelemetryClient, + type Tracer, ValueType, } from '@aztec/telemetry-client'; @@ -24,8 +25,12 @@ export class ProverInstrumentation { private circuitSize: Gauge; private circuitPublicInputCount: Gauge; + public readonly tracer: Tracer; + constructor(telemetry: TelemetryClient, name: string) { + this.tracer = telemetry.getTracer(name); const meter = telemetry.getMeter(name); + this.simulationDuration = meter.createHistogram(Metrics.CIRCUIT_SIMULATION_DURATION, { description: 'Records how long it takes to simulate a circuit', unit: 's', diff --git a/yarn-project/bb-prover/src/prover/bb_prover.ts b/yarn-project/bb-prover/src/prover/bb_prover.ts index 30b4f6f3c6b..13f75a05cfd 100644 --- a/yarn-project/bb-prover/src/prover/bb_prover.ts +++ b/yarn-project/bb-prover/src/prover/bb_prover.ts @@ -57,7 +57,7 @@ import { convertRootRollupOutputsFromWitnessMap, } from '@aztec/noir-protocol-circuits-types'; import { NativeACVMSimulator } from '@aztec/simulator'; -import { type TelemetryClient } from '@aztec/telemetry-client'; +import { Attributes, type TelemetryClient, trackSpan } from '@aztec/telemetry-client'; import { abiEncode } from '@noir-lang/noirc_abi'; import { type Abi, type WitnessMap } from '@noir-lang/types'; @@ -111,6 +111,10 @@ export class BBNativeRollupProver implements ServerCircuitProver { this.instrumentation = new ProverInstrumentation(telemetry, 'BBNativeRollupProver'); } + get tracer() { + return this.instrumentation.tracer; + } + static async new(config: BBProverConfig, telemetry: TelemetryClient) { await fs.access(config.acvmBinaryPath, fs.constants.R_OK); await fs.mkdir(config.acvmWorkingDirectory, { recursive: true }); @@ -127,6 +131,7 @@ export class BBNativeRollupProver implements ServerCircuitProver { * @param inputs - Inputs to the circuit. * @returns The public inputs of the parity circuit. */ + @trackSpan('BBNativeRollupProver.getBaseParityProof', { [Attributes.PROTOCOL_CIRCUIT_NAME]: 'base-parity' }) public async getBaseParityProof(inputs: BaseParityInputs): Promise> { const { circuitOutput, proof } = await this.createRecursiveProof( inputs, @@ -148,6 +153,7 @@ export class BBNativeRollupProver implements ServerCircuitProver { * @param inputs - Inputs to the circuit. * @returns The public inputs of the parity circuit. */ + @trackSpan('BBNativeRollupProver.getBaseParityProof', { [Attributes.PROTOCOL_CIRCUIT_NAME]: 'root-parity' }) public async getRootParityProof( inputs: RootParityInputs, ): Promise> { @@ -171,6 +177,9 @@ export class BBNativeRollupProver implements ServerCircuitProver { * @param inputs - The inputs to the AVM circuit. * @returns The proof. */ + @trackSpan('BBNativeRollupProver.getAvmProof', inputs => ({ + [Attributes.APP_CIRCUIT_NAME]: inputs.functionName, + })) public async getAvmProof(inputs: AvmCircuitInputs): Promise { const proofAndVk = await this.createAvmProof(inputs); await this.verifyAvmProof(proofAndVk.proof, proofAndVk.verificationKey); @@ -182,6 +191,11 @@ export class BBNativeRollupProver implements ServerCircuitProver { * @param kernelRequest - The object encapsulating the request for a proof * @returns The requested circuit's public inputs and proof */ + @trackSpan('BBNativeRollupProver.getPublicKernelProof', kernelReq => ({ + [Attributes.PROTOCOL_CIRCUIT_NAME]: mapProtocolArtifactNameToCircuitName( + PublicKernelArtifactMapping[kernelReq.type]!.artifact, + ), + })) public async getPublicKernelProof( kernelRequest: PublicKernelNonTailRequest, ): Promise> { diff --git a/yarn-project/bb-prover/src/stats.ts b/yarn-project/bb-prover/src/stats.ts index c61b3d5ccca..f31e611dd8c 100644 --- a/yarn-project/bb-prover/src/stats.ts +++ b/yarn-project/bb-prover/src/stats.ts @@ -1,21 +1,7 @@ -import { type PublicKernelRequest, PublicKernelType } from '@aztec/circuit-types'; import type { CircuitName } from '@aztec/circuit-types/stats'; import { type ClientProtocolArtifact, type ServerProtocolArtifact } from '@aztec/noir-protocol-circuits-types'; -export function mapPublicKernelToCircuitName(kernelType: PublicKernelRequest['type']): CircuitName { - switch (kernelType) { - case PublicKernelType.SETUP: - return 'public-kernel-setup'; - case PublicKernelType.APP_LOGIC: - return 'public-kernel-app-logic'; - case PublicKernelType.TEARDOWN: - return 'public-kernel-teardown'; - case PublicKernelType.TAIL: - return 'public-kernel-tail'; - default: - throw new Error(`Unknown kernel type: ${kernelType}`); - } -} +export { mapPublicKernelToCircuitName } from '@aztec/circuit-types'; export function mapProtocolArtifactNameToCircuitName( artifact: ServerProtocolArtifact | ClientProtocolArtifact, diff --git a/yarn-project/bb-prover/src/test/test_circuit_prover.ts b/yarn-project/bb-prover/src/test/test_circuit_prover.ts index 5fd4a9efe7e..85ce58a9580 100644 --- a/yarn-project/bb-prover/src/test/test_circuit_prover.ts +++ b/yarn-project/bb-prover/src/test/test_circuit_prover.ts @@ -57,7 +57,7 @@ import { convertSimulatedPublicTailOutputFromWitnessMap, } from '@aztec/noir-protocol-circuits-types'; import { type SimulationProvider, WASMSimulator, emitCircuitSimulationStats } from '@aztec/simulator'; -import { type TelemetryClient } from '@aztec/telemetry-client'; +import { type TelemetryClient, trackSpan } from '@aztec/telemetry-client'; import { ProverInstrumentation } from '../instrumentation.js'; import { SimulatedPublicKernelArtifactMapping } from '../mappings/mappings.js'; @@ -93,6 +93,10 @@ export class TestCircuitProver implements ServerCircuitProver { this.instrumentation = new ProverInstrumentation(telemetry, 'TestCircuitProver'); } + get tracer() { + return this.instrumentation.tracer; + } + public async getEmptyPrivateKernelProof( inputs: PrivateKernelEmptyInputData, ): Promise> { @@ -117,6 +121,7 @@ export class TestCircuitProver implements ServerCircuitProver { * @param inputs - Inputs to the circuit. * @returns The public inputs of the parity circuit. */ + @trackSpan('TestCircuitProver.getBaseParityProof') public async getBaseParityProof(inputs: BaseParityInputs): Promise> { const timer = new Timer(); const witnessMap = convertBaseParityInputsToWitnessMap(inputs); @@ -149,6 +154,7 @@ export class TestCircuitProver implements ServerCircuitProver { * @param inputs - Inputs to the circuit. * @returns The public inputs of the parity circuit. */ + @trackSpan('TestCircuitProver.getRootParityProof') public async getRootParityProof( inputs: RootParityInputs, ): Promise> { @@ -183,6 +189,7 @@ export class TestCircuitProver implements ServerCircuitProver { * @param input - Inputs to the circuit. * @returns The public inputs as outputs of the simulation. */ + @trackSpan('TestCircuitProver.getBaseRollupProof') public async getBaseRollupProof( input: BaseRollupInputs, ): Promise> { @@ -213,6 +220,7 @@ export class TestCircuitProver implements ServerCircuitProver { * @param input - Inputs to the circuit. * @returns The public inputs as outputs of the simulation. */ + @trackSpan('TestCircuitProver.getMergeRollupProof') public async getMergeRollupProof( input: MergeRollupInputs, ): Promise> { @@ -244,6 +252,7 @@ export class TestCircuitProver implements ServerCircuitProver { * @param input - Inputs to the circuit. * @returns The public inputs as outputs of the simulation. */ + @trackSpan('TestCircuitProver.getRootRollupProof') public async getRootRollupProof( input: RootRollupInputs, ): Promise> { @@ -270,6 +279,7 @@ export class TestCircuitProver implements ServerCircuitProver { ); } + @trackSpan('TestCircuitProver.getPublicKernelProof') public async getPublicKernelProof( kernelRequest: PublicKernelNonTailRequest, ): Promise> { @@ -303,6 +313,7 @@ export class TestCircuitProver implements ServerCircuitProver { ); } + @trackSpan('TestCircuitProver.getPublicTailProof') public async getPublicTailProof( kernelRequest: PublicKernelTailRequest, ): Promise> { diff --git a/yarn-project/circuit-types/src/tx/processed_tx.ts b/yarn-project/circuit-types/src/tx/processed_tx.ts index 5147b71c2ce..d20983f261b 100644 --- a/yarn-project/circuit-types/src/tx/processed_tx.ts +++ b/yarn-project/circuit-types/src/tx/processed_tx.ts @@ -27,6 +27,8 @@ import { makeEmptyProof, } from '@aztec/circuits.js'; +import { type CircuitName } from '../stats/stats.js'; + /** * Used to communicate to the prover which type of circuit to prove */ @@ -304,3 +306,18 @@ export function validateProcessedTx(tx: ProcessedTx): void { validateProcessedTxLogs(tx); // TODO: validate other fields } + +export function mapPublicKernelToCircuitName(kernelType: PublicKernelRequest['type']): CircuitName { + switch (kernelType) { + case PublicKernelType.SETUP: + return 'public-kernel-setup'; + case PublicKernelType.APP_LOGIC: + return 'public-kernel-app-logic'; + case PublicKernelType.TEARDOWN: + return 'public-kernel-teardown'; + case PublicKernelType.TAIL: + return 'public-kernel-tail'; + default: + throw new Error(`Unknown kernel type: ${kernelType}`); + } +} diff --git a/yarn-project/end-to-end/src/fixtures/snapshot_manager.ts b/yarn-project/end-to-end/src/fixtures/snapshot_manager.ts index c897648c62c..64c8bf24d93 100644 --- a/yarn-project/end-to-end/src/fixtures/snapshot_manager.ts +++ b/yarn-project/end-to-end/src/fixtures/snapshot_manager.ts @@ -20,6 +20,7 @@ import { type Logger, createDebugLogger } from '@aztec/foundation/log'; import { makeBackoff, retry } from '@aztec/foundation/retry'; import { resolver, reviver } from '@aztec/foundation/serialize'; import { type PXEService, createPXEService, getPXEServiceConfig } from '@aztec/pxe'; +import { createAndStartTelemetryClient, getConfigEnvVars as getTelemetryConfig } from '@aztec/telemetry-client/start'; import { type Anvil, createAnvil } from '@viem/anvil'; import { existsSync, mkdirSync, readFileSync, writeFileSync } from 'fs'; @@ -270,8 +271,9 @@ async function setupFromFresh(statePath: string | undefined, logger: Logger): Pr aztecNodeConfig.bbWorkingDirectory = bbConfig.bbWorkingDirectory; } + const telemetry = createAndStartTelemetryClient(getTelemetryConfig(), 'aztec-test'); logger.verbose('Creating and synching an aztec node...'); - const aztecNode = await AztecNodeService.createAndSync(aztecNodeConfig); + const aztecNode = await AztecNodeService.createAndSync(aztecNodeConfig, telemetry); logger.verbose('Creating pxe...'); const pxeConfig = getPXEServiceConfig(); @@ -343,7 +345,8 @@ async function setupFromState(statePath: string, logger: Logger): Promise", "^\\./|\\.\\./"], "importOrderSeparation": true, "importOrderSortSpecifiers": true, - "importOrderParserPlugins": ["importAssertions", "typescript"] + "importOrderParserPlugins": ["importAssertions", "typescript", "decorators"] } diff --git a/yarn-project/prover-client/src/mocks/test_context.ts b/yarn-project/prover-client/src/mocks/test_context.ts index 32e0fcae8be..507068e36ae 100644 --- a/yarn-project/prover-client/src/mocks/test_context.ts +++ b/yarn-project/prover-client/src/mocks/test_context.ts @@ -96,6 +96,7 @@ export class TestContext { const publicWorldStateDB = mock(); const publicKernel = new RealPublicKernelCircuitSimulator(new WASMSimulator()); const actualDb = await MerkleTrees.new(openTmpStore()).then(t => t.asLatest()); + const telemetry = new NoopTelemetryClient(); const processor = new PublicProcessor( actualDb, publicExecutor, @@ -104,6 +105,7 @@ export class TestContext { Header.empty(), publicContractsDB, publicWorldStateDB, + telemetry, ); let localProver: ServerCircuitProver; @@ -125,7 +127,7 @@ export class TestContext { } const queue = new MemoryProvingQueue(); - const orchestrator = new ProvingOrchestrator(actualDb, queue); + const orchestrator = new ProvingOrchestrator(actualDb, queue, telemetry); const agent = new ProverAgent(localProver, proverCount); queue.start(); diff --git a/yarn-project/prover-client/src/orchestrator/orchestrator.ts b/yarn-project/prover-client/src/orchestrator/orchestrator.ts index 5e9469d4265..c6159039a72 100644 --- a/yarn-project/prover-client/src/orchestrator/orchestrator.ts +++ b/yarn-project/prover-client/src/orchestrator/orchestrator.ts @@ -9,6 +9,7 @@ import { type TxEffect, makeEmptyProcessedTx, makePaddingProcessedTx, + mapPublicKernelToCircuitName, toTxEffect, } from '@aztec/circuit-types'; import { @@ -20,6 +21,7 @@ import { type PublicInputsAndRecursiveProof, type ServerCircuitProver, } from '@aztec/circuit-types/interfaces'; +import { type CircuitName } from '@aztec/circuit-types/stats'; import { AGGREGATION_OBJECT_LENGTH, AvmCircuitInputs, @@ -53,6 +55,7 @@ import { createDebugLogger } from '@aztec/foundation/log'; import { promiseWithResolvers } from '@aztec/foundation/promise'; import { BufferReader, type Tuple } from '@aztec/foundation/serialize'; import { pushTestData } from '@aztec/foundation/testing'; +import { Attributes, type TelemetryClient, type Tracer, trackSpan, wrapCallbackInSpan } from '@aztec/telemetry-client'; import { type MerkleTreeOperations } from '@aztec/world-state'; import { inspect } from 'util'; @@ -91,7 +94,16 @@ export class ProvingOrchestrator { private pendingProvingJobs: AbortController[] = []; private paddingTx: PaddingProcessedTx | undefined = undefined; - constructor(private db: MerkleTreeOperations, private prover: ServerCircuitProver, private initialHeader?: Header) {} + public readonly tracer: Tracer; + + constructor( + private db: MerkleTreeOperations, + private prover: ServerCircuitProver, + telemetryClient: TelemetryClient, + private initialHeader?: Header, + ) { + this.tracer = telemetryClient.getTracer('ProvingOrchestrator'); + } /** * Resets the orchestrator's cached padding tx. @@ -108,6 +120,10 @@ export class ProvingOrchestrator { * @param verificationKeys - The private kernel verification keys * @returns A proving ticket, containing a promise notifying of proving completion */ + @trackSpan('ProvingOrchestrator.startNewBlock', (numTxs, globalVariables) => ({ + [Attributes.BLOCK_SIZE]: numTxs, + [Attributes.BLOCK_NUMBER]: globalVariables.blockNumber.toNumber(), + })) public async startNewBlock( numTxs: number, globalVariables: GlobalVariables, @@ -193,6 +209,9 @@ export class ProvingOrchestrator { * The interface to add a simulated transaction to the scheduler * @param tx - The transaction to be proven */ + @trackSpan('ProvingOrchestrator.addNewTx', tx => ({ + [Attributes.TX_HASH]: tx.hash.toString(), + })) public async addNewTx(tx: ProcessedTx): Promise { if (!this.provingState) { throw new Error(`Invalid proving state, call startNewBlock before adding transactions`); @@ -213,6 +232,17 @@ export class ProvingOrchestrator { /** * Marks the block as full and pads it to the full power of 2 block size, no more transactions will be accepted. */ + @trackSpan('ProvingOrchestrator.setBlockCompleted', function () { + if (!this.provingState) { + return {}; + } + + return { + [Attributes.BLOCK_NUMBER]: this.provingState!.globalVariables.blockNumber.toNumber(), + [Attributes.BLOCK_SIZE]: this.provingState!.totalNumTxs, + [Attributes.BLOCK_TXS_COUNT]: this.provingState!.transactionsReceived, + }; + }) public async setBlockCompleted() { if (!this.provingState) { throw new Error(`Invalid proving state, call startNewBlock before adding transactions or completing the block`); @@ -264,18 +294,26 @@ export class ProvingOrchestrator { logger.debug(`Enqueuing deferred proving for padding txs to enqueue ${txInputs.length} paddings`); this.deferredProving( provingState, - signal => - this.prover.getEmptyPrivateKernelProof( - { - // Chain id and version should not change even if the proving state does, so it's safe to use them for the padding tx - // which gets cached across multiple runs of the orchestrator with different proving states. If they were to change, - // we'd have to clear out the paddingTx here and regenerate it when they do. - chainId: unprovenPaddingTx.data.constants.txContext.chainId, - version: unprovenPaddingTx.data.constants.txContext.version, - header: unprovenPaddingTx.data.constants.historicalHeader, - }, - signal, - ), + wrapCallbackInSpan( + this.tracer, + 'ProvingOrchestrator.prover.getEmptyPrivateKernelProof', + { + [Attributes.PROTOCOL_CIRCUIT_TYPE]: 'server', + [Attributes.PROTOCOL_CIRCUIT_NAME]: 'private-kernel-empty' as CircuitName, + }, + signal => + this.prover.getEmptyPrivateKernelProof( + { + // Chain id and version should not change even if the proving state does, so it's safe to use them for the padding tx + // which gets cached across multiple runs of the orchestrator with different proving states. If they were to change, + // we'd have to clear out the paddingTx here and regenerate it when they do. + chainId: unprovenPaddingTx.data.constants.txContext.chainId, + version: unprovenPaddingTx.data.constants.txContext.version, + header: unprovenPaddingTx.data.constants.historicalHeader, + }, + signal, + ), + ), result => { logger.debug(`Completed proof for padding tx, now enqueuing ${txInputs.length} padding txs`); this.paddingTx = makePaddingProcessedTx(result); @@ -319,6 +357,13 @@ export class ProvingOrchestrator { * Performs the final tree update for the block and returns the fully proven block. * @returns The fully proven block and proof. */ + @trackSpan('ProvingOrchestrator.finaliseBlock', function () { + return { + [Attributes.BLOCK_NUMBER]: this.provingState!.globalVariables.blockNumber.toNumber(), + [Attributes.BLOCK_TXS_COUNT]: this.provingState!.transactionsReceived, + [Attributes.BLOCK_SIZE]: this.provingState!.totalNumTxs, + }; + }) public async finaliseBlock() { try { if ( @@ -496,6 +541,9 @@ export class ProvingOrchestrator { } // Updates the merkle trees for a transaction. The first enqueued job for a transaction + @trackSpan('ProvingOrchestrator.prepareBaseRollupInputs', (_, tx) => ({ + [Attributes.TX_HASH]: tx.hash.toString(), + })) private async prepareBaseRollupInputs( provingState: ProvingState | undefined, tx: ProcessedTx, @@ -593,7 +641,16 @@ export class ProvingOrchestrator { this.deferredProving( provingState, - signal => this.prover.getBaseRollupProof(tx.baseRollupInputs, signal), + wrapCallbackInSpan( + this.tracer, + 'ProvingOrchestrator.prover.getBaseRollupProof', + { + [Attributes.TX_HASH]: tx.processedTx.hash.toString(), + [Attributes.PROTOCOL_CIRCUIT_TYPE]: 'server', + [Attributes.PROTOCOL_CIRCUIT_NAME]: 'base-rollup' as CircuitName, + }, + signal => this.prover.getBaseRollupProof(tx.baseRollupInputs, signal), + ), result => { logger.debug(`Completed proof for base rollup for tx ${tx.processedTx.hash.toString()}`); validatePartialState(result.inputs.end, tx.treeSnapshots); @@ -622,7 +679,15 @@ export class ProvingOrchestrator { this.deferredProving( provingState, - signal => this.prover.getMergeRollupProof(inputs, signal), + wrapCallbackInSpan( + this.tracer, + 'ProvingOrchestrator.prover.getMergeRollupProof', + { + [Attributes.PROTOCOL_CIRCUIT_TYPE]: 'server', + [Attributes.PROTOCOL_CIRCUIT_NAME]: 'merge-rollup' as CircuitName, + }, + signal => this.prover.getMergeRollupProof(inputs, signal), + ), result => { this.storeAndExecuteNextMergeLevel(provingState, level, index, [ result.inputs, @@ -658,7 +723,15 @@ export class ProvingOrchestrator { this.deferredProving( provingState, - signal => this.prover.getRootRollupProof(inputs, signal), + wrapCallbackInSpan( + this.tracer, + 'ProvingOrchestrator.prover.getRootRollupProof', + { + [Attributes.PROTOCOL_CIRCUIT_TYPE]: 'server', + [Attributes.PROTOCOL_CIRCUIT_NAME]: 'root-rollup' as CircuitName, + }, + signal => this.prover.getRootRollupProof(inputs, signal), + ), result => { provingState.rootRollupPublicInputs = result.inputs; provingState.finalAggregationObject = extractAggregationObject( @@ -680,7 +753,15 @@ export class ProvingOrchestrator { private enqueueBaseParityCircuit(provingState: ProvingState, inputs: BaseParityInputs, index: number) { this.deferredProving( provingState, - signal => this.prover.getBaseParityProof(inputs, signal), + wrapCallbackInSpan( + this.tracer, + 'ProvingOrchestrator.prover.getBaseParityProof', + { + [Attributes.PROTOCOL_CIRCUIT_TYPE]: 'server', + [Attributes.PROTOCOL_CIRCUIT_NAME]: 'base-parity' as CircuitName, + }, + signal => this.prover.getBaseParityProof(inputs, signal), + ), rootInput => { provingState.setRootParityInputs(rootInput, index); if (provingState.areRootParityInputsReady()) { @@ -701,7 +782,15 @@ export class ProvingOrchestrator { private enqueueRootParityCircuit(provingState: ProvingState | undefined, inputs: RootParityInputs) { this.deferredProving( provingState, - signal => this.prover.getRootParityProof(inputs, signal), + wrapCallbackInSpan( + this.tracer, + 'ProvingOrchestrator.prover.getRootParityProof', + { + [Attributes.PROTOCOL_CIRCUIT_TYPE]: 'server', + [Attributes.PROTOCOL_CIRCUIT_NAME]: 'root-parity' as CircuitName, + }, + signal => this.prover.getRootParityProof(inputs, signal), + ), async rootInput => { provingState!.finalRootParityInput = rootInput; await this.checkAndEnqueueRootRollup(provingState); @@ -770,26 +859,34 @@ export class ProvingOrchestrator { if (publicFunction.vmRequest) { // This function tries to do AVM proving. If there is a failure, it fakes the proof unless AVM_PROVING_STRICT is defined. // Nothing downstream depends on the AVM proof yet. So having this mode lets us incrementally build the AVM circuit. - const doAvmProving = async (signal: AbortSignal) => { - const inputs: AvmCircuitInputs = new AvmCircuitInputs( - publicFunction.vmRequest!.functionName, - publicFunction.vmRequest!.bytecode, - publicFunction.vmRequest!.calldata, - publicFunction.vmRequest!.kernelRequest.inputs.publicCall.callStackItem.publicInputs, - publicFunction.vmRequest!.avmHints, - ); - try { - return await this.prover.getAvmProof(inputs, signal); - } catch (err) { - if (process.env.AVM_PROVING_STRICT) { - throw err; - } else { - logger.warn(`Error thrown when proving AVM circuit: ${err}`); - logger.warn(`AVM_PROVING_STRICT is off, faking AVM proof and carrying on...`); - return { proof: makeEmptyProof(), verificationKey: VerificationKeyData.makeFake() }; + const doAvmProving = wrapCallbackInSpan( + this.tracer, + 'ProvingOrchestrator.prover.getAvmProof', + { + [Attributes.TX_HASH]: txProvingState.processedTx.hash.toString(), + [Attributes.APP_CIRCUIT_NAME]: publicFunction.vmRequest!.functionName, + }, + async (signal: AbortSignal) => { + const inputs: AvmCircuitInputs = new AvmCircuitInputs( + publicFunction.vmRequest!.functionName, + publicFunction.vmRequest!.bytecode, + publicFunction.vmRequest!.calldata, + publicFunction.vmRequest!.kernelRequest.inputs.publicCall.callStackItem.publicInputs, + publicFunction.vmRequest!.avmHints, + ); + try { + return await this.prover.getAvmProof(inputs, signal); + } catch (err) { + if (process.env.AVM_PROVING_STRICT) { + throw err; + } else { + logger.warn(`Error thrown when proving AVM circuit: ${err}`); + logger.warn(`AVM_PROVING_STRICT is off, faking AVM proof and carrying on...`); + return { proof: makeEmptyProof(), verificationKey: VerificationKeyData.makeFake() }; + } } - } - }; + }, + ); this.deferredProving(provingState, doAvmProving, proofAndVk => { logger.debug(`Proven VM for function index ${functionIndex} of tx index ${txIndex}`); this.checkAndEnqueuePublicKernel(provingState, txIndex, functionIndex, proofAndVk.proof); @@ -835,13 +932,25 @@ export class ProvingOrchestrator { this.deferredProving( provingState, - (signal): Promise> => { - if (request.type === PublicKernelType.TAIL) { - return this.prover.getPublicTailProof(request, signal); - } else { - return this.prover.getPublicKernelProof(request, signal); - } - }, + wrapCallbackInSpan( + this.tracer, + request.type === PublicKernelType.TAIL + ? 'ProvingOrchestrator.prover.getPublicTailProof' + : 'ProvingOrchestrator.prover.getPublicKernelProof', + { + [Attributes.PROTOCOL_CIRCUIT_TYPE]: 'server', + [Attributes.PROTOCOL_CIRCUIT_NAME]: mapPublicKernelToCircuitName(request.type), + }, + ( + signal, + ): Promise> => { + if (request.type === PublicKernelType.TAIL) { + return this.prover.getPublicTailProof(request, signal); + } else { + return this.prover.getPublicKernelProof(request, signal); + } + }, + ), result => { const nextKernelRequest = txProvingState.getNextPublicKernelFromKernelProof( functionIndex, diff --git a/yarn-project/prover-client/src/orchestrator/orchestrator_failures.test.ts b/yarn-project/prover-client/src/orchestrator/orchestrator_failures.test.ts index e61cf418c3c..d53cb3aff11 100644 --- a/yarn-project/prover-client/src/orchestrator/orchestrator_failures.test.ts +++ b/yarn-project/prover-client/src/orchestrator/orchestrator_failures.test.ts @@ -30,7 +30,7 @@ describe('prover/orchestrator/failures', () => { beforeEach(() => { mockProver = new TestCircuitProver(new NoopTelemetryClient(), new WASMSimulator()); - orchestrator = new ProvingOrchestrator(context.actualDb, mockProver); + orchestrator = new ProvingOrchestrator(context.actualDb, mockProver, new NoopTelemetryClient()); }); it.each([ diff --git a/yarn-project/prover-client/src/orchestrator/orchestrator_lifecycle.test.ts b/yarn-project/prover-client/src/orchestrator/orchestrator_lifecycle.test.ts index 715211200cc..5814ae93b20 100644 --- a/yarn-project/prover-client/src/orchestrator/orchestrator_lifecycle.test.ts +++ b/yarn-project/prover-client/src/orchestrator/orchestrator_lifecycle.test.ts @@ -143,7 +143,7 @@ describe('prover/orchestrator/lifecycle', () => { it('cancels proving requests', async () => { const prover: ServerCircuitProver = new TestCircuitProver(new NoopTelemetryClient()); - const orchestrator = new ProvingOrchestrator(context.actualDb, prover); + const orchestrator = new ProvingOrchestrator(context.actualDb, prover, new NoopTelemetryClient()); const spy = jest.spyOn(prover, 'getBaseParityProof'); const deferredPromises: PromiseWithResolvers[] = []; diff --git a/yarn-project/prover-client/src/orchestrator/orchestrator_workflow.test.ts b/yarn-project/prover-client/src/orchestrator/orchestrator_workflow.test.ts index 07158f9aeec..e139b16d18f 100644 --- a/yarn-project/prover-client/src/orchestrator/orchestrator_workflow.test.ts +++ b/yarn-project/prover-client/src/orchestrator/orchestrator_workflow.test.ts @@ -11,6 +11,7 @@ import { makeGlobalVariables, makeRootParityInput } from '@aztec/circuits.js/tes import { promiseWithResolvers } from '@aztec/foundation/promise'; import { sleep } from '@aztec/foundation/sleep'; import { openTmpStore } from '@aztec/kv-store/utils'; +import { NoopTelemetryClient } from '@aztec/telemetry-client/noop'; import { type MerkleTreeOperations, MerkleTrees } from '@aztec/world-state'; import { type MockProxy, mock } from 'jest-mock-extended'; @@ -25,7 +26,7 @@ describe('prover/orchestrator', () => { beforeEach(async () => { actualDb = await MerkleTrees.new(openTmpStore()).then(t => t.asLatest()); mockProver = mock(); - orchestrator = new ProvingOrchestrator(actualDb, mockProver); + orchestrator = new ProvingOrchestrator(actualDb, mockProver, new NoopTelemetryClient()); }); it('calls root parity circuit only when ready', async () => { diff --git a/yarn-project/prover-client/src/tx-prover/tx-prover.ts b/yarn-project/prover-client/src/tx-prover/tx-prover.ts index 94353ae6c87..09392412912 100644 --- a/yarn-project/prover-client/src/tx-prover/tx-prover.ts +++ b/yarn-project/prover-client/src/tx-prover/tx-prover.ts @@ -34,7 +34,12 @@ export class TxProver implements ProverClient { initialHeader?: Header, ) { this.queue = new MemoryProvingQueue(config.proverJobTimeoutMs, config.proverJobPollIntervalMs); - this.orchestrator = new ProvingOrchestrator(worldStateSynchronizer.getLatest(), this.queue, initialHeader); + this.orchestrator = new ProvingOrchestrator( + worldStateSynchronizer.getLatest(), + this.queue, + telemetry, + initialHeader, + ); } async updateProverConfig(config: Partial): Promise { diff --git a/yarn-project/sequencer-client/package.json b/yarn-project/sequencer-client/package.json index a8479023370..05659179f15 100644 --- a/yarn-project/sequencer-client/package.json +++ b/yarn-project/sequencer-client/package.json @@ -35,6 +35,7 @@ "@aztec/p2p": "workspace:^", "@aztec/protocol-contracts": "workspace:^", "@aztec/simulator": "workspace:^", + "@aztec/telemetry-client": "workspace:^", "@aztec/types": "workspace:^", "@aztec/world-state": "workspace:^", "@noir-lang/acvm_js": "portal:../../noir/packages/acvm_js", diff --git a/yarn-project/sequencer-client/src/client/sequencer-client.ts b/yarn-project/sequencer-client/src/client/sequencer-client.ts index dae754f04a4..5250d962aca 100644 --- a/yarn-project/sequencer-client/src/client/sequencer-client.ts +++ b/yarn-project/sequencer-client/src/client/sequencer-client.ts @@ -2,6 +2,7 @@ import { type L1ToL2MessageSource, type L2BlockSource } from '@aztec/circuit-typ import { type BlockProver } from '@aztec/circuit-types/interfaces'; import { type P2P } from '@aztec/p2p'; import { PublicProcessorFactory, type SimulationProvider } from '@aztec/simulator'; +import { type TelemetryClient } from '@aztec/telemetry-client'; import { type ContractDataSource } from '@aztec/types/contracts'; import { type WorldStateSynchronizer } from '@aztec/world-state'; @@ -38,12 +39,18 @@ export class SequencerClient { l1ToL2MessageSource: L1ToL2MessageSource, prover: BlockProver, simulationProvider: SimulationProvider, + telemetryClient: TelemetryClient, ) { const publisher = getL1Publisher(config); const globalsBuilder = getGlobalVariableBuilder(config); const merkleTreeDb = worldStateSynchronizer.getLatest(); - const publicProcessorFactory = new PublicProcessorFactory(merkleTreeDb, contractDataSource, simulationProvider); + const publicProcessorFactory = new PublicProcessorFactory( + merkleTreeDb, + contractDataSource, + simulationProvider, + telemetryClient, + ); const sequencer = new Sequencer( publisher, @@ -55,6 +62,7 @@ export class SequencerClient { l1ToL2MessageSource, publicProcessorFactory, new TxValidatorFactory(merkleTreeDb, contractDataSource, !!config.enforceFees), + telemetryClient, config, ); diff --git a/yarn-project/sequencer-client/src/sequencer/sequencer.test.ts b/yarn-project/sequencer-client/src/sequencer/sequencer.test.ts index 4292e85e838..a0bdf943af3 100644 --- a/yarn-project/sequencer-client/src/sequencer/sequencer.test.ts +++ b/yarn-project/sequencer-client/src/sequencer/sequencer.test.ts @@ -27,6 +27,7 @@ import { randomBytes } from '@aztec/foundation/crypto'; import { type Writeable } from '@aztec/foundation/types'; import { type P2P, P2PClientState } from '@aztec/p2p'; import { type PublicProcessor, type PublicProcessorFactory } from '@aztec/simulator'; +import { NoopTelemetryClient } from '@aztec/telemetry-client/noop'; import { type ContractDataSource } from '@aztec/types/contracts'; import { type MerkleTreeOperations, WorldStateRunningState, type WorldStateSynchronizer } from '@aztec/world-state'; @@ -115,6 +116,7 @@ describe('sequencer', () => { l1ToL2MessageSource, publicProcessorFactory, new TxValidatorFactory(merkleTreeOps, contractSource, false), + new NoopTelemetryClient(), ); }); diff --git a/yarn-project/sequencer-client/src/sequencer/sequencer.ts b/yarn-project/sequencer-client/src/sequencer/sequencer.ts index aaa1831c190..670c63ae897 100644 --- a/yarn-project/sequencer-client/src/sequencer/sequencer.ts +++ b/yarn-project/sequencer-client/src/sequencer/sequencer.ts @@ -13,13 +13,14 @@ import { PROVING_STATUS, } from '@aztec/circuit-types/interfaces'; import { type L2BlockBuiltStats } from '@aztec/circuit-types/stats'; -import { AztecAddress, EthAddress, type Proof } from '@aztec/circuits.js'; +import { AztecAddress, EthAddress, type GlobalVariables, type Header, type Proof } from '@aztec/circuits.js'; import { Fr } from '@aztec/foundation/fields'; import { createDebugLogger } from '@aztec/foundation/log'; import { RunningPromise } from '@aztec/foundation/running-promise'; import { Timer, elapsed } from '@aztec/foundation/timer'; import { type P2P } from '@aztec/p2p'; import { type PublicProcessorFactory } from '@aztec/simulator'; +import { Attributes, type TelemetryClient, type Tracer, trackSpan } from '@aztec/telemetry-client'; import { type WorldStateStatus, type WorldStateSynchronizer } from '@aztec/world-state'; import { type GlobalVariableBuilder } from '../global_variable_builder/global_builder.js'; @@ -50,6 +51,8 @@ export class Sequencer { private allowedInTeardown: AllowedElement[] = []; private maxBlockSizeInBytes: number = 1024 * 1024; + public readonly tracer: Tracer; + constructor( private publisher: L1Publisher, private globalsBuilder: GlobalVariableBuilder, @@ -60,10 +63,12 @@ export class Sequencer { private l1ToL2MessageSource: L1ToL2MessageSource, private publicProcessorFactory: PublicProcessorFactory, private txValidatorFactory: TxValidatorFactory, + telemetry: TelemetryClient, config: SequencerConfig = {}, private log = createDebugLogger('aztec:sequencer'), ) { this.updateConfig(config); + this.tracer = telemetry.getTracer('sequencer'); this.log.verbose(`Initialized sequencer with ${this.minTxsPerBLock}-${this.maxTxsPerBlock} txs per block.`); } @@ -174,7 +179,6 @@ export class Sequencer { return; } - const workTimer = new Timer(); this.state = SequencerState.WAITING_FOR_TXS; // Get txs to build the new block @@ -184,19 +188,6 @@ export class Sequencer { } this.log.debug(`Retrieved ${pendingTxs.length} txs from P2P pool`); - /** - * We'll call this function before running expensive operations to avoid wasted work. - */ - const assertBlockHeight = async () => { - const currentBlockNumber = await this.l2BlockSource.getBlockNumber(); - if (currentBlockNumber + 1 !== newBlockNumber) { - throw new Error('New block was emitted while building block'); - } - if (!(await this.publisher.isItMyTurnToSubmit(newBlockNumber))) { - throw new Error(`Not this sequencer turn to submit block`); - } - }; - const newGlobalVariables = await this.globalsBuilder.buildGlobalVariables( new Fr(newBlockNumber), this._coinbase, @@ -220,72 +211,7 @@ export class Sequencer { return; } - this.log.info(`Building block ${newBlockNumber} with ${validTxs.length} transactions`); - this.state = SequencerState.CREATING_BLOCK; - - // Get l1 to l2 messages from the contract - this.log.debug('Requesting L1 to L2 messages from contract'); - const l1ToL2Messages = await this.l1ToL2MessageSource.getL1ToL2Messages(BigInt(newBlockNumber)); - this.log.verbose(`Retrieved ${l1ToL2Messages.length} L1 to L2 messages for block ${newBlockNumber}`); - - // We create a fresh processor each time to reset any cached state (eg storage writes) - const processor = await this.publicProcessorFactory.create(historicalHeader, newGlobalVariables); - - const blockBuildingTimer = new Timer(); - - // We must initialise the block to be a power of 2 in size - const numRealTxs = validTxs.length; - const pow2 = Math.log2(numRealTxs); - // TODO turn this back into a Math.ceil once we can pad blocks to the next-power-of-2 with empty txs - const totalTxs = 2 ** Math.ceil(pow2); - const blockSize = Math.max(2, totalTxs); - const blockTicket = await this.prover.startNewBlock(blockSize, newGlobalVariables, l1ToL2Messages); - - const [publicProcessorDuration, [processedTxs, failedTxs]] = await elapsed(() => - processor.process(validTxs, blockSize, this.prover, this.txValidatorFactory.validatorForProcessedTxs()), - ); - if (failedTxs.length > 0) { - const failedTxData = failedTxs.map(fail => fail.tx); - this.log.debug(`Dropping failed txs ${Tx.getHashes(failedTxData).join(', ')}`); - await this.p2pClient.deleteTxs(Tx.getHashes(failedTxData)); - } - - if (processedTxs.length === 0) { - this.log.verbose('No txs processed correctly to build block. Exiting'); - this.prover.cancelBlock(); - return; - } - - await assertBlockHeight(); - - // All real transactions have been added, set the block as full and complete the proving. - await this.prover.setBlockCompleted(); - - // Here we are now waiting for the block to be proven. - // TODO(@PhilWindle) We should probably periodically check for things like another - // block being published before ours instead of just waiting on our block - const result = await blockTicket.provingPromise; - if (result.status === PROVING_STATUS.FAILURE) { - throw new Error(`Block proving failed, reason: ${result.reason}`); - } - - await assertBlockHeight(); - - // Block is proven, now finalise and publish! - const { block, aggregationObject, proof } = await this.prover.finaliseBlock(); - - await assertBlockHeight(); - - this.log.verbose(`Assembled block ${block.number}`, { - eventName: 'l2-block-built', - duration: workTimer.ms(), - publicProcessDuration: publicProcessorDuration, - rollupCircuitsDuration: blockBuildingTimer.ms(), - ...block.getStats(), - } satisfies L2BlockBuiltStats); - - await this.publishL2Block(block, aggregationObject, proof); - this.log.info(`Submitted rollup block ${block.number} with ${processedTxs.length} transactions`); + await this.buildBlockAndPublish(validTxs, newGlobalVariables, historicalHeader); } catch (err) { if (BlockProofError.isBlockProofError(err)) { const txHashes = err.txHashes.filter(h => !h.isZero()); @@ -299,10 +225,104 @@ export class Sequencer { } } + @trackSpan('Sequencer.buildBlockAndPublish', (validTxs, newGlobalVariables, historicalHeader) => ({ + [Attributes.BLOCK_NUMBER]: newGlobalVariables.blockNumber.toNumber(), + [Attributes.BLOCK_PARENT]: historicalHeader?.globalVariables.blockNumber.toNumber(), + [Attributes.BLOCK_CANDIDATE_TXS_COUNT]: validTxs.length, + })) + private async buildBlockAndPublish( + validTxs: Tx[], + newGlobalVariables: GlobalVariables, + historicalHeader: Header | undefined, + ): Promise { + const workTimer = new Timer(); + this.state = SequencerState.CREATING_BLOCK; + this.log.info(`Building block ${newGlobalVariables.blockNumber.toNumber()} with ${validTxs.length} transactions`); + + const assertBlockHeight = async () => { + const currentBlockNumber = await this.l2BlockSource.getBlockNumber(); + if (currentBlockNumber + 1 !== newGlobalVariables.blockNumber.toNumber()) { + throw new Error('New block was emitted while building block'); + } + if (!(await this.publisher.isItMyTurnToSubmit(newGlobalVariables.blockNumber.toNumber()))) { + throw new Error(`Not this sequencer turn to submit block`); + } + }; + + // Get l1 to l2 messages from the contract + this.log.debug('Requesting L1 to L2 messages from contract'); + const l1ToL2Messages = await this.l1ToL2MessageSource.getL1ToL2Messages(newGlobalVariables.blockNumber.toBigInt()); + this.log.verbose( + `Retrieved ${l1ToL2Messages.length} L1 to L2 messages for block ${newGlobalVariables.blockNumber.toNumber()}`, + ); + + // We create a fresh processor each time to reset any cached state (eg storage writes) + const processor = await this.publicProcessorFactory.create(historicalHeader, newGlobalVariables); + + const numRealTxs = validTxs.length; + const pow2 = Math.log2(numRealTxs); + // TODO turn this back into a Math.ceil once we can pad blocks to the next-power-of-2 with empty txs + const totalTxs = 2 ** Math.ceil(pow2); + const blockSize = Math.max(2, totalTxs); + + const blockBuildingTimer = new Timer(); + const blockTicket = await this.prover.startNewBlock(blockSize, newGlobalVariables, l1ToL2Messages); + + const [publicProcessorDuration, [processedTxs, failedTxs]] = await elapsed(() => + processor.process(validTxs, blockSize, this.prover, this.txValidatorFactory.validatorForProcessedTxs()), + ); + if (failedTxs.length > 0) { + const failedTxData = failedTxs.map(fail => fail.tx); + this.log.debug(`Dropping failed txs ${Tx.getHashes(failedTxData).join(', ')}`); + await this.p2pClient.deleteTxs(Tx.getHashes(failedTxData)); + } + + if (processedTxs.length === 0) { + this.log.verbose('No txs processed correctly to build block. Exiting'); + this.prover.cancelBlock(); + return; + } + + await assertBlockHeight(); + + // All real transactions have been added, set the block as full and complete the proving. + await this.prover.setBlockCompleted(); + + // Here we are now waiting for the block to be proven. + // TODO(@PhilWindle) We should probably periodically check for things like another + // block being published before ours instead of just waiting on our block + const result = await blockTicket.provingPromise; + if (result.status === PROVING_STATUS.FAILURE) { + throw new Error(`Block proving failed, reason: ${result.reason}`); + } + + await assertBlockHeight(); + + // Block is proven, now finalise and publish! + const { block, aggregationObject, proof } = await this.prover.finaliseBlock(); + + await assertBlockHeight(); + + this.log.verbose(`Assembled block ${block.number}`, { + eventName: 'l2-block-built', + duration: workTimer.ms(), + publicProcessDuration: publicProcessorDuration, + rollupCircuitsDuration: blockBuildingTimer.ms(), + ...block.getStats(), + } satisfies L2BlockBuiltStats); + + await this.publishL2Block(block, aggregationObject, proof); + this.log.info(`Submitted rollup block ${block.number} with ${processedTxs.length} transactions`); + } + /** * Publishes the L2Block to the rollup contract. * @param block - The L2Block to be published. */ + @trackSpan('Sequencer.publishL2Block', block => ({ + [Attributes.BLOCK_NUMBER]: block.number, + [Attributes.BLOCK_TXS_COUNT]: block.body.txEffects.length, + })) protected async publishL2Block(block: L2Block, aggregationObject: Fr[], proof: Proof) { // Publishes new block to the network and awaits the tx to be mined this.state = SequencerState.PUBLISHING_BLOCK; diff --git a/yarn-project/sequencer-client/tsconfig.json b/yarn-project/sequencer-client/tsconfig.json index 4ec1ceda867..b4140a80da4 100644 --- a/yarn-project/sequencer-client/tsconfig.json +++ b/yarn-project/sequencer-client/tsconfig.json @@ -39,6 +39,9 @@ { "path": "../simulator" }, + { + "path": "../telemetry-client" + }, { "path": "../types" }, diff --git a/yarn-project/simulator/package.json b/yarn-project/simulator/package.json index f99e74aa4b2..42bb7c5fb4d 100644 --- a/yarn-project/simulator/package.json +++ b/yarn-project/simulator/package.json @@ -53,6 +53,7 @@ "@aztec/foundation": "workspace:^", "@aztec/noir-protocol-circuits-types": "workspace:^", "@aztec/protocol-contracts": "workspace:^", + "@aztec/telemetry-client": "workspace:^", "@aztec/types": "workspace:^", "@aztec/world-state": "workspace:^", "@noir-lang/acvm_js": "portal:../../noir/packages/acvm_js", diff --git a/yarn-project/simulator/src/public/public_processor.test.ts b/yarn-project/simulator/src/public/public_processor.test.ts index 4c2e84eeed1..362fd788e1d 100644 --- a/yarn-project/simulator/src/public/public_processor.test.ts +++ b/yarn-project/simulator/src/public/public_processor.test.ts @@ -45,6 +45,7 @@ import { WASMSimulator, computeFeePayerBalanceLeafSlot, } from '@aztec/simulator'; +import { NoopTelemetryClient } from '@aztec/telemetry-client/noop'; import { type MerkleTreeOperations, type TreeInfo } from '@aztec/world-state'; import { jest } from '@jest/globals'; @@ -95,6 +96,7 @@ describe('public_processor', () => { Header.empty(), publicContractsDB, publicWorldStateDB, + new NoopTelemetryClient(), ); }); @@ -219,6 +221,7 @@ describe('public_processor', () => { header, publicContractsDB, publicWorldStateDB, + new NoopTelemetryClient(), ); }); diff --git a/yarn-project/simulator/src/public/public_processor.ts b/yarn-project/simulator/src/public/public_processor.ts index f5f5bd3749c..68459bcee35 100644 --- a/yarn-project/simulator/src/public/public_processor.ts +++ b/yarn-project/simulator/src/public/public_processor.ts @@ -30,6 +30,7 @@ import { computeFeePayerBalanceLeafSlot, computeFeePayerBalanceStorageSlot, } from '@aztec/simulator'; +import { Attributes, type TelemetryClient, type Tracer, trackSpan } from '@aztec/telemetry-client'; import { type ContractDataSource } from '@aztec/types/contracts'; import { type MerkleTreeOperations } from '@aztec/world-state'; @@ -47,6 +48,7 @@ export class PublicProcessorFactory { private merkleTree: MerkleTreeOperations, private contractDataSource: ContractDataSource, private simulator: SimulationProvider, + private telemetryClient: TelemetryClient, ) {} /** @@ -74,6 +76,7 @@ export class PublicProcessorFactory { historicalHeader, publicContractsDB, worldStatePublicDB, + this.telemetryClient, ); } } @@ -83,6 +86,7 @@ export class PublicProcessorFactory { * any public function calls in them. Txs with private calls only are unaffected. */ export class PublicProcessor { + public readonly tracer: Tracer; constructor( protected db: MerkleTreeOperations, protected publicExecutor: PublicExecutor, @@ -91,9 +95,11 @@ export class PublicProcessor { protected historicalHeader: Header, protected publicContractsDB: ContractsDataSourcePublicDB, protected publicStateDB: PublicStateDB, - + telemetryClient: TelemetryClient, private log = createDebugLogger('aztec:sequencer:public-processor'), - ) {} + ) { + this.tracer = telemetryClient.getTracer('@aztec/simulator:PublicProcessor'); + } /** * Run each tx through the public circuit and the public kernel circuit if needed. @@ -208,6 +214,9 @@ export class PublicProcessor { return finalPublicDataUpdateRequests; } + @trackSpan('PublicProcessor.processTxWithPublicCalls', tx => ({ + [Attributes.TX_HASH]: tx.getTxHash().toString(), + })) private async processTxWithPublicCalls(tx: Tx): Promise<[ProcessedTx, NestedProcessReturnValues[]]> { let returnValues: NestedProcessReturnValues[] = []; const publicProvingRequests: PublicProvingRequest[] = []; diff --git a/yarn-project/simulator/tsconfig.json b/yarn-project/simulator/tsconfig.json index effb5a7151c..60a3f7e62de 100644 --- a/yarn-project/simulator/tsconfig.json +++ b/yarn-project/simulator/tsconfig.json @@ -21,6 +21,9 @@ { "path": "../protocol-contracts" }, + { + "path": "../telemetry-client" + }, { "path": "../types" }, diff --git a/yarn-project/telemetry-client/package.json b/yarn-project/telemetry-client/package.json index 4c07997cc50..84ee023757e 100644 --- a/yarn-project/telemetry-client/package.json +++ b/yarn-project/telemetry-client/package.json @@ -29,9 +29,11 @@ "@aztec/foundation": "workspace:^", "@opentelemetry/api": "^1.9.0", "@opentelemetry/exporter-metrics-otlp-http": "^0.52.0", + "@opentelemetry/exporter-trace-otlp-http": "^0.52.0", "@opentelemetry/host-metrics": "^0.35.2", "@opentelemetry/resources": "^1.25.0", "@opentelemetry/sdk-metrics": "^1.25.0", + "@opentelemetry/sdk-trace-node": "^1.25.0", "@opentelemetry/semantic-conventions": "^1.25.0" }, "devDependencies": { diff --git a/yarn-project/telemetry-client/src/attributes.ts b/yarn-project/telemetry-client/src/attributes.ts index bf39983a967..d4df0253436 100644 --- a/yarn-project/telemetry-client/src/attributes.ts +++ b/yarn-project/telemetry-client/src/attributes.ts @@ -34,3 +34,16 @@ export const APP_CIRCUIT_NAME = 'aztec.circuit.app_circuit_name'; * The type of app circuit being run: server or client */ export const APP_CIRCUIT_TYPE = 'aztec.circuit.app_circuit_type'; + +/** The block number */ +export const BLOCK_NUMBER = 'aztec.block.number'; +/** The parent's block number */ +export const BLOCK_PARENT = 'aztec.block.parent'; +/** How many txs are being processed to build this block */ +export const BLOCK_CANDIDATE_TXS_COUNT = 'aztec.block.candidate_txs_count'; +/** How many actual txs were included in this block */ +export const BLOCK_TXS_COUNT = 'aztec.block.txs_count'; +/** The block size (power of 2) */ +export const BLOCK_SIZE = 'aztec.block.size'; +/** The tx hash */ +export const TX_HASH = 'aztec.tx.hash'; diff --git a/yarn-project/telemetry-client/src/noop.ts b/yarn-project/telemetry-client/src/noop.ts index 2ef3c8344e1..e4ab8162ffb 100644 --- a/yarn-project/telemetry-client/src/noop.ts +++ b/yarn-project/telemetry-client/src/noop.ts @@ -1,4 +1,4 @@ -import { type Meter, createNoopMeter } from '@opentelemetry/api'; +import { type Meter, type Span, type SpanContext, type Tracer, createNoopMeter } from '@opentelemetry/api'; import { type TelemetryClient } from './telemetry.js'; @@ -7,7 +7,77 @@ export class NoopTelemetryClient implements TelemetryClient { return createNoopMeter(); } + getTracer(): Tracer { + return new NoopTracer(); + } + stop(): Promise { return Promise.resolve(); } } + +// @opentelemetry/api internally uses NoopTracer and NoopSpan but they're not exported +// make our own versions +// https://github.com/open-telemetry/opentelemetry-js/issues/4518#issuecomment-2179405444 +class NoopTracer implements Tracer { + startSpan(): Span { + return new NoopSpan(); + } + + startActiveSpan any>(_name: string, ...args: (unknown | F)[]): ReturnType { + // there are three different signatures for startActiveSpan, grab the function, we don't care about the rest + const fn = args.find(arg => typeof arg === 'function') as F; + return fn(new NoopSpan()); + } +} + +class NoopSpan implements Span { + private recording: boolean = true; + addEvent(): this { + return this; + } + + addLink(): this { + return this; + } + + addLinks(): this { + return this; + } + + end(): void { + this.recording = false; + } + + isRecording(): boolean { + return this.recording; + } + + recordException(): void { + return; + } + + setAttribute(): this { + return this; + } + + setAttributes(): this { + return this; + } + + setStatus(): this { + return this; + } + + spanContext(): SpanContext { + return { + spanId: '', + traceId: '', + traceFlags: 0, + }; + } + + updateName(): this { + return this; + } +} diff --git a/yarn-project/telemetry-client/src/otel.ts b/yarn-project/telemetry-client/src/otel.ts index 002b70ca9c9..6ed1ce01218 100644 --- a/yarn-project/telemetry-client/src/otel.ts +++ b/yarn-project/telemetry-client/src/otel.ts @@ -1,20 +1,30 @@ -import { type Meter } from '@opentelemetry/api'; +import { type Meter, type Tracer, type TracerProvider } from '@opentelemetry/api'; import { OTLPMetricExporter } from '@opentelemetry/exporter-metrics-otlp-http'; +import { OTLPTraceExporter } from '@opentelemetry/exporter-trace-otlp-http'; import { HostMetrics } from '@opentelemetry/host-metrics'; import { Resource } from '@opentelemetry/resources'; import { MeterProvider, PeriodicExportingMetricReader } from '@opentelemetry/sdk-metrics'; +import { BatchSpanProcessor, NodeTracerProvider } from '@opentelemetry/sdk-trace-node'; import { SEMRESATTRS_SERVICE_NAME, SEMRESATTRS_SERVICE_VERSION } from '@opentelemetry/semantic-conventions'; import { type TelemetryClient } from './telemetry.js'; export class OpenTelemetryClient implements TelemetryClient { hostMetrics: HostMetrics | undefined; - protected constructor(private resource: Resource, private meterProvider: MeterProvider) {} + protected constructor( + private resource: Resource, + private meterProvider: MeterProvider, + private traceProvider: TracerProvider, + ) {} getMeter(name: string): Meter { return this.meterProvider.getMeter(name, this.resource.attributes[SEMRESATTRS_SERVICE_VERSION] as string); } + getTracer(name: string): Tracer { + return this.traceProvider.getTracer(name, this.resource.attributes[SEMRESATTRS_SERVICE_VERSION] as string); + } + public start() { this.hostMetrics = new HostMetrics({ name: this.resource.attributes[SEMRESATTRS_SERVICE_NAME] as string, @@ -34,6 +44,14 @@ export class OpenTelemetryClient implements TelemetryClient { [SEMRESATTRS_SERVICE_VERSION]: version, }); + const tracerProvider = new NodeTracerProvider({ + resource, + }); + tracerProvider.addSpanProcessor( + new BatchSpanProcessor(new OTLPTraceExporter({ url: new URL('/v1/traces', collectorBaseUrl).href })), + ); + tracerProvider.register(); + const meterProvider = new MeterProvider({ resource, readers: [ @@ -45,7 +63,7 @@ export class OpenTelemetryClient implements TelemetryClient { ], }); - const service = new OpenTelemetryClient(resource, meterProvider); + const service = new OpenTelemetryClient(resource, meterProvider, tracerProvider); service.start(); return service; diff --git a/yarn-project/telemetry-client/src/telemetry.ts b/yarn-project/telemetry-client/src/telemetry.ts index 0aa7dedcbe1..2aba308ddee 100644 --- a/yarn-project/telemetry-client/src/telemetry.ts +++ b/yarn-project/telemetry-client/src/telemetry.ts @@ -4,12 +4,15 @@ import { type Gauge as OtelGauge, type Histogram as OtelHistogram, type UpDownCounter as OtelUpDownCounter, + type Span, + SpanStatusCode, + Tracer, } from '@opentelemetry/api'; import * as Attributes from './attributes.js'; import * as Metrics from './metrics.js'; -export { ValueType } from '@opentelemetry/api'; +export { ValueType, Span } from '@opentelemetry/api'; type ValuesOf = T extends Record ? U : never; @@ -25,6 +28,8 @@ export type Gauge = OtelGauge; export type Histogram = OtelHistogram; export type UpDownCounter = OtelUpDownCounter; +export { Tracer }; + // INTERNAL NOTE: this interface is the same as opentelemetry's Meter, but with proper types /** * A meter that provides instruments for recording metrics. @@ -62,8 +67,98 @@ export interface TelemetryClient { */ getMeter(name: string): Meter; + /** + * Creates a new tracer + * @param name - The name of the tracer. + */ + getTracer(name: string): Tracer; + /** * Stops the telemetry client. */ stop(): Promise; } + +/** Objects that adhere to this interface can use @trackSpan */ +export interface Traceable { + tracer: Tracer; +} + +type SpanDecorator any> = ( + originalMethod: F, + context: ClassMethodDecoratorContext, +) => F; + +/** + * Starts a new span whenever the decorated method is called. + * @param spanName - The name of the span to create. Can be a string or a function that returns a string. + * @param attributes - Initial attributes to set on the span. If a function is provided, it will be called with the arguments of the method. + * @param extraAttributes - Extra attributes to set on the span after the method is called. Will be called with the return value of the method. Note: if the function throws then this will not be called. + * @returns A decorator that wraps the method in a span. + */ +export function trackSpan any>( + spanName: string | ((this: T, ...args: Parameters) => string), + attributes?: Attributes | ((this: T, ...args: Parameters) => Attributes), + extraAttributes?: (this: T, returnValue: Awaited>) => Attributes, +): SpanDecorator { + return (originalMethod: F, _context: ClassMethodDecoratorContext) => { + return function replacementMethod(this: T, ...args: Parameters): Promise>> { + const name = typeof spanName === 'function' ? spanName.call(this, ...args) : spanName; + const currentAttrs = typeof attributes === 'function' ? attributes.call(this, ...args) : attributes; + return this.tracer.startActiveSpan(name, async (span: Span) => { + for (const [key, value] of Object.entries(currentAttrs ?? {})) { + span.setAttribute(key, value); + } + + try { + const res = await originalMethod.call(this, ...args); + const extraAttrs = extraAttributes?.call(this, res); + for (const [key, value] of Object.entries(extraAttrs ?? {})) { + span.setAttribute(key, value); + } + + return res; + } catch (err) { + span.setStatus({ + code: SpanStatusCode.ERROR, + message: String(err), + }); + throw err; + } finally { + span.end(); + } + }); + } as F; + }; +} + +/** + * Runs an event callback in a span. The span is started immediately and completes once the callback finishes running. + * @param tracer - The tracer instance to use + * @param spanName - The name of the span to create + * @param attributes - Initial attributes to set on the span + * @param callback - The callback to wrap in a span + * @returns - A new function that wraps the callback in a span + */ +export function wrapCallbackInSpan any>( + tracer: Tracer, + spanName: string, + attributes: Attributes, + callback: F, +): F { + const span = tracer.startSpan(spanName, { attributes }); + return (async (...args: Parameters) => { + try { + const res = await callback(...args); + return res; + } catch (err) { + span.setStatus({ + code: SpanStatusCode.ERROR, + message: String(err), + }); + throw err; + } finally { + span.end(); + } + }) as F; +} diff --git a/yarn-project/yarn.lock b/yarn-project/yarn.lock index 2e13e78b916..1b9ed2231ac 100644 --- a/yarn-project/yarn.lock +++ b/yarn-project/yarn.lock @@ -877,6 +877,7 @@ __metadata: "@aztec/p2p": "workspace:^" "@aztec/protocol-contracts": "workspace:^" "@aztec/simulator": "workspace:^" + "@aztec/telemetry-client": "workspace:^" "@aztec/types": "workspace:^" "@aztec/world-state": "workspace:^" "@jest/globals": ^29.5.0 @@ -916,6 +917,7 @@ __metadata: "@aztec/noir-contracts.js": "workspace:^" "@aztec/noir-protocol-circuits-types": "workspace:^" "@aztec/protocol-contracts": "workspace:^" + "@aztec/telemetry-client": "workspace:^" "@aztec/types": "workspace:^" "@aztec/world-state": "workspace:^" "@jest/globals": ^29.5.0 @@ -946,9 +948,11 @@ __metadata: "@jest/globals": ^29.5.0 "@opentelemetry/api": ^1.9.0 "@opentelemetry/exporter-metrics-otlp-http": ^0.52.0 + "@opentelemetry/exporter-trace-otlp-http": ^0.52.0 "@opentelemetry/host-metrics": ^0.35.2 "@opentelemetry/resources": ^1.25.0 "@opentelemetry/sdk-metrics": ^1.25.0 + "@opentelemetry/sdk-trace-node": ^1.25.0 "@opentelemetry/semantic-conventions": ^1.25.0 "@types/jest": ^29.5.0 jest: ^29.5.0 @@ -3067,6 +3071,15 @@ __metadata: languageName: node linkType: hard +"@opentelemetry/context-async-hooks@npm:1.25.0": + version: 1.25.0 + resolution: "@opentelemetry/context-async-hooks@npm:1.25.0" + peerDependencies: + "@opentelemetry/api": ">=1.0.0 <1.10.0" + checksum: f50f6ef621b6cfaa1d0919e4470b7c8326371beaf6be9a635c6f3221677bf9f5429a81a29b5518a41d3c002e35d4a89cb748ae61f650d61aa2ae3cbe123c0301 + languageName: node + linkType: hard + "@opentelemetry/core@npm:1.25.0": version: 1.25.0 resolution: "@opentelemetry/core@npm:1.25.0" @@ -3093,6 +3106,21 @@ __metadata: languageName: node linkType: hard +"@opentelemetry/exporter-trace-otlp-http@npm:^0.52.0": + version: 0.52.0 + resolution: "@opentelemetry/exporter-trace-otlp-http@npm:0.52.0" + dependencies: + "@opentelemetry/core": 1.25.0 + "@opentelemetry/otlp-exporter-base": 0.52.0 + "@opentelemetry/otlp-transformer": 0.52.0 + "@opentelemetry/resources": 1.25.0 + "@opentelemetry/sdk-trace-base": 1.25.0 + peerDependencies: + "@opentelemetry/api": ^1.0.0 + checksum: bed18523289c579b8108b1c3fcb2b74361bed2d7f3016270feb080a047fa422fc9dfb0678ff1b726cb1e0fa9413cead5824e7f97d1d781467aa983a87fe1ee93 + languageName: node + linkType: hard + "@opentelemetry/host-metrics@npm:^0.35.2": version: 0.35.2 resolution: "@opentelemetry/host-metrics@npm:0.35.2" @@ -3134,6 +3162,28 @@ __metadata: languageName: node linkType: hard +"@opentelemetry/propagator-b3@npm:1.25.0": + version: 1.25.0 + resolution: "@opentelemetry/propagator-b3@npm:1.25.0" + dependencies: + "@opentelemetry/core": 1.25.0 + peerDependencies: + "@opentelemetry/api": ">=1.0.0 <1.10.0" + checksum: 5e8a0feec400ebb20644ee217f904ec8894ccad49b753e80c5e131a4f3390504ca3fd17de58ff546313dedc6498dbd198ff83acc3d8084a205e1d901cfc0bb2d + languageName: node + linkType: hard + +"@opentelemetry/propagator-jaeger@npm:1.25.0": + version: 1.25.0 + resolution: "@opentelemetry/propagator-jaeger@npm:1.25.0" + dependencies: + "@opentelemetry/core": 1.25.0 + peerDependencies: + "@opentelemetry/api": ">=1.0.0 <1.10.0" + checksum: c652b4285e254041654a5153649f822b8e2eaa526b67e0a8c56c4eb173d9d0d0efa41ffed3f7dcdd1c2c2b85365cd05e001ee145e8701e4af9d7eef79488ca18 + languageName: node + linkType: hard + "@opentelemetry/resources@npm:1.25.0, @opentelemetry/resources@npm:^1.25.0": version: 1.25.0 resolution: "@opentelemetry/resources@npm:1.25.0" @@ -3185,6 +3235,22 @@ __metadata: languageName: node linkType: hard +"@opentelemetry/sdk-trace-node@npm:^1.25.0": + version: 1.25.0 + resolution: "@opentelemetry/sdk-trace-node@npm:1.25.0" + dependencies: + "@opentelemetry/context-async-hooks": 1.25.0 + "@opentelemetry/core": 1.25.0 + "@opentelemetry/propagator-b3": 1.25.0 + "@opentelemetry/propagator-jaeger": 1.25.0 + "@opentelemetry/sdk-trace-base": 1.25.0 + semver: ^7.5.2 + peerDependencies: + "@opentelemetry/api": ">=1.0.0 <1.10.0" + checksum: 22a0a61a6c092841ef4438f914edd259d3025078cc9331aaac340c624c2963aa6fdc4970ade5a0e6647c64e92e893ebde0b8ecdd021abac5358ea3c814a5c01c + languageName: node + linkType: hard + "@opentelemetry/semantic-conventions@npm:1.25.0, @opentelemetry/semantic-conventions@npm:^1.25.0": version: 1.25.0 resolution: "@opentelemetry/semantic-conventions@npm:1.25.0" @@ -12760,6 +12826,15 @@ __metadata: languageName: node linkType: hard +"semver@npm:^7.5.2": + version: 7.6.2 + resolution: "semver@npm:7.6.2" + bin: + semver: bin/semver.js + checksum: 40f6a95101e8d854357a644da1b8dd9d93ce786d5c6a77227bc69dbb17bea83d0d1d1d7c4cd5920a6df909f48e8bd8a5909869535007f90278289f2451d0292d + languageName: node + linkType: hard + "serialize-javascript@npm:^6.0.1": version: 6.0.2 resolution: "serialize-javascript@npm:6.0.2" From 5198c4d64a6181abf9556ba15d7f66c4447696c9 Mon Sep 17 00:00:00 2001 From: Alex Gherghisan Date: Tue, 25 Jun 2024 10:16:18 +0000 Subject: [PATCH 2/4] refactor: otel -> tel --- docker-compose.yml | 8 ++++---- .../aztec/src/cli/cmds/start_archiver.ts | 2 +- yarn-project/aztec/src/cli/cmds/start_node.ts | 2 +- yarn-project/aztec/src/cli/cmds/start_prover.ts | 2 +- .../end-to-end/src/fixtures/snapshot_manager.ts | 4 ++-- yarn-project/end-to-end/src/fixtures/utils.ts | 2 +- yarn-project/telemetry-client/src/start.ts | 16 ++++++++-------- 7 files changed, 18 insertions(+), 18 deletions(-) diff --git a/docker-compose.yml b/docker-compose.yml index 7c44b906b5c..b65e980c58d 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -61,7 +61,7 @@ services: P2P_ENABLED: true PEER_ID_PRIVATE_KEY: AZTEC_PORT: 8999 - OTEL_COLLECTOR_BASE_URL: ${OTEL_COLLECTOR_BASE_URL:-http://otel-collector:4318} + TEL_COLLECTOR_BASE_URL: ${TEL_COLLECTOR_BASE_URL:-http://otel-collector:4318} secrets: - ethereum-host - p2p-boot-node @@ -76,13 +76,13 @@ services: # if the stack is started with --profile metrics --profile node, give the collector a chance to start before the node i=0 max=3 - while ! curl --head --silent $$OTEL_COLLECTOR_BASE_URL > /dev/null; do + while ! curl --head --silent $$TEL_COLLECTOR_BASE_URL > /dev/null; do echo "OpenTelemetry collector not up. Retrying after 1s"; sleep 1; i=$$((i+1)); if [ $$i -eq $$max ]; then - echo "OpenTelemetry collector at $$OTEL_COLLECTOR_BASE_URL not up after $${max}s. Running without metrics"; - unset OTEL_COLLECTOR_BASE_URL; + echo "OpenTelemetry collector at $$TEL_COLLECTOR_BASE_URL not up after $${max}s. Running without metrics"; + unset TEL_COLLECTOR_BASE_URL; break fi; done; diff --git a/yarn-project/aztec/src/cli/cmds/start_archiver.ts b/yarn-project/aztec/src/cli/cmds/start_archiver.ts index bf0df3808c2..ad2f296d958 100644 --- a/yarn-project/aztec/src/cli/cmds/start_archiver.ts +++ b/yarn-project/aztec/src/cli/cmds/start_archiver.ts @@ -34,7 +34,7 @@ export const startArchiver = async (options: any, signalHandlers: (() => Promise ); const archiverStore = new KVArchiverDataStore(store, archiverConfig.maxLogs); - const telemetry = createAndStartTelemetryClient(getTelemetryClientConfig(), 'aztec-archiver'); + const telemetry = createAndStartTelemetryClient(getTelemetryClientConfig()); const archiver = await Archiver.createAndSync(archiverConfig, archiverStore, telemetry, true); const archiverServer = createArchiverRpcServer(archiver); services.push({ archiver: archiverServer }); diff --git a/yarn-project/aztec/src/cli/cmds/start_node.ts b/yarn-project/aztec/src/cli/cmds/start_node.ts index c6ff3024794..6ef8e67fe4b 100644 --- a/yarn-project/aztec/src/cli/cmds/start_node.ts +++ b/yarn-project/aztec/src/cli/cmds/start_node.ts @@ -85,7 +85,7 @@ export const startNode = async ( } // Create and start Aztec Node. - const telemetryClient = createAndStartTelemetryClient(getTelemetryClientConfig(), 'aztec-node'); + const telemetryClient = createAndStartTelemetryClient(getTelemetryClientConfig()); const node = await createAztecNode(telemetryClient, nodeConfig); const nodeServer = createAztecNodeRpcServer(node); diff --git a/yarn-project/aztec/src/cli/cmds/start_prover.ts b/yarn-project/aztec/src/cli/cmds/start_prover.ts index f5e32e7e741..64fd693f9ed 100644 --- a/yarn-project/aztec/src/cli/cmds/start_prover.ts +++ b/yarn-project/aztec/src/cli/cmds/start_prover.ts @@ -34,7 +34,7 @@ export const startProver: ServiceStarter = async (options, signalHandlers, logge ? parseInt(proverOptions.proverAgentPollInterval, 10) : proverOptions.proverAgentPollInterval; - const telemetry = createAndStartTelemetryClient(getTelemetryClientConfig(), 'aztec-prover'); + const telemetry = createAndStartTelemetryClient(getTelemetryClientConfig()); let circuitProver: ServerCircuitProver; if (proverOptions.realProofs) { if (!proverOptions.acvmBinaryPath || !proverOptions.bbBinaryPath) { diff --git a/yarn-project/end-to-end/src/fixtures/snapshot_manager.ts b/yarn-project/end-to-end/src/fixtures/snapshot_manager.ts index 64c8bf24d93..fc985e66290 100644 --- a/yarn-project/end-to-end/src/fixtures/snapshot_manager.ts +++ b/yarn-project/end-to-end/src/fixtures/snapshot_manager.ts @@ -271,7 +271,7 @@ async function setupFromFresh(statePath: string | undefined, logger: Logger): Pr aztecNodeConfig.bbWorkingDirectory = bbConfig.bbWorkingDirectory; } - const telemetry = createAndStartTelemetryClient(getTelemetryConfig(), 'aztec-test'); + const telemetry = createAndStartTelemetryClient(getTelemetryConfig()); logger.verbose('Creating and synching an aztec node...'); const aztecNode = await AztecNodeService.createAndSync(aztecNodeConfig, telemetry); @@ -345,7 +345,7 @@ async function setupFromState(statePath: string, logger: Logger): Promise { await telemetry.stop(); diff --git a/yarn-project/telemetry-client/src/start.ts b/yarn-project/telemetry-client/src/start.ts index f811f9c6ec6..f83baa83400 100644 --- a/yarn-project/telemetry-client/src/start.ts +++ b/yarn-project/telemetry-client/src/start.ts @@ -4,24 +4,24 @@ import { type TelemetryClient } from './telemetry.js'; export interface TelemetryClientConfig { collectorBaseUrl?: URL; + serviceName: string; + serviceVersion: string; } -export function createAndStartTelemetryClient( - config: TelemetryClientConfig, - serviceName: string, - serviceVersion?: string, -): TelemetryClient { +export function createAndStartTelemetryClient(config: TelemetryClientConfig): TelemetryClient { if (config.collectorBaseUrl) { - return OpenTelemetryClient.createAndStart(serviceName, serviceVersion ?? '0.0.0', config.collectorBaseUrl); + return OpenTelemetryClient.createAndStart(config.serviceName, config.serviceVersion, config.collectorBaseUrl); } else { return new NoopTelemetryClient(); } } export function getConfigEnvVars(): TelemetryClientConfig { - const { OTEL_COLLECTOR_BASE_URL } = process.env; + const { TEL_COLLECTOR_BASE_URL, TEL_SERVICE_NAME = 'aztec', TEL_SERVICE_VERSION = '0.0.0' } = process.env; return { - collectorBaseUrl: OTEL_COLLECTOR_BASE_URL ? new URL(OTEL_COLLECTOR_BASE_URL) : undefined, + collectorBaseUrl: TEL_COLLECTOR_BASE_URL ? new URL(TEL_COLLECTOR_BASE_URL) : undefined, + serviceName: TEL_SERVICE_NAME, + serviceVersion: TEL_SERVICE_VERSION, }; } From 2eec95e8474cf160596ba1b41acb42ea36d627e9 Mon Sep 17 00:00:00 2001 From: Alex Gherghisan Date: Tue, 25 Jun 2024 16:15:49 +0100 Subject: [PATCH 3/4] Apply suggestions from code review Co-authored-by: Santiago Palladino --- .../bb-prover/src/prover/bb_prover.ts | 2 +- .../src/sequencer/sequencer.ts | 8 ++--- .../simulator/src/public/public_processor.ts | 2 +- .../telemetry-client/src/telemetry.ts | 30 ++++++++++++++----- 4 files changed, 27 insertions(+), 15 deletions(-) diff --git a/yarn-project/bb-prover/src/prover/bb_prover.ts b/yarn-project/bb-prover/src/prover/bb_prover.ts index 13f75a05cfd..7eed17fbe13 100644 --- a/yarn-project/bb-prover/src/prover/bb_prover.ts +++ b/yarn-project/bb-prover/src/prover/bb_prover.ts @@ -153,7 +153,7 @@ export class BBNativeRollupProver implements ServerCircuitProver { * @param inputs - Inputs to the circuit. * @returns The public inputs of the parity circuit. */ - @trackSpan('BBNativeRollupProver.getBaseParityProof', { [Attributes.PROTOCOL_CIRCUIT_NAME]: 'root-parity' }) + @trackSpan('BBNativeRollupProver.getRootParityProof', { [Attributes.PROTOCOL_CIRCUIT_NAME]: 'root-parity' }) public async getRootParityProof( inputs: RootParityInputs, ): Promise> { diff --git a/yarn-project/sequencer-client/src/sequencer/sequencer.ts b/yarn-project/sequencer-client/src/sequencer/sequencer.ts index 670c63ae897..0a441453780 100644 --- a/yarn-project/sequencer-client/src/sequencer/sequencer.ts +++ b/yarn-project/sequencer-client/src/sequencer/sequencer.ts @@ -68,7 +68,7 @@ export class Sequencer { private log = createDebugLogger('aztec:sequencer'), ) { this.updateConfig(config); - this.tracer = telemetry.getTracer('sequencer'); + this.tracer = telemetry.getTracer('Sequencer'); this.log.verbose(`Initialized sequencer with ${this.minTxsPerBLock}-${this.maxTxsPerBlock} txs per block.`); } @@ -225,10 +225,8 @@ export class Sequencer { } } - @trackSpan('Sequencer.buildBlockAndPublish', (validTxs, newGlobalVariables, historicalHeader) => ({ + @trackSpan('Sequencer.buildBlockAndPublish', (_validTxs, newGlobalVariables, _historicalHeader) => ({ [Attributes.BLOCK_NUMBER]: newGlobalVariables.blockNumber.toNumber(), - [Attributes.BLOCK_PARENT]: historicalHeader?.globalVariables.blockNumber.toNumber(), - [Attributes.BLOCK_CANDIDATE_TXS_COUNT]: validTxs.length, })) private async buildBlockAndPublish( validTxs: Tx[], @@ -261,7 +259,6 @@ export class Sequencer { const numRealTxs = validTxs.length; const pow2 = Math.log2(numRealTxs); - // TODO turn this back into a Math.ceil once we can pad blocks to the next-power-of-2 with empty txs const totalTxs = 2 ** Math.ceil(pow2); const blockSize = Math.max(2, totalTxs); @@ -321,7 +318,6 @@ export class Sequencer { */ @trackSpan('Sequencer.publishL2Block', block => ({ [Attributes.BLOCK_NUMBER]: block.number, - [Attributes.BLOCK_TXS_COUNT]: block.body.txEffects.length, })) protected async publishL2Block(block: L2Block, aggregationObject: Fr[], proof: Proof) { // Publishes new block to the network and awaits the tx to be mined diff --git a/yarn-project/simulator/src/public/public_processor.ts b/yarn-project/simulator/src/public/public_processor.ts index 68459bcee35..fa15414db15 100644 --- a/yarn-project/simulator/src/public/public_processor.ts +++ b/yarn-project/simulator/src/public/public_processor.ts @@ -98,7 +98,7 @@ export class PublicProcessor { telemetryClient: TelemetryClient, private log = createDebugLogger('aztec:sequencer:public-processor'), ) { - this.tracer = telemetryClient.getTracer('@aztec/simulator:PublicProcessor'); + this.tracer = telemetryClient.getTracer('PublicProcessor'); } /** diff --git a/yarn-project/telemetry-client/src/telemetry.ts b/yarn-project/telemetry-client/src/telemetry.ts index 2aba308ddee..bf56bf51af5 100644 --- a/yarn-project/telemetry-client/src/telemetry.ts +++ b/yarn-project/telemetry-client/src/telemetry.ts @@ -95,28 +95,39 @@ type SpanDecorator any> = ( * @param attributes - Initial attributes to set on the span. If a function is provided, it will be called with the arguments of the method. * @param extraAttributes - Extra attributes to set on the span after the method is called. Will be called with the return value of the method. Note: if the function throws then this will not be called. * @returns A decorator that wraps the method in a span. + * + * @privateRemarks + * This code looks complex but it's not that difficult: + * - decorators are functions that _replace_ a method with a different implementation + * - normal decorators can't take function arguments, but if we write a function that returns a decorator, we can pass arguments to that function + * + * The trackSpan function takes a span's name and some attributes and builds a decorator that wraps a method in a span with the given name and props + * The decorator can currently only be applied to methods on classes that have a `tracer` property. The compiler will enforce this. */ export function trackSpan any>( spanName: string | ((this: T, ...args: Parameters) => string), attributes?: Attributes | ((this: T, ...args: Parameters) => Attributes), extraAttributes?: (this: T, returnValue: Awaited>) => Attributes, ): SpanDecorator { + // the return value of trackSpan is a decorator return (originalMethod: F, _context: ClassMethodDecoratorContext) => { + // the return value of the decorator replaces the original method + // in this wrapper method we start a span, call the original method, and then end the span return function replacementMethod(this: T, ...args: Parameters): Promise>> { const name = typeof spanName === 'function' ? spanName.call(this, ...args) : spanName; const currentAttrs = typeof attributes === 'function' ? attributes.call(this, ...args) : attributes; + + // run originalMethod wrapped in an active span + // "active" means the span will be alive for the duration of the function execution + // and if any other spans are started during the execution of originalMethod, they will be children of this span + // behind the scenes this uses AsyncLocalStorage https://nodejs.org/dist/latest-v18.x/docs/api/async_context.html return this.tracer.startActiveSpan(name, async (span: Span) => { - for (const [key, value] of Object.entries(currentAttrs ?? {})) { - span.setAttribute(key, value); - } + span.setAttributes(currentAttrs ?? {}); try { const res = await originalMethod.call(this, ...args); const extraAttrs = extraAttributes?.call(this, res); - for (const [key, value] of Object.entries(extraAttrs ?? {})) { - span.setAttribute(key, value); - } - + span.setAttributes(extraAttrs ?? {}); return res; } catch (err) { span.setStatus({ @@ -134,10 +145,13 @@ export function trackSpan any /** * Runs an event callback in a span. The span is started immediately and completes once the callback finishes running. + * The span will have two events added: 'callbackStart' and 'callbackEnd' to mark the start and end of the callback. + * * @param tracer - The tracer instance to use * @param spanName - The name of the span to create * @param attributes - Initial attributes to set on the span * @param callback - The callback to wrap in a span + * * @returns - A new function that wraps the callback in a span */ export function wrapCallbackInSpan any>( @@ -149,6 +163,7 @@ export function wrapCallbackInSpan any>( const span = tracer.startSpan(spanName, { attributes }); return (async (...args: Parameters) => { try { + span.addEvent('callbackStart'); const res = await callback(...args); return res; } catch (err) { @@ -158,6 +173,7 @@ export function wrapCallbackInSpan any>( }); throw err; } finally { + span.addEvent('callbackEnd'); span.end(); } }) as F; From af3f2c4ffb783f5ea1438554052576b67a2adfff Mon Sep 17 00:00:00 2001 From: Alex Gherghisan Date: Tue, 25 Jun 2024 15:57:36 +0000 Subject: [PATCH 4/4] chore: enable decorators in swc/jest --- yarn-project/accounts/package.json | 10 +++++++++- yarn-project/archiver/package.json | 10 +++++++++- yarn-project/aztec-faucet/package.json | 10 +++++++++- yarn-project/aztec-node/package.json | 10 +++++++++- yarn-project/aztec.js/package.json | 10 +++++++++- yarn-project/aztec/package.json | 10 +++++++++- yarn-project/bb-prover/package.json | 10 +++++++++- yarn-project/builder/package.json | 10 +++++++++- yarn-project/circuit-types/package.json | 10 +++++++++- yarn-project/circuits.js/package.json | 10 +++++++++- yarn-project/cli/package.json | 10 +++++++++- yarn-project/end-to-end/package.json | 10 +++++++++- yarn-project/entrypoints/package.json | 10 +++++++++- yarn-project/ethereum/package.json | 10 +++++++++- yarn-project/foundation/package.json | 10 +++++++++- yarn-project/key-store/package.json | 10 +++++++++- yarn-project/kv-store/package.json | 10 +++++++++- yarn-project/merkle-tree/package.json | 10 +++++++++- yarn-project/noir-contracts.js/package.json | 10 +++++++++- .../noir-protocol-circuits-types/package.json | 10 +++++++++- yarn-project/p2p-bootstrap/package.json | 10 +++++++++- yarn-project/p2p/package.json | 10 +++++++++- yarn-project/package.common.json | 14 +++++++++++++- yarn-project/protocol-contracts/package.json | 10 +++++++++- yarn-project/prover-client/package.json | 10 +++++++++- yarn-project/pxe/package.json | 10 +++++++++- yarn-project/scripts/package.json | 10 +++++++++- yarn-project/sequencer-client/package.json | 10 +++++++++- yarn-project/simulator/package.json | 10 +++++++++- yarn-project/telemetry-client/package.json | 10 +++++++++- yarn-project/txe/package.json | 10 +++++++++- yarn-project/types/package.json | 10 +++++++++- yarn-project/world-state/package.json | 10 +++++++++- 33 files changed, 301 insertions(+), 33 deletions(-) diff --git a/yarn-project/accounts/package.json b/yarn-project/accounts/package.json index 2b3cde6cec1..90d2d36ab83 100644 --- a/yarn-project/accounts/package.json +++ b/yarn-project/accounts/package.json @@ -45,7 +45,15 @@ "rootDir": "./src", "transform": { "^.+\\.tsx?$": [ - "@swc/jest" + "@swc/jest", + { + "jsc": { + "parser": { + "syntax": "typescript", + "decorators": true + } + } + } ] }, "extensionsToTreatAsEsm": [ diff --git a/yarn-project/archiver/package.json b/yarn-project/archiver/package.json index 645d038c2f6..9514bf2b7e4 100644 --- a/yarn-project/archiver/package.json +++ b/yarn-project/archiver/package.json @@ -34,7 +34,15 @@ "workerThreads": true, "transform": { "^.+\\.tsx?$": [ - "@swc/jest" + "@swc/jest", + { + "jsc": { + "parser": { + "syntax": "typescript", + "decorators": true + } + } + } ] }, "extensionsToTreatAsEsm": [ diff --git a/yarn-project/aztec-faucet/package.json b/yarn-project/aztec-faucet/package.json index 957b2203b87..567a3afb5dc 100644 --- a/yarn-project/aztec-faucet/package.json +++ b/yarn-project/aztec-faucet/package.json @@ -31,7 +31,15 @@ "rootDir": "./src", "transform": { "^.+\\.tsx?$": [ - "@swc/jest" + "@swc/jest", + { + "jsc": { + "parser": { + "syntax": "typescript", + "decorators": true + } + } + } ] }, "extensionsToTreatAsEsm": [ diff --git a/yarn-project/aztec-node/package.json b/yarn-project/aztec-node/package.json index 2ef2eb39a5f..6189671d529 100644 --- a/yarn-project/aztec-node/package.json +++ b/yarn-project/aztec-node/package.json @@ -32,7 +32,15 @@ "rootDir": "./src", "transform": { "^.+\\.tsx?$": [ - "@swc/jest" + "@swc/jest", + { + "jsc": { + "parser": { + "syntax": "typescript", + "decorators": true + } + } + } ] }, "extensionsToTreatAsEsm": [ diff --git a/yarn-project/aztec.js/package.json b/yarn-project/aztec.js/package.json index 3656a356d9a..367a1216ab2 100644 --- a/yarn-project/aztec.js/package.json +++ b/yarn-project/aztec.js/package.json @@ -49,7 +49,15 @@ "rootDir": "./src", "transform": { "^.+\\.tsx?$": [ - "@swc/jest" + "@swc/jest", + { + "jsc": { + "parser": { + "syntax": "typescript", + "decorators": true + } + } + } ] }, "extensionsToTreatAsEsm": [ diff --git a/yarn-project/aztec/package.json b/yarn-project/aztec/package.json index b0e86433144..989cf37a039 100644 --- a/yarn-project/aztec/package.json +++ b/yarn-project/aztec/package.json @@ -77,7 +77,15 @@ "rootDir": "./src", "transform": { "^.+\\.tsx?$": [ - "@swc/jest" + "@swc/jest", + { + "jsc": { + "parser": { + "syntax": "typescript", + "decorators": true + } + } + } ] }, "extensionsToTreatAsEsm": [ diff --git a/yarn-project/bb-prover/package.json b/yarn-project/bb-prover/package.json index 07441ca4c14..89ad0498089 100644 --- a/yarn-project/bb-prover/package.json +++ b/yarn-project/bb-prover/package.json @@ -35,7 +35,15 @@ "rootDir": "./src", "transform": { "^.+\\.tsx?$": [ - "@swc/jest" + "@swc/jest", + { + "jsc": { + "parser": { + "syntax": "typescript", + "decorators": true + } + } + } ] }, "extensionsToTreatAsEsm": [ diff --git a/yarn-project/builder/package.json b/yarn-project/builder/package.json index 29b648700ff..c6f153b7f05 100644 --- a/yarn-project/builder/package.json +++ b/yarn-project/builder/package.json @@ -41,7 +41,15 @@ "rootDir": "./src", "transform": { "^.+\\.tsx?$": [ - "@swc/jest" + "@swc/jest", + { + "jsc": { + "parser": { + "syntax": "typescript", + "decorators": true + } + } + } ] }, "extensionsToTreatAsEsm": [ diff --git a/yarn-project/circuit-types/package.json b/yarn-project/circuit-types/package.json index 8779c385af2..09e852caff3 100644 --- a/yarn-project/circuit-types/package.json +++ b/yarn-project/circuit-types/package.json @@ -36,7 +36,15 @@ "rootDir": "./src", "transform": { "^.+\\.tsx?$": [ - "@swc/jest" + "@swc/jest", + { + "jsc": { + "parser": { + "syntax": "typescript", + "decorators": true + } + } + } ] }, "extensionsToTreatAsEsm": [ diff --git a/yarn-project/circuits.js/package.json b/yarn-project/circuits.js/package.json index 84f9e873016..524b4a62210 100644 --- a/yarn-project/circuits.js/package.json +++ b/yarn-project/circuits.js/package.json @@ -72,7 +72,15 @@ ], "transform": { "^.+\\.tsx?$": [ - "@swc/jest" + "@swc/jest", + { + "jsc": { + "parser": { + "syntax": "typescript", + "decorators": true + } + } + } ] }, "moduleNameMapper": { diff --git a/yarn-project/cli/package.json b/yarn-project/cli/package.json index 007477eecff..303d4d9c336 100644 --- a/yarn-project/cli/package.json +++ b/yarn-project/cli/package.json @@ -37,7 +37,15 @@ ], "transform": { "^.+\\.tsx?$": [ - "@swc/jest" + "@swc/jest", + { + "jsc": { + "parser": { + "syntax": "typescript", + "decorators": true + } + } + } ] }, "reporters": [ diff --git a/yarn-project/end-to-end/package.json b/yarn-project/end-to-end/package.json index 027d9403c2d..6444cf51694 100644 --- a/yarn-project/end-to-end/package.json +++ b/yarn-project/end-to-end/package.json @@ -116,7 +116,15 @@ ], "transform": { "^.+\\.tsx?$": [ - "@swc/jest" + "@swc/jest", + { + "jsc": { + "parser": { + "syntax": "typescript", + "decorators": true + } + } + } ] }, "reporters": [ diff --git a/yarn-project/entrypoints/package.json b/yarn-project/entrypoints/package.json index 63470f19789..48c6c5535a4 100644 --- a/yarn-project/entrypoints/package.json +++ b/yarn-project/entrypoints/package.json @@ -35,7 +35,15 @@ "rootDir": "./src", "transform": { "^.+\\.tsx?$": [ - "@swc/jest" + "@swc/jest", + { + "jsc": { + "parser": { + "syntax": "typescript", + "decorators": true + } + } + } ] }, "extensionsToTreatAsEsm": [ diff --git a/yarn-project/ethereum/package.json b/yarn-project/ethereum/package.json index efd72f2d3da..b7518c9547f 100644 --- a/yarn-project/ethereum/package.json +++ b/yarn-project/ethereum/package.json @@ -51,7 +51,15 @@ "rootDir": "./src", "transform": { "^.+\\.tsx?$": [ - "@swc/jest" + "@swc/jest", + { + "jsc": { + "parser": { + "syntax": "typescript", + "decorators": true + } + } + } ] }, "extensionsToTreatAsEsm": [ diff --git a/yarn-project/foundation/package.json b/yarn-project/foundation/package.json index ec8d34c3480..17295e60f26 100644 --- a/yarn-project/foundation/package.json +++ b/yarn-project/foundation/package.json @@ -59,7 +59,15 @@ "jest": { "transform": { "^.+\\.tsx?$": [ - "@swc/jest" + "@swc/jest", + { + "jsc": { + "parser": { + "syntax": "typescript", + "decorators": true + } + } + } ] }, "moduleNameMapper": { diff --git a/yarn-project/key-store/package.json b/yarn-project/key-store/package.json index 79ce75204c3..0bf868d644d 100644 --- a/yarn-project/key-store/package.json +++ b/yarn-project/key-store/package.json @@ -29,7 +29,15 @@ "rootDir": "./src", "transform": { "^.+\\.tsx?$": [ - "@swc/jest" + "@swc/jest", + { + "jsc": { + "parser": { + "syntax": "typescript", + "decorators": true + } + } + } ] }, "extensionsToTreatAsEsm": [ diff --git a/yarn-project/kv-store/package.json b/yarn-project/kv-store/package.json index 0fcb06fd0e3..2ca6477b149 100644 --- a/yarn-project/kv-store/package.json +++ b/yarn-project/kv-store/package.json @@ -28,7 +28,15 @@ "workerThreads": true, "transform": { "^.+\\.tsx?$": [ - "@swc/jest" + "@swc/jest", + { + "jsc": { + "parser": { + "syntax": "typescript", + "decorators": true + } + } + } ] }, "extensionsToTreatAsEsm": [ diff --git a/yarn-project/merkle-tree/package.json b/yarn-project/merkle-tree/package.json index 8d19e74c5a5..0446f9a9d02 100644 --- a/yarn-project/merkle-tree/package.json +++ b/yarn-project/merkle-tree/package.json @@ -31,7 +31,15 @@ "testTimeout": 15000, "transform": { "^.+\\.tsx?$": [ - "@swc/jest" + "@swc/jest", + { + "jsc": { + "parser": { + "syntax": "typescript", + "decorators": true + } + } + } ] }, "extensionsToTreatAsEsm": [ diff --git a/yarn-project/noir-contracts.js/package.json b/yarn-project/noir-contracts.js/package.json index 138a1771247..881226f0a93 100644 --- a/yarn-project/noir-contracts.js/package.json +++ b/yarn-project/noir-contracts.js/package.json @@ -29,7 +29,15 @@ "rootDir": "./src", "transform": { "^.+\\.tsx?$": [ - "@swc/jest" + "@swc/jest", + { + "jsc": { + "parser": { + "syntax": "typescript", + "decorators": true + } + } + } ] }, "extensionsToTreatAsEsm": [ diff --git a/yarn-project/noir-protocol-circuits-types/package.json b/yarn-project/noir-protocol-circuits-types/package.json index 1565d182094..dab1d3a0e72 100644 --- a/yarn-project/noir-protocol-circuits-types/package.json +++ b/yarn-project/noir-protocol-circuits-types/package.json @@ -33,7 +33,15 @@ ], "transform": { "^.+\\.tsx?$": [ - "@swc/jest" + "@swc/jest", + { + "jsc": { + "parser": { + "syntax": "typescript", + "decorators": true + } + } + } ] }, "reporters": [ diff --git a/yarn-project/p2p-bootstrap/package.json b/yarn-project/p2p-bootstrap/package.json index 86c7b7ff4c5..9e9d564c9ae 100644 --- a/yarn-project/p2p-bootstrap/package.json +++ b/yarn-project/p2p-bootstrap/package.json @@ -55,7 +55,15 @@ "rootDir": "./src", "transform": { "^.+\\.tsx?$": [ - "@swc/jest" + "@swc/jest", + { + "jsc": { + "parser": { + "syntax": "typescript", + "decorators": true + } + } + } ] }, "extensionsToTreatAsEsm": [ diff --git a/yarn-project/p2p/package.json b/yarn-project/p2p/package.json index e37434e949d..b1f146e1356 100644 --- a/yarn-project/p2p/package.json +++ b/yarn-project/p2p/package.json @@ -31,7 +31,15 @@ "rootDir": "./src", "transform": { "^.+\\.tsx?$": [ - "@swc/jest" + "@swc/jest", + { + "jsc": { + "parser": { + "syntax": "typescript", + "decorators": true + } + } + } ] }, "extensionsToTreatAsEsm": [ diff --git a/yarn-project/package.common.json b/yarn-project/package.common.json index 6fcf1a28315..7ef660b828e 100644 --- a/yarn-project/package.common.json +++ b/yarn-project/package.common.json @@ -20,7 +20,19 @@ }, "jest": { "extensionsToTreatAsEsm": [".ts"], - "transform": { "^.+\\.tsx?$": ["@swc/jest"] }, + "transform": { + "^.+\\.tsx?$": [ + "@swc/jest", + { + "jsc": { + "parser": { + "syntax": "typescript", + "decorators": true + } + } + } + ] + }, "moduleNameMapper": { "^(\\.{1,2}/.*)\\.[cm]?js$": "$1" }, diff --git a/yarn-project/protocol-contracts/package.json b/yarn-project/protocol-contracts/package.json index d05a83250fd..48007fea086 100644 --- a/yarn-project/protocol-contracts/package.json +++ b/yarn-project/protocol-contracts/package.json @@ -40,7 +40,15 @@ "rootDir": "./src", "transform": { "^.+\\.tsx?$": [ - "@swc/jest" + "@swc/jest", + { + "jsc": { + "parser": { + "syntax": "typescript", + "decorators": true + } + } + } ] }, "extensionsToTreatAsEsm": [ diff --git a/yarn-project/prover-client/package.json b/yarn-project/prover-client/package.json index 87cd8921482..04cc3185a95 100644 --- a/yarn-project/prover-client/package.json +++ b/yarn-project/prover-client/package.json @@ -34,7 +34,15 @@ "rootDir": "./src", "transform": { "^.+\\.tsx?$": [ - "@swc/jest" + "@swc/jest", + { + "jsc": { + "parser": { + "syntax": "typescript", + "decorators": true + } + } + } ] }, "extensionsToTreatAsEsm": [ diff --git a/yarn-project/pxe/package.json b/yarn-project/pxe/package.json index 9d3e04fee0c..588fb9fec9b 100644 --- a/yarn-project/pxe/package.json +++ b/yarn-project/pxe/package.json @@ -32,7 +32,15 @@ "workerThreads": true, "transform": { "^.+\\.tsx?$": [ - "@swc/jest" + "@swc/jest", + { + "jsc": { + "parser": { + "syntax": "typescript", + "decorators": true + } + } + } ] }, "extensionsToTreatAsEsm": [ diff --git a/yarn-project/scripts/package.json b/yarn-project/scripts/package.json index e7a327778e6..0f5849a2ab2 100644 --- a/yarn-project/scripts/package.json +++ b/yarn-project/scripts/package.json @@ -59,7 +59,15 @@ "rootDir": "./src", "transform": { "^.+\\.tsx?$": [ - "@swc/jest" + "@swc/jest", + { + "jsc": { + "parser": { + "syntax": "typescript", + "decorators": true + } + } + } ] }, "extensionsToTreatAsEsm": [ diff --git a/yarn-project/sequencer-client/package.json b/yarn-project/sequencer-client/package.json index 05659179f15..96574ecbb1b 100644 --- a/yarn-project/sequencer-client/package.json +++ b/yarn-project/sequencer-client/package.json @@ -79,7 +79,15 @@ ], "transform": { "^.+\\.tsx?$": [ - "@swc/jest" + "@swc/jest", + { + "jsc": { + "parser": { + "syntax": "typescript", + "decorators": true + } + } + } ] }, "moduleNameMapper": { diff --git a/yarn-project/simulator/package.json b/yarn-project/simulator/package.json index 42bb7c5fb4d..8356b976f28 100644 --- a/yarn-project/simulator/package.json +++ b/yarn-project/simulator/package.json @@ -32,7 +32,15 @@ "rootDir": "./src", "transform": { "^.+\\.tsx?$": [ - "@swc/jest" + "@swc/jest", + { + "jsc": { + "parser": { + "syntax": "typescript", + "decorators": true + } + } + } ] }, "extensionsToTreatAsEsm": [ diff --git a/yarn-project/telemetry-client/package.json b/yarn-project/telemetry-client/package.json index 84ee023757e..d937716d35f 100644 --- a/yarn-project/telemetry-client/package.json +++ b/yarn-project/telemetry-client/package.json @@ -49,7 +49,15 @@ ], "transform": { "^.+\\.tsx?$": [ - "@swc/jest" + "@swc/jest", + { + "jsc": { + "parser": { + "syntax": "typescript", + "decorators": true + } + } + } ] }, "moduleNameMapper": { diff --git a/yarn-project/txe/package.json b/yarn-project/txe/package.json index 506521672f5..f77a47b7f62 100644 --- a/yarn-project/txe/package.json +++ b/yarn-project/txe/package.json @@ -33,7 +33,15 @@ "workerThreads": true, "transform": { "^.+\\.tsx?$": [ - "@swc/jest" + "@swc/jest", + { + "jsc": { + "parser": { + "syntax": "typescript", + "decorators": true + } + } + } ] }, "extensionsToTreatAsEsm": [ diff --git a/yarn-project/types/package.json b/yarn-project/types/package.json index 4abfe1f9a65..b750c105ca8 100644 --- a/yarn-project/types/package.json +++ b/yarn-project/types/package.json @@ -34,7 +34,15 @@ "rootDir": "./src", "transform": { "^.+\\.tsx?$": [ - "@swc/jest" + "@swc/jest", + { + "jsc": { + "parser": { + "syntax": "typescript", + "decorators": true + } + } + } ] }, "extensionsToTreatAsEsm": [ diff --git a/yarn-project/world-state/package.json b/yarn-project/world-state/package.json index 5e3dd632a20..2f2b53a1cdf 100644 --- a/yarn-project/world-state/package.json +++ b/yarn-project/world-state/package.json @@ -29,7 +29,15 @@ "rootDir": "./src", "transform": { "^.+\\.tsx?$": [ - "@swc/jest" + "@swc/jest", + { + "jsc": { + "parser": { + "syntax": "typescript", + "decorators": true + } + } + } ] }, "extensionsToTreatAsEsm": [