Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ async function cleanupArtifacts(target: string) {
}
const fileData = JSON.parse((await readFile(join(target, file), 'utf8')).toString());
fileData.file_map = {};
fileData.debug_symbols = {};
fileData.debug_symbols = '';
await writeFile(join(target, file), JSON.stringify(fileData));
}
}
Expand Down
2 changes: 1 addition & 1 deletion yarn-project/simulator/src/client.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
export * from './private/index.js';
export { WASMSimulator } from './private/providers/acvm_wasm.js';
export { type SimulationProvider } from './private/providers/simulation_provider.js';
export { type SimulationProvider, type DecodedError } from './private/providers/simulation_provider.js';
export * from './common/index.js';
4 changes: 2 additions & 2 deletions yarn-project/simulator/src/private/providers/acvm_wasm.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import type { NoirCompiledCircuit } from '@aztec/stdlib/noir';

import { type ACIRCallback, acvm } from '../acvm/acvm.js';
import type { ACVMWitness } from '../acvm/acvm_types.js';
import { type SimulationProvider, parseErrorPayload } from './simulation_provider.js';
import { type SimulationProvider, enrichNoirError } from './simulation_provider.js';

export class WASMSimulator implements SimulationProvider {
constructor(protected log = createLogger('wasm-simulator')) {}
Expand Down Expand Up @@ -42,7 +42,7 @@ export class WASMSimulator implements SimulationProvider {
} catch (err) {
// Typescript types catched errors as unknown or any, so we need to narrow its type to check if it has raw assertion payload.
if (typeof err === 'object' && err !== null && 'rawAssertionPayload' in err) {
const parsed = parseErrorPayload(compiledCircuit.abi, err as ExecutionError);
const parsed = enrichNoirError(compiledCircuit, err as ExecutionError);
this.log.debug('execution failed', {
hash: compiledCircuit.hash,
error: parsed,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import type { NoirCompiledCircuit } from '@aztec/stdlib/noir';

import type { ACIRCallback, ACIRExecutionResult } from '../acvm/acvm.js';
import type { ACVMWitness } from '../acvm/acvm_types.js';
import { type SimulationProvider, parseErrorPayload } from './simulation_provider.js';
import { type SimulationProvider, enrichNoirError } from './simulation_provider.js';

/**
* A simulation provider that uses the WASM simulator with the ability to handle blobs via the foreign call handler.
Expand Down Expand Up @@ -33,7 +33,7 @@ export class WASMSimulatorWithBlobs implements SimulationProvider {
} catch (err) {
// Typescript types catched errors as unknown or any, so we need to narrow its type to check if it has raw assertion payload.
if (typeof err === 'object' && err !== null && 'rawAssertionPayload' in err) {
throw parseErrorPayload(compiledCircuit.abi, err as ExecutionError);
throw enrichNoirError(compiledCircuit, err as ExecutionError);
}
throw new Error(`Circuit execution failed: ${err}`);
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
import type { ExecutionError } from '@aztec/noir-acvm_js';
import { abiDecodeError } from '@aztec/noir-noirc_abi';
import type { Abi, WitnessMap } from '@aztec/noir-types';
import type { WitnessMap } from '@aztec/noir-types';
import { parseDebugSymbols } from '@aztec/stdlib/abi';
import type { NoirCompiledCircuit } from '@aztec/stdlib/noir';

import type { ACIRCallback, ACIRExecutionResult } from '../acvm/acvm.js';
import { type ACIRCallback, type ACIRExecutionResult, extractCallStack } from '../acvm/acvm.js';
import type { ACVMWitness } from '../acvm/acvm_types.js';

/**
Expand All @@ -14,20 +15,20 @@ export interface SimulationProvider {
executeUserCircuit(acir: Buffer, initialWitness: ACVMWitness, callback: ACIRCallback): Promise<ACIRExecutionResult>;
}

export type ErrorWithPayload = ExecutionError & { decodedAssertionPayload?: any };
export type DecodedError = ExecutionError & { decodedAssertionPayload?: any; noirCallStack?: string[] };

// Error handling taken from noir/noir-repo/tooling/noir_js/src/witness_generation.ts.
// Payload parsing taken from noir/noir-repo/tooling/noir_js/src/witness_generation.ts.
// TODO: import this in isolation without having to import noir_js in its entirety.
export function parseErrorPayload(abi: Abi, originalError: ExecutionError): Error {
export function enrichNoirError(artifact: NoirCompiledCircuit, originalError: ExecutionError): DecodedError {
const payload = originalError.rawAssertionPayload;
if (!payload) {
return originalError;
}
const enrichedError = originalError as ErrorWithPayload;
const enrichedError = originalError as DecodedError;

try {
// Decode the payload
const decodedPayload = abiDecodeError(abi, payload);
const decodedPayload = abiDecodeError(artifact.abi, payload);

if (typeof decodedPayload === 'string') {
// If it's a string, just add it to the error message
Expand All @@ -40,5 +41,26 @@ export function parseErrorPayload(abi: Abi, originalError: ExecutionError): Erro
// Ignore errors decoding the payload
}

try {
// Decode the callstack
const callStack = extractCallStack(originalError, {
// TODO(https://github.com/AztecProtocol/aztec-packages/issues/5813)
// We only support handling debug info for the circuit entry point.
// So for now we simply index into the first debug info.
debugSymbols: parseDebugSymbols(artifact.debug_symbols)[0],
files: artifact.file_map,
});

enrichedError.noirCallStack = callStack?.map(errorLocation => {
if (typeof errorLocation === 'string') {
return `at opcode ${errorLocation}`;
} else {
return `at ${errorLocation.locationText} (${errorLocation.filePath}:${errorLocation.line}:${errorLocation.column})`;
}
});
} catch (_errorResolving) {
// Ignore errors resolving the callstack
}

return enrichedError;
}
10 changes: 6 additions & 4 deletions yarn-project/stdlib/src/abi/abi.ts
Original file line number Diff line number Diff line change
Expand Up @@ -419,6 +419,10 @@ export async function getFunctionArtifact(
return { ...functionArtifact, debug: debugMetadata };
}

export function parseDebugSymbols(debugSymbols: string): DebugInfo[] {
return JSON.parse(inflate(Buffer.from(debugSymbols, 'base64'), { to: 'string', raw: true })).debug_infos;
}

/**
* Gets the debug metadata of a given function from the contract artifact
* @param artifact - The contract build artifact
Expand All @@ -432,14 +436,12 @@ export function getFunctionDebugMetadata(
try {
if (functionArtifact.debugSymbols && contractArtifact.fileMap) {
// TODO(https://github.com/AztecProtocol/aztec-packages/issues/10546) investigate why debugMetadata is so big for some tests.
const programDebugSymbols = JSON.parse(
inflate(Buffer.from(functionArtifact.debugSymbols, 'base64'), { to: 'string', raw: true }),
);
const programDebugSymbols = parseDebugSymbols(functionArtifact.debugSymbols);
// TODO(https://github.com/AztecProtocol/aztec-packages/issues/5813)
// We only support handling debug info for the contract function entry point.
// So for now we simply index into the first debug info.
return {
debugSymbols: programDebugSymbols.debug_infos[0],
debugSymbols: programDebugSymbols[0],
files: contractArtifact.fileMap,
};
}
Expand Down