Skip to content

fix: Explicitly duplicate unconstrained logging functions rather than removing constraints through closures#16174

Closed
vezenovm wants to merge 3 commits intonextfrom
mv/dup-unconstrained-version-aztec-nr-logs-events
Closed

fix: Explicitly duplicate unconstrained logging functions rather than removing constraints through closures#16174
vezenovm wants to merge 3 commits intonextfrom
mv/dup-unconstrained-version-aztec-nr-logs-events

Conversation

@vezenovm
Copy link
Contributor

@vezenovm vezenovm commented Aug 1, 2025

Note: This is purely a debugging PR to display a Noir compilation bug, not a requested change (yet).

I was suspicious that some of the failures of the Noir sync were as a result of this pre-existing issue with labeling (un)constrained runtimes when using closures.

We merged a workaround for the sync noir-lang/noir#9376. However, it still was not fully clear as to why a panic was occurring. We were seeing a type mismatch panic in the Brillig VM. Even if we were accessing an array OOB, in Brillig every array access has preceding OOB checks. I felt there may be a mismatch in the runtime labeling as noir-lang/noir#9200 switched ACIR arrays to not have explicit OOB checks and that was the PR that started triggering the sync failures.

Testing against fix(ssa): Replace unreachable array access with a constrain in Brillig #9378 shows us that we in fact do have mislabeled runtimes. When based off of next we started hitting the following e2e test failures:

Details

