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
2 changes: 2 additions & 0 deletions tooling/noir_js/scripts/compile_test_programs.sh
Original file line number Diff line number Diff line change
Expand Up @@ -8,3 +8,5 @@ $NARGO --program-dir ./test/noir_compiled_examples/assert_msg_runtime compile --
$NARGO --program-dir ./test/noir_compiled_examples/fold_fibonacci compile --force --pedantic-solving
$NARGO --program-dir ./test/noir_compiled_examples/assert_raw_payload compile --force --pedantic-solving
$NARGO --program-dir ./test/noir_compiled_examples/databus compile --force --pedantic-solving
# Compile with no inlining to test runtime call stacks
$NARGO --program-dir ./test/noir_compiled_examples/assert_inside_brillig_nested compile --force --pedantic-solving --inliner-aggressiveness -9223372036854775808
11 changes: 6 additions & 5 deletions tooling/noir_js/src/debug.ts
Original file line number Diff line number Diff line change
Expand Up @@ -71,13 +71,13 @@ function resolveOpcodeLocations(
);
// Adds the acir call stack if the last location is a brillig opcode
if (locations.length > 0) {
const runtimeLocations = opcodeLocations[opcodeLocations.length - 1].split('.');
if (runtimeLocations.length === 2) {
const acirCallstackId = debug.acir_locations[runtimeLocations[0]];
const decomposedOpcodeLocation = opcodeLocations[opcodeLocations.length - 1].split('.');
if (decomposedOpcodeLocation.length === 2) {
const acirCallstackId = debug.acir_locations[decomposedOpcodeLocation[0]];
if (acirCallstackId !== undefined) {
const callStack = debug.location_tree.locations[acirCallstackId];
const acirCallstack = getCallStackFromLocationNode(callStack, debug.location_tree.locations, files);
locations = locations.concat(acirCallstack);
locations = acirCallstack.concat(locations);
}
}
}
Expand Down Expand Up @@ -110,7 +110,8 @@ function getCallStackFromLocationNode(

callStack = location_tree[callStack.parent];
}
return result;
// Reverse since we explored the child nodes first
return result.reverse();
}
/**
* Extracts the call stack from the location of a failing opcode and the debug metadata.
Expand Down
36 changes: 34 additions & 2 deletions tooling/noir_js/test/node/execute.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import assert_msg_json from '../noir_compiled_examples/assert_msg_runtime/target
import fold_fibonacci_json from '../noir_compiled_examples/fold_fibonacci/target/fold_fibonacci.json' assert { type: 'json' };
import assert_raw_payload_json from '../noir_compiled_examples/assert_raw_payload/target/assert_raw_payload.json' assert { type: 'json' };
import databus_json from '../noir_compiled_examples/databus/target/databus.json' assert { type: 'json' };
import assert_inside_brillig_nested_json from '../noir_compiled_examples/assert_inside_brillig_nested/target/assert_inside_brillig_nested.json' assert { type: 'json' };

import { Noir, ErrorWithPayload } from '@noir-lang/noir_js';
import { CompiledCircuit } from '@noir-lang/types';
Expand All @@ -12,6 +13,7 @@ const assert_lt_program = assert_lt_json as CompiledCircuit;
const assert_msg_runtime = assert_msg_json as CompiledCircuit;
const fold_fibonacci_program = fold_fibonacci_json as CompiledCircuit;
const databus_program = databus_json as CompiledCircuit;
const assert_inside_brillig_nested = assert_inside_brillig_nested_json as CompiledCircuit;

it('executes a single-ACIR program correctly', async () => {
const inputs = {
Expand Down Expand Up @@ -40,8 +42,38 @@ it('circuit with a fmt string assert message should fail with the resolved asser
} catch (error) {
const knownError = error as ErrorWithPayload;
expect(knownError.message).to.equal('Circuit execution failed: Expected x < y but got 10 < 5');
expect(knownError.noirCallStack).to.have.lengthOf(1);
expect(knownError.noirCallStack![0]).to.match(/^at x < y \(.*assert_msg_runtime\/src\/main.nr:3:12\)$/);
}
});

it('circuit with a nested assertion should fail with the resolved call stack', async () => {
try {
await new Noir(assert_msg_runtime).execute({
x: '10',
y: '5',
});
} catch (error) {
const knownError = error as ErrorWithPayload;
expect(knownError.noirCallStack).to.have.lengthOf(2);
expect(knownError.noirCallStack![0]).to.match(
/^at make_assertion\(x, y\) \(.*assert_msg_runtime\/src\/main.nr:2:5\)$/,
);
expect(knownError.noirCallStack![1]).to.match(/^at x < y \(.*assert_msg_runtime\/src\/main.nr:7:12\)$/);
}
});

it('circuit with a nested assertion inside brillig should fail with the resolved call stack', async () => {
try {
await new Noir(assert_inside_brillig_nested).execute({
x: '10',
});
} catch (error) {
const knownError = error as ErrorWithPayload;
const expectedStack = ['acir_wrapper(x)', 'brillig_entrypoint(x)', 'brillig_nested(x)', 'x < 10'];
expect(knownError.noirCallStack).to.have.lengthOf(expectedStack.length);

for (let i = 0; i < expectedStack.length; i++) {
expect(knownError.noirCallStack![i]).to.contain(expectedStack[i]);
}
}
});

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
[package]
name = "assert_inside_brillig_nested"
type = "bin"
authors = [""]
[dependencies]
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
fn main(x: u64) {
acir_wrapper(x);
}

fn acir_wrapper(x: u64) {
unsafe {
brillig_entrypoint(x);
}
}

unconstrained fn brillig_entrypoint(x: u64) {
brillig_nested(x);
}

unconstrained fn brillig_nested(x: u64) {
assert(x < 10);
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,8 @@
fn main(x: u64, y: pub u64) {
make_assertion(x, y);
}

fn make_assertion(x: u64, y: u64) {
// A fmtstr assertion message is used to show that noirJS will decode the error payload as a string.
assert(x < y, f"Expected x < y but got {x} < {y}");
}
Loading