From fff6eb816240884696b12d7a9ada7070ea8c9f11 Mon Sep 17 00:00:00 2001 From: dbanks12 Date: Thu, 8 Jun 2023 01:52:47 +0000 Subject: [PATCH 1/7] advanced debug formatting for noir + acir simulator --- .../acir-simulator/src/client/debug.ts | 81 +++++++++++++++++++ .../src/client/private_execution.ts | 6 +- .../src/client/unconstrained_execution.ts | 6 +- .../noir-aztec3/src/oracle/debug_log.nr | 26 ++++-- 4 files changed, 108 insertions(+), 11 deletions(-) create mode 100644 yarn-project/acir-simulator/src/client/debug.ts diff --git a/yarn-project/acir-simulator/src/client/debug.ts b/yarn-project/acir-simulator/src/client/debug.ts new file mode 100644 index 000000000000..181c5a90c95d --- /dev/null +++ b/yarn-project/acir-simulator/src/client/debug.ts @@ -0,0 +1,81 @@ +import { + ACVMField, +} from '../acvm/index.js'; + +/** + * Convert an array of ACVMFields to a string. + * + * @param msg - array of ACVMFields where each represents a single ascii character + * @returns string representation of the message + */ +function acvmFieldMessageToString(msg: ACVMField[]): string { + let msgStr = ""; + for (const msgChar of msg) { + const asciiCode = Number(msgChar); + const asciiChar = String.fromCharCode(asciiCode); + msgStr = msgStr.concat(asciiChar); + + } + const nullCharIndex = msgStr.indexOf("\\0"); + if (nullCharIndex >= 0) { + msgStr = msgStr.substring(0, nullCharIndex); + } + return msgStr.replaceAll("\\n", "\n").replaceAll("\\t", "\t");; +} + +/** + * Format a debug string for Noir filling in `'{0}'` entries with their + * corresponding values from the args array. + * + * @param formatStr - str of form `'this is a string with some entries like {0} and {1}'` + * @param args - array of fields to fill in the string format entries with + * @returns formatted string + */ +function applyStringFormatting(formatStr: string, args: ACVMField[]): string { + const matches = formatStr.match(/{\d+}/g); + if (matches == null) { + return formatStr; + } + // Get the numeric values within the curly braces, convert them to numbers, + // and find the maximum value. + const maxIndex = Math.max(...matches.map(match => Number(match.slice(1, -1)))); + const argsPadded = args.concat( + Array.from( + { length: Math.max(0, maxIndex - args.length) }, + () => '0xBAD', + ) + ); + + return formatStr.replace(/{(\d+)}/g, function(match, index) { + return typeof args[index] != 'undefined' ? argsPadded[index] : match; + }); +} + +/** + * Convert an array of ACVMFields from ACVM to a formatted string. + * + * @param fields - either a single field to be printed, or a string to be formatted. + * When it is a string to be formatted: + * The last entry in `fields` is `numArgs` (the number of formatting + * args). The `formatArgs` occupy the end of the `fields` array, + * excluding that last entry (`numArgs`). The message string `msg` + * takes up the remaining entries at the start of the `fields` array. + * + * @returns formatted string + */ +export function fieldsToFormattedStr(fields: ACVMField[]): string { + if (fields.length === 1) { + return `${fields[0]}`; + } else { + const numArgs = Number(fields[fields.length - 1]); + const msgLen = fields.length - 1 - numArgs; + + const msgFields = fields.slice(0, msgLen); + const formatArgs = fields.slice(msgLen, fields.length - 1); + + const msg = acvmFieldMessageToString(msgFields); + const formattedMsg = applyStringFormatting(msg, formatArgs); + + return formattedMsg; + } +} \ No newline at end of file diff --git a/yarn-project/acir-simulator/src/client/private_execution.ts b/yarn-project/acir-simulator/src/client/private_execution.ts index b8395eff9534..bdaaafc04c33 100644 --- a/yarn-project/acir-simulator/src/client/private_execution.ts +++ b/yarn-project/acir-simulator/src/client/private_execution.ts @@ -26,6 +26,7 @@ import { toAcvmEnqueuePublicFunctionResult, } from '../acvm/index.js'; import { sizeOfType } from '../index.js'; +import { fieldsToFormattedStr } from './debug.js'; import { ClientTxExecutionContext } from './client_execution_context.js'; import { Tuple, assertLength } from '@aztec/foundation/serialize'; @@ -173,9 +174,8 @@ export class PrivateFunctionExecution { }, getL1ToL2Message: ([msgKey]: ACVMField[]) => this.context.getL1ToL2Message(fromACVMField(msgKey)), - debugLog: ([data]: ACVMField[]) => { - // eslint-disable-next-line - console.log(data); + debugLog: (fields: ACVMField[]) => { + this.log(fieldsToFormattedStr(fields)); return Promise.resolve([ZERO_ACVM_FIELD]); }, enqueuePublicFunctionCall: async ([acvmContractAddress, acvmFunctionSelector, ...acvmArgs]) => { diff --git a/yarn-project/acir-simulator/src/client/unconstrained_execution.ts b/yarn-project/acir-simulator/src/client/unconstrained_execution.ts index fb06a399fb3d..24235c157aac 100644 --- a/yarn-project/acir-simulator/src/client/unconstrained_execution.ts +++ b/yarn-project/acir-simulator/src/client/unconstrained_execution.ts @@ -8,6 +8,7 @@ import { ClientTxExecutionContext } from './client_execution_context.js'; import { select_return_flattened as selectReturnFlattened } from '@noir-lang/noir_util_wasm'; import { AztecAddress } from '@aztec/foundation/aztec-address'; import { Fr } from '@aztec/foundation/fields'; +import { fieldsToFormattedStr } from './debug.js'; const notAvailable = () => { return Promise.reject(new Error(`Not available for unconstrained function execution`)); @@ -55,9 +56,8 @@ export class UnconstrainedFunctionExecution { frToNumber(fromACVMField(acvmLimit)), frToNumber(fromACVMField(acvmOffset)), ), - debugLog: ([data]: ACVMField[]) => { - // eslint-disable-next-line - console.log(data); + debugLog: (fields: ACVMField[]) => { + this.log(fieldsToFormattedStr(fields)); return Promise.resolve([ZERO_ACVM_FIELD]); }, getL1ToL2Message: ([msgKey]: ACVMField[]) => this.context.getL1ToL2Message(fromACVMField(msgKey)), diff --git a/yarn-project/noir-contracts/src/contracts/noir-aztec3/src/oracle/debug_log.nr b/yarn-project/noir-contracts/src/contracts/noir-aztec3/src/oracle/debug_log.nr index cca9c200cecf..225558f927cf 100644 --- a/yarn-project/noir-contracts/src/contracts/noir-aztec3/src/oracle/debug_log.nr +++ b/yarn-project/noir-contracts/src/contracts/noir-aztec3/src/oracle/debug_log.nr @@ -1,9 +1,25 @@ - // Utility function to console.log data in the acir simulator #[oracle(debugLog)] -fn debug_log_oracle(_msg: Field) -> Field {} +fn debug_log_oracle(_msg: T, _num_args: Field) -> Field {} +#[oracle(debugLog)] +fn debug_log_format_oracle(_msg: T, _args: [Field; N], _num_args: Field) -> Field {} +#[oracle(debugLog)] +fn debug_log_field_oracle(_field: Field) -> Field {} + +/// NOTE: call this with a str msg of length > 1 +unconstrained fn debug_log(msg: T) { + constrain debug_log_oracle(msg, 0) == 0; +} + +/// NOTE: call this with a str msg of form +/// "some string with {0} and {1} ... {N}" +/// and an array of N field which will be formatted +/// into the string in the simulator. +unconstrained fn debug_log_format(msg: T, args: [Field; N]) { + constrain debug_log_format_oracle(msg, args, args.len()) == 0; +} -unconstrained fn debug_log(msg: Field) -> Field { - debug_log_oracle(msg) -} \ No newline at end of file +unconstrained fn debug_log_field(field: Field) { + constrain debug_log_field_oracle(field) == 0; +} From 559a31d3816e915561b584504564d097b80afde0 Mon Sep 17 00:00:00 2001 From: dbanks12 Date: Thu, 8 Jun 2023 02:11:09 +0000 Subject: [PATCH 2/7] formatting --- .../acir-simulator/src/client/debug.ts | 22 ++++++------------- .../noir-aztec3/src/state_vars/set.nr | 1 + 2 files changed, 8 insertions(+), 15 deletions(-) diff --git a/yarn-project/acir-simulator/src/client/debug.ts b/yarn-project/acir-simulator/src/client/debug.ts index 181c5a90c95d..6813223661a2 100644 --- a/yarn-project/acir-simulator/src/client/debug.ts +++ b/yarn-project/acir-simulator/src/client/debug.ts @@ -1,6 +1,4 @@ -import { - ACVMField, -} from '../acvm/index.js'; +import { ACVMField } from '../acvm/index.js'; /** * Convert an array of ACVMFields to a string. @@ -9,18 +7,17 @@ import { * @returns string representation of the message */ function acvmFieldMessageToString(msg: ACVMField[]): string { - let msgStr = ""; + let msgStr = ''; for (const msgChar of msg) { const asciiCode = Number(msgChar); const asciiChar = String.fromCharCode(asciiCode); msgStr = msgStr.concat(asciiChar); - } - const nullCharIndex = msgStr.indexOf("\\0"); + const nullCharIndex = msgStr.indexOf('\\0'); if (nullCharIndex >= 0) { msgStr = msgStr.substring(0, nullCharIndex); } - return msgStr.replaceAll("\\n", "\n").replaceAll("\\t", "\t");; + return msgStr.replaceAll('\\n', '\n').replaceAll('\\t', '\t'); } /** @@ -39,14 +36,9 @@ function applyStringFormatting(formatStr: string, args: ACVMField[]): string { // Get the numeric values within the curly braces, convert them to numbers, // and find the maximum value. const maxIndex = Math.max(...matches.map(match => Number(match.slice(1, -1)))); - const argsPadded = args.concat( - Array.from( - { length: Math.max(0, maxIndex - args.length) }, - () => '0xBAD', - ) - ); + const argsPadded = args.concat(Array.from({ length: Math.max(0, maxIndex - args.length) }, () => '0xBAD')); - return formatStr.replace(/{(\d+)}/g, function(match, index) { + return formatStr.replace(/{(\d+)}/g, function (match, index) { return typeof args[index] != 'undefined' ? argsPadded[index] : match; }); } @@ -78,4 +70,4 @@ export function fieldsToFormattedStr(fields: ACVMField[]): string { return formattedMsg; } -} \ No newline at end of file +} diff --git a/yarn-project/noir-contracts/src/contracts/noir-aztec3/src/state_vars/set.nr b/yarn-project/noir-contracts/src/contracts/noir-aztec3/src/state_vars/set.nr index 0aee34854d25..0820f0198bac 100644 --- a/yarn-project/noir-contracts/src/contracts/noir-aztec3/src/state_vars/set.nr +++ b/yarn-project/noir-contracts/src/contracts/noir-aztec3/src/state_vars/set.nr @@ -83,6 +83,7 @@ impl Set { constrain note_getter_data.1.root == inputs.old_private_data_tree_root; let notes = (note_getter_data.0.note, note_getter_data.1.note); + debug_log_format("get_2(slot:{0}) =>\n\t0:{0}\n\t1:{1}!", [storage_slot, notes.0.value, notes.1.value]); (context, notes) } From 29be1fd661c550812aeedb769d05f3ad1d0dbbe5 Mon Sep 17 00:00:00 2001 From: dbanks12 Date: Thu, 8 Jun 2023 02:15:14 +0000 Subject: [PATCH 3/7] comment out debug log in noir --- .../src/contracts/noir-aztec3/src/state_vars/set.nr | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/yarn-project/noir-contracts/src/contracts/noir-aztec3/src/state_vars/set.nr b/yarn-project/noir-contracts/src/contracts/noir-aztec3/src/state_vars/set.nr index 0820f0198bac..7b8df2c14939 100644 --- a/yarn-project/noir-contracts/src/contracts/noir-aztec3/src/state_vars/set.nr +++ b/yarn-project/noir-contracts/src/contracts/noir-aztec3/src/state_vars/set.nr @@ -11,7 +11,7 @@ use crate::state_vars::note_getter_data::note_getter_data_len; use crate::oracle::notes::notify_created_note; use crate::oracle::notes::notify_nullified_note; use crate::oracle::notes::get_notes_2_internal; - +//use crate::oracle::debug_log::debug_log_format; fn get_2_notes(storage_slot: Field) -> (NoteGetterData, NoteGetterData) { let fields = get_notes_2_internal(storage_slot); @@ -83,7 +83,7 @@ impl Set { constrain note_getter_data.1.root == inputs.old_private_data_tree_root; let notes = (note_getter_data.0.note, note_getter_data.1.note); - debug_log_format("get_2(slot:{0}) =>\n\t0:{0}\n\t1:{1}!", [storage_slot, notes.0.value, notes.1.value]); + //debug_log_format("get_2(slot:{0}) =>\n\t0:{0}\n\t1:{1}!", [storage_slot, notes.0.value, notes.1.value]); (context, notes) } From 6b3f1d35d05c35782c0f1622fdb4d117c8884f91 Mon Sep 17 00:00:00 2001 From: dbanks12 Date: Thu, 8 Jun 2023 02:22:05 +0000 Subject: [PATCH 4/7] cleanup --- yarn-project/acir-simulator/src/client/debug.ts | 1 + .../src/contracts/noir-aztec3/src/state_vars/set.nr | 3 ++- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/yarn-project/acir-simulator/src/client/debug.ts b/yarn-project/acir-simulator/src/client/debug.ts index 6813223661a2..99b8740139b4 100644 --- a/yarn-project/acir-simulator/src/client/debug.ts +++ b/yarn-project/acir-simulator/src/client/debug.ts @@ -13,6 +13,7 @@ function acvmFieldMessageToString(msg: ACVMField[]): string { const asciiChar = String.fromCharCode(asciiCode); msgStr = msgStr.concat(asciiChar); } + // cut off string in case of preemptive null termination const nullCharIndex = msgStr.indexOf('\\0'); if (nullCharIndex >= 0) { msgStr = msgStr.substring(0, nullCharIndex); diff --git a/yarn-project/noir-contracts/src/contracts/noir-aztec3/src/state_vars/set.nr b/yarn-project/noir-contracts/src/contracts/noir-aztec3/src/state_vars/set.nr index 7b8df2c14939..ff47d99041ca 100644 --- a/yarn-project/noir-contracts/src/contracts/noir-aztec3/src/state_vars/set.nr +++ b/yarn-project/noir-contracts/src/contracts/noir-aztec3/src/state_vars/set.nr @@ -83,7 +83,8 @@ impl Set { constrain note_getter_data.1.root == inputs.old_private_data_tree_root; let notes = (note_getter_data.0.note, note_getter_data.1.note); - //debug_log_format("get_2(slot:{0}) =>\n\t0:{0}\n\t1:{1}!", [storage_slot, notes.0.value, notes.1.value]); + //debug_log_format("get_2(slot:{0}) =>\n\t0:{0}\n\t1:{1}", [storage_slot, notes.0.value, notes.1.value]); + (context, notes) } From 450040f4ec1fa6a5a9737eef828749ba350c7300 Mon Sep 17 00:00:00 2001 From: David Banks <47112877+dbanks12@users.noreply.github.com> Date: Thu, 8 Jun 2023 10:10:00 -0400 Subject: [PATCH 5/7] Update set.nr --- .../src/contracts/noir-aztec3/src/state_vars/set.nr | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/yarn-project/noir-contracts/src/contracts/noir-aztec3/src/state_vars/set.nr b/yarn-project/noir-contracts/src/contracts/noir-aztec3/src/state_vars/set.nr index ff47d99041ca..a40a60e34b7e 100644 --- a/yarn-project/noir-contracts/src/contracts/noir-aztec3/src/state_vars/set.nr +++ b/yarn-project/noir-contracts/src/contracts/noir-aztec3/src/state_vars/set.nr @@ -83,7 +83,7 @@ impl Set { constrain note_getter_data.1.root == inputs.old_private_data_tree_root; let notes = (note_getter_data.0.note, note_getter_data.1.note); - //debug_log_format("get_2(slot:{0}) =>\n\t0:{0}\n\t1:{1}", [storage_slot, notes.0.value, notes.1.value]); + //debug_log_format("get_2(slot:{0}) =>\n\t0:{1}\n\t1:{2}", [storage_slot, notes.0.value, notes.1.value]); (context, notes) } From d7a15d84cb1781db11ad7da13de78476b9389ad8 Mon Sep 17 00:00:00 2001 From: David Banks <47112877+dbanks12@users.noreply.github.com> Date: Thu, 8 Jun 2023 10:14:16 -0400 Subject: [PATCH 6/7] Remove commented out debug in set.nr --- .../src/contracts/noir-aztec3/src/state_vars/set.nr | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/yarn-project/noir-contracts/src/contracts/noir-aztec3/src/state_vars/set.nr b/yarn-project/noir-contracts/src/contracts/noir-aztec3/src/state_vars/set.nr index a40a60e34b7e..58b6bc9e26e5 100644 --- a/yarn-project/noir-contracts/src/contracts/noir-aztec3/src/state_vars/set.nr +++ b/yarn-project/noir-contracts/src/contracts/noir-aztec3/src/state_vars/set.nr @@ -11,7 +11,7 @@ use crate::state_vars::note_getter_data::note_getter_data_len; use crate::oracle::notes::notify_created_note; use crate::oracle::notes::notify_nullified_note; use crate::oracle::notes::get_notes_2_internal; -//use crate::oracle::debug_log::debug_log_format; + fn get_2_notes(storage_slot: Field) -> (NoteGetterData, NoteGetterData) { let fields = get_notes_2_internal(storage_slot); @@ -83,7 +83,6 @@ impl Set { constrain note_getter_data.1.root == inputs.old_private_data_tree_root; let notes = (note_getter_data.0.note, note_getter_data.1.note); - //debug_log_format("get_2(slot:{0}) =>\n\t0:{1}\n\t1:{2}", [storage_slot, notes.0.value, notes.1.value]); (context, notes) } From df8db8dacd1d3d9a7423eae49858a47f97917c5e Mon Sep 17 00:00:00 2001 From: David Banks <47112877+dbanks12@users.noreply.github.com> Date: Thu, 8 Jun 2023 10:16:39 -0400 Subject: [PATCH 7/7] Examples and warnings in debug log --- .../src/contracts/noir-aztec3/src/oracle/debug_log.nr | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/yarn-project/noir-contracts/src/contracts/noir-aztec3/src/oracle/debug_log.nr b/yarn-project/noir-contracts/src/contracts/noir-aztec3/src/oracle/debug_log.nr index 225558f927cf..d953ca952d7c 100644 --- a/yarn-project/noir-contracts/src/contracts/noir-aztec3/src/oracle/debug_log.nr +++ b/yarn-project/noir-contracts/src/contracts/noir-aztec3/src/oracle/debug_log.nr @@ -1,4 +1,5 @@ // Utility function to console.log data in the acir simulator +// WARNING: sometimes when using debug logs the ACVM errors with: `thrown: "solver opcode resolution error: cannot solve opcode: expression has too many unknowns x155"` #[oracle(debugLog)] fn debug_log_oracle(_msg: T, _num_args: Field) -> Field {} @@ -8,6 +9,8 @@ fn debug_log_format_oracle(_msg: T, _args: [Field; N], _num_args: Field) - fn debug_log_field_oracle(_field: Field) -> Field {} /// NOTE: call this with a str msg of length > 1 +/// Example: +/// `debug_log("blah blah this is a debug string");` unconstrained fn debug_log(msg: T) { constrain debug_log_oracle(msg, 0) == 0; } @@ -16,10 +19,14 @@ unconstrained fn debug_log(msg: T) { /// "some string with {0} and {1} ... {N}" /// and an array of N field which will be formatted /// into the string in the simulator. +/// Example: +/// `debug_log_format("get_2(slot:{0}) =>\n\t0:{1}\n\t1:{2}", [storage_slot, notes.0.value, notes.1.value]);` unconstrained fn debug_log_format(msg: T, args: [Field; N]) { constrain debug_log_format_oracle(msg, args, args.len()) == 0; } +/// Example: +/// `debug_log_field(my_field);` unconstrained fn debug_log_field(field: Field) { constrain debug_log_field_oracle(field) == 0; }