19:59:47  FAIL  src/e2e_offchain_effect.test.ts
19:59:47   e2e_offchain_effect
19:59:47     ✓ should emit offchain effects (2071 ms)
19:59:47     ✓ should not emit any offchain effects (1249 ms)
19:59:47     ✓ should revert when emitting offchain effects from utility function (148 ms)
19:59:47     ✕ should emit event as offchain message and process it (336 ms)
19:59:47     ✕ should emit note as offchain message and process it (436 ms)
19:59:47 
19:59:47   ● e2e_offchain_effect › should emit event as offchain message and process it
19:59:47 
19:59:47     Simulation error: Assertion failed: Index out of bounds, index is 3, length is 3 'self.cache[self.cache_size]'
19:59:47     Context:
19:59:47     TxExecutionRequest(0x0c7b5a3312b3afb6b033f92b01aee25e76b23ada1f534501e65fa792e2943519 called 0x27e740b2)
19:59:47     undefined
19:59:47 
19:59:47       45 |         } else {
19:59:47       46 |             // If we're absorbing, and the cache is not full, add the input into the cache
19:59:47     > 47 |             self.cache[self.cache_size] = input;
19:59:47          |             ^
19:59:47       48 |             self.cache_size += 1;
19:59:47       49 |         }
19:59:47       50 |     }
19:59:47 
19:59:47       at self.cache[self.cache_size] (../../../../../nargo/github.com/noir-lang/poseidon/v0.1.1/src/poseidon2.nr:47:13)
19:59:47       at sponge.absorb (../../../../../nargo/github.com/noir-lang/poseidon/v0.1.1/src/poseidon2.nr:80:13)
19:59:47       at Poseidon2::hash_internal(input, message_size, message_size != N) (../../../../../nargo/github.com/noir-lang/poseidon/v0.1.1/src/poseidon2.nr:16:9)
19:59:47       at poseidon::poseidon2::Poseidon2::hash(inputs, N) (../../../noir-projects/noir-protocol-circuits/crates/types/src/hash.nr:216:5)
19:59:47       at poseidon2_hash(inputs_with_separator) (../../../noir-projects/noir-protocol-circuits/crates/types/src/hash.nr:225:5)
19:59:47       at poseidon2_hash_with_separator([shared_secret.x, shared_secret.y], separator_1) (../../../noir-projects/aztec-nr/aztec/src/messages/encryption/aes128.nr:108:13)
19:59:47       at extract_many_close_to_uniformly_random_256_bits_from_ecdh_shared_secret_using_poseidon2_unsafe(
19:59:47               shared_secret,
19:59:47       ) (/mnt/user-data/maxim/AztecProtocol/aztec-packages/noir-projects/aztec-nr/aztec/src/messages/encryption/aes128.nr:151:47)
19:59:47       at derive_aes_symmetric_key_and_iv_from_ecdh_shared_secret_using_poseidon2_unsafe::<2>(
19:59:47                   ciphertext_shared_secret,
19:59:47       ) (/mnt/user-data/maxim/AztecProtocol/aztec-packages/noir-projects/aztec-nr/aztec/src/messages/encryption/aes128.nr:195:21)
19:59:47       at AES128::encrypt_log(plaintext, recipient) (../../../noir-projects/aztec-nr/aztec/src/messages/logs/event.nr:44:6)
19:59:47       at to_encrypted_private_event_message(event, recipient) (../../../noir-projects/aztec-nr/aztec/src/event/event_interface.nr:97:40)
19:59:47       at f() (../../../noir-projects/aztec-nr/aztec/src/utils/remove_constraints.nr:4:5)
19:59:47       at remove_constraints(|| to_encrypted_private_event_message(event, recipient)) (../../../noir-projects/aztec-nr/aztec/src/event/event_interface.nr:97:18)
19:59:47       at emit_event_as_offchain_message(TestEvent { a, b, c }, &mut context, context.msg_sender()) (../../../noir-projects/noir-contracts/contracts/test/offchain_effect_contract/src/main.nr:54:9)
19:59:47       at OffchainEffect.emit_event_as_offchain_message_for_msg_sender
19:59:47       at SchnorrAccount.entrypoint
19:59:47 
19:59:47     Cause:
19:59:47     Assertion failed
19:59:47 
19:59:47       556 |
19:59:47       557 | module.exports.__wbg_constructor_485c344f17716fe1 = function(arg0) {
19:59:47     > 558 |     const ret = new Error(arg0);
19:59:47           |                 ^
19:59:47       559 |     return ret;
19:59:47       560 | };
19:59:47       561 |
19:59:47 
19:59:47       at module.exports.__wbg_constructor_485c344f17716fe1 (../../../noir/packages/acvm_js/nodejs/acvm_js.js:558:17)
19:59:47       at __wbg_constructor_485c344f17716fe1 externref shim (../wasm:/wasm/acvm_js.wasm-010f1132:1:3407557)
19:59:47       at acvm_js::js_execution_error::JsExecutionError::new::had2b462e5b8eeab4 (../wasm:/wasm/acvm_js.wasm-010f1132:1:2744578)
19:59:47       at acvm_js::execute::ProgramExecutor<B>::execute_circuit::{{closure}}::h6df6bacd9e749451 (../wasm:/wasm/acvm_js.wasm-010f1132:1:147264)
19:59:47       at acvm_js::execute::execute_program_with_native_program_and_return::{{closure}}::h711be51e30da48d4 (../wasm:/wasm/acvm_js.wasm-010f1132:1:2589518)
19:59:47       at wasm_bindgen_futures::future_to_promise::{{closure}}::{{closure}}::ha5c8b60b1cc496ea (../wasm:/wasm/acvm_js.wasm-010f1132:1:1897959)
19:59:47       at wasm_bindgen_futures::queue::Queue::new::{{closure}}::hffc2d5910f77df29 (../wasm:/wasm/acvm_js.wasm-010f1132:1:2990771)
19:59:47       at <dyn core::ops::function::FnMut<(A,)>+Output = R as wasm_bindgen::closure::WasmClosure>::describe::invoke::h7e4a2734b30d0aeb (../wasm:/wasm/acvm_js.wasm-010f1132:1:3405742)
19:59:47       at closure647 externref shim (../wasm:/wasm/acvm_js.wasm-010f1132:1:3409914)
19:59:47       at __wbg_adapter_30 (../../../noir/packages/acvm_js/nodejs/acvm_js.js:531:10)
19:59:47       at real (../../../noir/packages/acvm_js/nodejs/acvm_js.js:125:20)
19:59:47       at AsyncResource.runMicrotask (../node:internal/process/task_queues:148:8)Error:
19:59:47       at ../../simulator/dest/private/acvm/acvm.js:33:23
19:59:47       at acvm (../../simulator/dest/private/acvm/acvm.js:13:36)
19:59:47       at SimulatorRecorderWrapper.#simulate (../../simulator/dest/private/circuit_recording/simulator_recorder_wrapper.js:25:22)
19:59:47       at executePrivateFunction (../../pxe/dest/contract_function_simulator/oracle/private_execution.js:27:33)
19:59:47       at PrivateExecutionOracle.callPrivateFunction (../../pxe/dest/contract_function_simulator/oracle/private_execution_oracle.js:293:38)
19:59:47       at Oracle.callPrivateFunction (../../pxe/dest/contract_function_simulator/oracle/oracle.js:217:55)Error:
19:59:47       at ../../simulator/dest/private/acvm/acvm.js:33:23
19:59:47       at acvm (../../simulator/dest/private/acvm/acvm.js:13:36)
19:59:47       at SimulatorRecorderWrapper.#simulate (../../simulator/dest/private/circuit_recording/simulator_recorder_wrapper.js:25:22)
19:59:47       at executePrivateFunction (../../pxe/dest/contract_function_simulator/oracle/private_execution.js:27:33)
19:59:47       at ContractFunctionSimulator.run (../../pxe/dest/contract_function_simulator/contract_function_simulator.js:70:37)
19:59:47       at PXEService.#executePrivate (../../pxe/dest/pxe_service/pxe_service.js:214:28)
19:59:47       at ../../pxe/dest/pxe_service/pxe_service.js:475:46
19:59:47       at ../../foundation/dest/queue/serial_queue.js:58:33
19:59:47       at FifoMemoryQueue.process (../../foundation/dest/queue/base_memory_queue.js:110:17)
19:59:47 
19:59:47   ● e2e_offchain_effect › should emit note as offchain message and process it
19:59:47 
19:59:47     Simulation error: Assertion failed: Index out of bounds, index is 3, length is 3 'self.cache[self.cache_size]'
19:59:47     Context:
19:59:47     TxExecutionRequest(0x0c7b5a3312b3afb6b033f92b01aee25e76b23ada1f534501e65fa792e2943519 called 0x27e740b2)
19:59:47     undefined
19:59:47 
19:59:47       45 |         } else {
19:59:47       46 |             // If we're absorbing, and the cache is not full, add the input into the cache
19:59:47     > 47 |             self.cache[self.cache_size] = input;
19:59:47          |             ^
19:59:47       48 |             self.cache_size += 1;
19:59:47       49 |         }
19:59:47       50 |     }
19:59:47 
19:59:47       at self.cache[self.cache_size] (../../../../../nargo/github.com/noir-lang/poseidon/v0.1.1/src/poseidon2.nr:47:13)
19:59:47       at sponge.absorb (../../../../../nargo/github.com/noir-lang/poseidon/v0.1.1/src/poseidon2.nr:80:13)
19:59:47       at Poseidon2::hash_internal(input, message_size, message_size != N) (../../../../../nargo/github.com/noir-lang/poseidon/v0.1.1/src/poseidon2.nr:16:9)
19:59:47       at poseidon::poseidon2::Poseidon2::hash(inputs, N) (../../../noir-projects/noir-protocol-circuits/crates/types/src/hash.nr:216:5)
19:59:47       at poseidon2_hash(inputs_with_separator) (../../../noir-projects/noir-protocol-circuits/crates/types/src/hash.nr:225:5)
19:59:47       at poseidon2_hash_with_separator([shared_secret.x, shared_secret.y], separator_1) (../../../noir-projects/aztec-nr/aztec/src/messages/encryption/aes128.nr:108:13)
19:59:47       at extract_many_close_to_uniformly_random_256_bits_from_ecdh_shared_secret_using_poseidon2_unsafe(
19:59:47               shared_secret,
19:59:47       ) (/mnt/user-data/maxim/AztecProtocol/aztec-packages/noir-projects/aztec-nr/aztec/src/messages/encryption/aes128.nr:151:47)
19:59:47       at derive_aes_symmetric_key_and_iv_from_ecdh_shared_secret_using_poseidon2_unsafe::<2>(
19:59:47                   ciphertext_shared_secret,
19:59:47       ) (/mnt/user-data/maxim/AztecProtocol/aztec-packages/noir-projects/aztec-nr/aztec/src/messages/encryption/aes128.nr:195:21)
19:59:47       at AES128::encrypt_log(plaintext, recipient) (../../../noir-projects/aztec-nr/aztec/src/messages/logs/note.nr:89:22)
19:59:47       at compute_log(note, storage_slot, recipient, PRIVATE_NOTE_MSG_TYPE_ID) (../../../noir-projects/aztec-nr/aztec/src/messages/logs/note.nr:36:5)
19:59:47       at compute_note_log(note, storage_slot, recipient) (../../../noir-projects/aztec-nr/aztec/src/messages/logs/note.nr:204:44)
19:59:47       at f() (../../../noir-projects/aztec-nr/aztec/src/utils/remove_constraints.nr:4:5)
19:59:47       at remove_constraints(|| compute_note_log(note, storage_slot, recipient)) (../../../noir-projects/aztec-nr/aztec/src/messages/logs/note.nr:204:22)
19:59:47       at _emit(self) (../../../noir-projects/aztec-nr/aztec/src/note/note_emission.nr:17:9)
19:59:47       at storage.balances.at(owner).insert(note).emit (../../../noir-projects/noir-contracts/contracts/test/offchain_effect_contract/src/main.nr:61:9)
19:59:47       at OffchainEffect.emit_note_as_offchain_message
19:59:47       at SchnorrAccount.entrypoint
19:59:47 
19:59:47     Cause:
19:59:47     Assertion failed
19:59:47 
19:59:47       556 |
19:59:47       557 | module.exports.__wbg_constructor_485c344f17716fe1 = function(arg0) {
19:59:47     > 558 |     const ret = new Error(arg0);
19:59:47           |                 ^
19:59:47       559 |     return ret;
19:59:47       560 | };
19:59:47       561 |
19:59:47 
19:59:47       at module.exports.__wbg_constructor_485c344f17716fe1 (../../../noir/packages/acvm_js/nodejs/acvm_js.js:558:17)
19:59:47       at __wbg_constructor_485c344f17716fe1 externref shim (../wasm:/wasm/acvm_js.wasm-010f1132:1:3407557)
19:59:47       at acvm_js::js_execution_error::JsExecutionError::new::had2b462e5b8eeab4 (../wasm:/wasm/acvm_js.wasm-010f1132:1:2744578)
19:59:47       at acvm_js::execute::ProgramExecutor<B>::execute_circuit::{{closure}}::h6df6bacd9e749451 (../wasm:/wasm/acvm_js.wasm-010f1132:1:147264)
19:59:47       at acvm_js::execute::execute_program_with_native_program_and_return::{{closure}}::h711be51e30da48d4 (../wasm:/wasm/acvm_js.wasm-010f1132:1:2589518)
19:59:47       at wasm_bindgen_futures::future_to_promise::{{closure}}::{{closure}}::ha5c8b60b1cc496ea (../wasm:/wasm/acvm_js.wasm-010f1132:1:1897959)
19:59:47       at wasm_bindgen_futures::queue::Queue::new::{{closure}}::hffc2d5910f77df29 (../wasm:/wasm/acvm_js.wasm-010f1132:1:2990771)
19:59:47       at <dyn core::ops::function::FnMut<(A,)>+Output = R as wasm_bindgen::closure::WasmClosure>::describe::invoke::h7e4a2734b30d0aeb (../wasm:/wasm/acvm_js.wasm-010f1132:1:3405742)
19:59:47       at closure647 externref shim (../wasm:/wasm/acvm_js.wasm-010f1132:1:3409914)
19:59:47       at __wbg_adapter_30 (../../../noir/packages/acvm_js/nodejs/acvm_js.js:531:10)
19:59:47       at real (../../../noir/packages/acvm_js/nodejs/acvm_js.js:125:20)
19:59:47       at AsyncResource.runMicrotask (../node:internal/process/task_queues:148:8)Error:
19:59:47       at ../../simulator/dest/private/acvm/acvm.js:33:23
19:59:47       at acvm (../../simulator/dest/private/acvm/acvm.js:13:36)
19:59:47       at SimulatorRecorderWrapper.#simulate (../../simulator/dest/private/circuit_recording/simulator_recorder_wrapper.js:25:22)
19:59:47       at executePrivateFunction (../../pxe/dest/contract_function_simulator/oracle/private_execution.js:27:33)
19:59:47       at PrivateExecutionOracle.callPrivateFunction (../../pxe/dest/contract_function_simulator/oracle/private_execution_oracle.js:293:38)
19:59:47       at Oracle.callPrivateFunction (../../pxe/dest/contract_function_simulator/oracle/oracle.js:217:55)Error:
19:59:47       at ../../simulator/dest/private/acvm/acvm.js:33:23
19:59:47       at acvm (../../simulator/dest/private/acvm/acvm.js:13:36)
19:59:47       at SimulatorRecorderWrapper.#simulate (../../simulator/dest/private/circuit_recording/simulator_recorder_wrapper.js:25:22)
19:59:47       at executePrivateFunction (../../pxe/dest/contract_function_simulator/oracle/private_execution.js:27:33)
19:59:47       at ContractFunctionSimulator.run (../../pxe/dest/contract_function_simulator/contract_function_simulator.js:70:37)
19:59:47       at PXEService.#executePrivate (../../pxe/dest/pxe_service/pxe_service.js:214:28)
19:59:47       at ../../pxe/dest/pxe_service/pxe_service.js:475:46
19:59:47       at ../../foundation/dest/queue/serial_queue.js:58:33
19:59:47       at FifoMemoryQueue.process (../../foundation/dest/queue/base_memory_queue.js:110:17)

Hitting an assertion failure in poseidon was especially unexpected. If we have an always failing array access we should not be suddenly hitting a constrain that replaces that access.

This PR simply changed all calls to remove_constraints to explicit calls to duplicated unconstrained functions. After doing this, there were no longer unexpected assertion failures and the e2e tests passed.

@vezenovm
Copy link
Contributor Author

vezenovm commented Sep 9, 2025

Closing as this has been resolved on the Noir side. See noir-lang/noir#7289 for more details.

@vezenovm vezenovm closed this Sep 9, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant