diff --git a/noir-projects/aztec-nr/aztec/src/context/private_context.nr b/noir-projects/aztec-nr/aztec/src/context/private_context.nr index 2901b62ea7f..5a492e7a35e 100644 --- a/noir-projects/aztec-nr/aztec/src/context/private_context.nr +++ b/noir-projects/aztec-nr/aztec/src/context/private_context.nr @@ -267,6 +267,9 @@ impl PrivateContext { // about secrets from other contracts. We therefore silo secret keys, and rely on the private kernel to // validate that we siloed secret key corresponds to correct siloing of the master secret key that hashes // to `pk_m_hash`. + + /// Safety: Kernels verify that the key validation request is valid and below we verify that a request + /// for the correct public key has been received. let request = unsafe { get_key_validation_request(pk_m_hash, key_index) }; assert(request.pk_m.hash() == pk_m_hash); @@ -388,11 +391,11 @@ impl PrivateContext { let mut is_static_call = is_static_call | self.inputs.call_context.is_static_call; let start_side_effect_counter = self.side_effect_counter; - // The oracle simulates the private call and returns the value of the side effects counter after execution of - // the call (which means that end_side_effect_counter - start_side_effect_counter is the number of side effects - // that took place), along with the hash of the return values. We validate these by requesting a private kernel - // iteration in which the return values are constrained to hash to `returns_hash` and the side effects counter - // to increment from start to end. + /// Safety: The oracle simulates the private call and returns the value of the side effects counter after + /// execution of the call (which means that end_side_effect_counter - start_side_effect_counter is + /// the number of side effects that took place), along with the hash of the return values. We validate these + /// by requesting a private kernel iteration in which the return values are constrained to hash + /// to `returns_hash` and the side effects counter to increment from start to end. let (end_side_effect_counter, returns_hash) = unsafe { call_private_function_internal( contract_address, @@ -491,12 +494,12 @@ impl PrivateContext { let counter = self.next_counter(); let mut is_static_call = is_static_call | self.inputs.call_context.is_static_call; - // TODO(https://github.com/AztecProtocol/aztec-packages/issues/8985): Fix this. - // WARNING: This is insecure and should be temporary! - // The oracle hashes the arguments and returns a new args_hash. - // new_args = [selector, ...old_args], so as to make it suitable to call the public dispatch function. - // We don't validate or compute it in the circuit because a) it's harder to do with slices, and - // b) this is only temporary. + /// Safety: TODO(https://github.com/AztecProtocol/aztec-packages/issues/8985): Fix this. + /// WARNING: This is insecure and should be temporary! + /// The oracle repacks the arguments and returns a new args_hash. + /// new_args = [selector, ...old_args], so as to make it suitable to call the public dispatch function. + /// We don't validate or compute it in the circuit because a) it's harder to do with slices, and + /// b) this is only temporary. let args_hash = unsafe { enqueue_public_function_call_internal( contract_address, @@ -547,12 +550,12 @@ impl PrivateContext { let counter = self.next_counter(); let mut is_static_call = is_static_call | self.inputs.call_context.is_static_call; - // TODO(https://github.com/AztecProtocol/aztec-packages/issues/8985): Fix this. - // WARNING: This is insecure and should be temporary! - // The oracle hashes the arguments and returns a new args_hash. - // new_args = [selector, ...old_args], so as to make it suitable to call the public dispatch function. - // We don't validate or compute it in the circuit because a) it's harder to do with slices, and - // b) this is only temporary. + /// Safety: TODO(https://github.com/AztecProtocol/aztec-packages/issues/8985): Fix this. + /// WARNING: This is insecure and should be temporary! + /// The oracle repacks the arguments and returns a new args_hash. + /// new_args = [selector, ...old_args], so as to make it suitable to call the public dispatch function. + /// We don't validate or compute it in the circuit because a) it's harder to do with slices, and + /// b) this is only temporary. let args_hash = unsafe { set_public_teardown_function_call_internal( contract_address, diff --git a/noir-projects/aztec-nr/aztec/src/context/public_context.nr b/noir-projects/aztec-nr/aztec/src/context/public_context.nr index d8095be42dd..ccb3526e65d 100644 --- a/noir-projects/aztec-nr/aztec/src/context/public_context.nr +++ b/noir-projects/aztec-nr/aztec/src/context/public_context.nr @@ -21,22 +21,22 @@ impl PublicContext { where T: Serialize, { - // AVM opcodes are constrained by the AVM itself + /// Safety: AVM opcodes are constrained by the AVM itself unsafe { emit_unencrypted_log(Serialize::serialize(log).as_slice()) }; } pub fn note_hash_exists(_self: Self, note_hash: Field, leaf_index: Field) -> bool { - // AVM opcodes are constrained by the AVM itself + /// Safety: AVM opcodes are constrained by the AVM itself unsafe { note_hash_exists(note_hash, leaf_index) } == 1 } pub fn l1_to_l2_msg_exists(_self: Self, msg_hash: Field, msg_leaf_index: Field) -> bool { - // AVM opcodes are constrained by the AVM itself + /// Safety: AVM opcodes are constrained by the AVM itself unsafe { l1_to_l2_msg_exists(msg_hash, msg_leaf_index) } == 1 } pub fn nullifier_exists(_self: Self, unsiloed_nullifier: Field, address: AztecAddress) -> bool { - // AVM opcodes are constrained by the AVM itself + /// Safety: AVM opcodes are constrained by the AVM itself unsafe { nullifier_exists(unsiloed_nullifier, address.to_field()) } == 1 } @@ -73,7 +73,7 @@ impl PublicContext { } pub fn message_portal(_self: &mut Self, recipient: EthAddress, content: Field) { - // AVM opcodes are constrained by the AVM itself + /// Safety: AVM opcodes are constrained by the AVM itself unsafe { send_l2_to_l1_msg(recipient, content) }; } @@ -114,29 +114,29 @@ impl PublicContext { } pub fn push_note_hash(_self: &mut Self, note_hash: Field) { - // AVM opcodes are constrained by the AVM itself + /// Safety: AVM opcodes are constrained by the AVM itself unsafe { emit_note_hash(note_hash) }; } pub fn push_nullifier(_self: &mut Self, nullifier: Field) { - // AVM opcodes are constrained by the AVM itself + /// Safety: AVM opcodes are constrained by the AVM itself unsafe { emit_nullifier(nullifier) }; } pub fn this_address(_self: Self) -> AztecAddress { - // AVM opcodes are constrained by the AVM itself + /// Safety: AVM opcodes are constrained by the AVM itself unsafe { address() } } pub fn msg_sender(_self: Self) -> AztecAddress { - // AVM opcodes are constrained by the AVM itself + /// Safety: AVM opcodes are constrained by the AVM itself unsafe { sender() } } pub fn selector(_self: Self) -> FunctionSelector { // The selector is the first element of the calldata when calling a public function through dispatch. - // AVM opcodes are constrained by the AVM itself. + /// Safety: AVM opcodes are constrained by the AVM itself let raw_selector: [Field; 1] = unsafe { calldata_copy(0, 1) }; FunctionSelector::from_field(raw_selector[0]) } @@ -148,70 +148,70 @@ impl PublicContext { self.args_hash.unwrap_unchecked() } pub fn transaction_fee(_self: Self) -> Field { - // AVM opcodes are constrained by the AVM itself + /// Safety: AVM opcodes are constrained by the AVM itself unsafe { transaction_fee() } } pub fn chain_id(_self: Self) -> Field { - // AVM opcodes are constrained by the AVM itself + /// Safety: AVM opcodes are constrained by the AVM itself unsafe { chain_id() } } pub fn version(_self: Self) -> Field { - // AVM opcodes are constrained by the AVM itself + /// Safety: AVM opcodes are constrained by the AVM itself unsafe { version() } } pub fn block_number(_self: Self) -> Field { - // AVM opcodes are constrained by the AVM itself + /// Safety: AVM opcodes are constrained by the AVM itself unsafe { block_number() } } pub fn timestamp(_self: Self) -> u64 { - // AVM opcodes are constrained by the AVM itself + /// Safety: AVM opcodes are constrained by the AVM itself unsafe { timestamp() } } pub fn fee_per_l2_gas(_self: Self) -> Field { - // AVM opcodes are constrained by the AVM itself + /// Safety: AVM opcodes are constrained by the AVM itself unsafe { fee_per_l2_gas() } } pub fn fee_per_da_gas(_self: Self) -> Field { - // AVM opcodes are constrained by the AVM itself + /// Safety: AVM opcodes are constrained by the AVM itself unsafe { fee_per_da_gas() } } pub fn l2_gas_left(_self: Self) -> Field { - // AVM opcodes are constrained by the AVM itself + /// Safety: AVM opcodes are constrained by the AVM itself unsafe { l2_gas_left() } } pub fn da_gas_left(_self: Self) -> Field { - // AVM opcodes are constrained by the AVM itself + /// Safety: AVM opcodes are constrained by the AVM itself unsafe { da_gas_left() } } pub fn is_static_call(_self: Self) -> bool { - // AVM opcodes are constrained by the AVM itself + /// Safety: AVM opcodes are constrained by the AVM itself unsafe { is_static_call() } == 1 } pub fn raw_storage_read(_self: Self, storage_slot: Field) -> [Field; N] { let mut out = [0; N]; for i in 0..N { - // AVM opcodes are constrained by the AVM itself + /// Safety: AVM opcodes are constrained by the AVM itself out[i] = unsafe { storage_read(storage_slot + i as Field) }; } out @@ -226,7 +226,7 @@ impl PublicContext { pub fn raw_storage_write(_self: Self, storage_slot: Field, values: [Field; N]) { for i in 0..N { - // AVM opcodes are constrained by the AVM itself + /// Safety: AVM opcodes are constrained by the AVM itself unsafe { storage_write(storage_slot + i as Field, values[i]) }; } } diff --git a/noir-projects/aztec-nr/aztec/src/encrypted_logs/encrypted_event_emission.nr b/noir-projects/aztec-nr/aztec/src/encrypted_logs/encrypted_event_emission.nr index a0fd3619a3c..452bbeccd4c 100644 --- a/noir-projects/aztec-nr/aztec/src/encrypted_logs/encrypted_event_emission.nr +++ b/noir-projects/aztec-nr/aztec/src/encrypted_logs/encrypted_event_emission.nr @@ -55,8 +55,8 @@ where Event: EventInterface, { |e: Event| { - // Unconstrained logs have both their content and encryption unconstrained - it could occur that the - // recipient is unable to decrypt the payload. + /// Safety: Unconstrained logs have both their content and encryption unconstrained - it could occur that the + /// recipient is unable to decrypt the payload. let encrypted_log = unsafe { compute_payload_unconstrained(*context, e, recipient, sender) }; context.emit_private_log(encrypted_log); diff --git a/noir-projects/aztec-nr/aztec/src/encrypted_logs/encrypted_note_emission.nr b/noir-projects/aztec-nr/aztec/src/encrypted_logs/encrypted_note_emission.nr index f7f6f7cf52f..a427c874759 100644 --- a/noir-projects/aztec-nr/aztec/src/encrypted_logs/encrypted_note_emission.nr +++ b/noir-projects/aztec-nr/aztec/src/encrypted_logs/encrypted_note_emission.nr @@ -73,16 +73,15 @@ where Note: NoteInterface, { |e: NoteEmission| { - // Unconstrained logs have both their content and encryption unconstrained - it could occur that the - // recipient is unable to decrypt the payload. - // Regarding the note hash counter, this is used for squashing. The kernel assumes that a given note can have - // more than one log and removes all of the matching ones, so all a malicious sender could do is either: cause - // for the log to be deleted when it shouldn't have (which is fine - they can already make the content be - // whatever), or cause for the log to not be deleted when it should have (which is also fine - it'll be a log - // for a note that doesn't exist). - // It's important here that we do not - // return the log from this function to the app, otherwise it could try to do stuff with it and then that might - // be wrong. + /// Safety: Unconstrained logs have both their content and encryption unconstrained - it could occur that + /// the recipient is unable to decrypt the payload. + /// Regarding the note hash counter, this is used for squashing. The kernel assumes that a given note can + /// have more than one log and removes all of the matching ones, so all a malicious sender could do is + /// either: cause for the log to be deleted when it shouldn't have (which is fine - they can already make + /// the content be whatever), or cause for the log to not be deleted when it should have (which is also fine + /// - it'll be a log for a note that doesn't exist). + /// It's important here that we do not return the log from this function to the app, otherwise it could + /// try to do stuff with it and then that might be wrong. let (encrypted_log, note_hash_counter) = unsafe { compute_payload_unconstrained(*context, e.note, recipient, sender) }; context.emit_raw_note_log(encrypted_log, note_hash_counter); diff --git a/noir-projects/aztec-nr/aztec/src/encrypted_logs/payload.nr b/noir-projects/aztec-nr/aztec/src/encrypted_logs/payload.nr index 3ea33631215..264f5ce4d6a 100644 --- a/noir-projects/aztec-nr/aztec/src/encrypted_logs/payload.nr +++ b/noir-projects/aztec-nr/aztec/src/encrypted_logs/payload.nr @@ -62,9 +62,9 @@ pub fn compute_private_log_payload( let encrypted: [u8; ENCRYPTED_PAYLOAD_SIZE_IN_BYTES] = compute_encrypted_log(contract_address, recipient, extended_plaintext); - // We assume that the sender wants for the recipient to find the tagged note, and therefore that they will cooperate - // and use the correct tag. Usage of a bad tag will result in the recipient not being able to find the note - // automatically. + /// Safety: We assume that the sender wants for the recipient to find the tagged note, and therefore that they + /// will cooperate and use the correct tag. Usage of a bad tag will result in the recipient not being able to + /// find the note automatically. let tag = unsafe { get_app_tag_as_sender(sender, recipient) }; increment_app_tagging_secret_index_as_sender(sender, recipient); @@ -82,9 +82,9 @@ pub fn compute_partial_public_log_payload( let encrypted: [u8; M - 32] = compute_encrypted_log(contract_address, recipient, extended_plaintext); - // We assume that the sender wants for the recipient to find the tagged note, and therefore that they will cooperate - // and use the correct tag. Usage of a bad tag will result in the recipient not being able to find the note - // automatically. + /// Safety: We assume that the sender wants for the recipient to find the tagged note, and therefore that they + /// will cooperate and use the correct tag. Usage of a bad tag will result in the recipient not being able to + /// find the note automatically. let tag = unsafe { get_app_tag_as_sender(sender, recipient) }; increment_app_tagging_secret_index_as_sender(sender, recipient); // Silo the tag with contract address. @@ -158,6 +158,8 @@ fn compute_encrypted_log( // Prepend the plaintext length as the first byte, then copy the plaintext itself starting from the second byte. // Fill the remaining bytes with random values to reach a fixed length of N. fn extend_private_log_plaintext(plaintext: [u8; P]) -> [u8; N] { + /// Safety: A malicious sender could reveal the whole contents of the encrypted log so trusting it to set + /// a random padding in plaintext is fine. let mut padded = unsafe { get_random_bytes() }; padded[0] = (P >> 8) as u8; padded[1] = P as u8; @@ -192,10 +194,11 @@ fn fr_to_fq(r: Field) -> Scalar { fn generate_ephemeral_key_pair() -> (Scalar, Point) { // @todo Need to draw randomness from the full domain of Fq not only Fr - // We use the randomness to preserve the privacy of both the sender and recipient via encryption, so a malicious - // sender could use non-random values to reveal the plaintext. But they already know it themselves anyway, and so - // the recipient already trusts them to not disclose this information. We can therefore assume that the sender will - // cooperate in the random value generation. + + /// Safety: We use the randomness to preserve the privacy of both the sender and recipient via encryption, so + /// a malicious sender could use non-random values to reveal the plaintext. But they already know it themselves + /// anyway, and so the recipient already trusts them to not disclose this information. We can therefore assume + /// that the sender will cooperate in the random value generation. let randomness = unsafe { random() }; // We use the unsafe version of `fr_to_fq` because multi_scalar_mul (called by derive_public_key) will constrain diff --git a/noir-projects/aztec-nr/aztec/src/history/note_inclusion.nr b/noir-projects/aztec-nr/aztec/src/history/note_inclusion.nr index 886801dea6f..87f34e3f150 100644 --- a/noir-projects/aztec-nr/aztec/src/history/note_inclusion.nr +++ b/noir-projects/aztec-nr/aztec/src/history/note_inclusion.nr @@ -19,6 +19,7 @@ impl ProveNoteInclusion for BlockHeader { { let note_hash = compute_note_hash_for_nullify(note); + /// Safety: The witness is only used as a "magical value" that makes the merkle proof below pass. Hence it's safe. let witness = unsafe { get_note_hash_membership_witness(self.global_variables.block_number as u32, note_hash) }; diff --git a/noir-projects/aztec-nr/aztec/src/history/nullifier_inclusion.nr b/noir-projects/aztec-nr/aztec/src/history/nullifier_inclusion.nr index f1278658b89..8aa051fe808 100644 --- a/noir-projects/aztec-nr/aztec/src/history/nullifier_inclusion.nr +++ b/noir-projects/aztec-nr/aztec/src/history/nullifier_inclusion.nr @@ -14,6 +14,7 @@ trait ProveNullifierInclusion { impl ProveNullifierInclusion for BlockHeader { fn prove_nullifier_inclusion(self, nullifier: Field) { // 1) Get the membership witness of the nullifier + /// Safety: The witness is only used as a "magical value" that makes the proof below pass. Hence it's safe. let witness = unsafe { get_nullifier_membership_witness(self.global_variables.block_number as u32, nullifier) }; diff --git a/noir-projects/aztec-nr/aztec/src/history/nullifier_non_inclusion.nr b/noir-projects/aztec-nr/aztec/src/history/nullifier_non_inclusion.nr index e40c199ea7d..2a786bafb26 100644 --- a/noir-projects/aztec-nr/aztec/src/history/nullifier_non_inclusion.nr +++ b/noir-projects/aztec-nr/aztec/src/history/nullifier_non_inclusion.nr @@ -16,6 +16,7 @@ trait ProveNullifierNonInclusion { impl ProveNullifierNonInclusion for BlockHeader { fn prove_nullifier_non_inclusion(self, nullifier: Field) { // 1) Get the membership witness of a low nullifier of the nullifier + /// Safety: The witness is only used as a "magical value" that makes the proof below pass. Hence it's safe. let witness = unsafe { get_low_nullifier_membership_witness( self.global_variables.block_number as u32, diff --git a/noir-projects/aztec-nr/aztec/src/history/public_storage.nr b/noir-projects/aztec-nr/aztec/src/history/public_storage.nr index 37cf287cde4..4195674e9ad 100644 --- a/noir-projects/aztec-nr/aztec/src/history/public_storage.nr +++ b/noir-projects/aztec-nr/aztec/src/history/public_storage.nr @@ -27,6 +27,7 @@ impl PublicStorageHistoricalRead for BlockHeader { ); // 2) Get the membership witness for the tree index. + /// Safety: The witness is only used as a "magical value" that makes the proof below pass. Hence it's safe. let witness = unsafe { get_public_data_witness( self.global_variables.block_number as u32, diff --git a/noir-projects/aztec-nr/aztec/src/keys/getters/mod.nr b/noir-projects/aztec-nr/aztec/src/keys/getters/mod.nr index d04a08baee2..5430aac2659 100644 --- a/noir-projects/aztec-nr/aztec/src/keys/getters/mod.nr +++ b/noir-projects/aztec-nr/aztec/src/keys/getters/mod.nr @@ -24,7 +24,7 @@ pub unconstrained fn get_ovsk_app(ovpk_m_hash: Field) -> Field { // keys at once since the constraints for reading them all are actually fewer than if we read them one at a time - any // read keys that are not required by the caller can simply be discarded. pub fn get_public_keys(account: AztecAddress) -> PublicKeys { - // Public keys are constrained by showing their inclusion in the address's preimage. + /// Safety: Public keys are constrained by showing their inclusion in the address's preimage. let (public_keys, partial_address) = unsafe { get_public_keys_and_partial_address(account) }; assert_eq( account, diff --git a/noir-projects/aztec-nr/aztec/src/messaging.nr b/noir-projects/aztec-nr/aztec/src/messaging.nr index 98a4110e628..4fd9b7a8012 100644 --- a/noir-projects/aztec-nr/aztec/src/messaging.nr +++ b/noir-projects/aztec-nr/aztec/src/messaging.nr @@ -31,6 +31,7 @@ pub fn process_l1_to_l2_message( // We prove that `message_hash` is in the tree by showing the derivation of the tree root, using a merkle path we // get from an oracle. + /// Safety: The witness is only used as a "magical value" that makes the merkle proof below pass. Hence it's safe. let (_leaf_index, sibling_path) = unsafe { get_l1_to_l2_membership_witness(contract_address, message_hash, secret) }; diff --git a/noir-projects/aztec-nr/aztec/src/note/note_getter/mod.nr b/noir-projects/aztec-nr/aztec/src/note/note_getter/mod.nr index c79c2e18730..02f94ef64c3 100644 --- a/noir-projects/aztec-nr/aztec/src/note/note_getter/mod.nr +++ b/noir-projects/aztec-nr/aztec/src/note/note_getter/mod.nr @@ -89,10 +89,9 @@ pub fn get_note( where Note: NoteInterface + NullifiableNote, { + /// Safety: Constraining that we got a valid note from the oracle is fairly straightforward: all we need to do + /// is check that the metadata is correct, and that the note exists. let note = unsafe { get_note_internal(storage_slot) }; - - // Constraining that we got a valid note from the oracle is fairly straightforward: all we need to do is check that - // the metadata is correct, and that the note exists. check_note_header(*context, storage_slot, note); let note_hash_for_read_request = compute_note_hash_for_read_request(note); @@ -109,6 +108,7 @@ pub fn get_notes( where Note: NoteInterface + NullifiableNote + Eq, { + /// Safety: The notes are constrained below. let opt_notes = unsafe { get_notes_internal(storage_slot, options) }; // We apply the constraints in a separate function instead of inlining them here to make it easier to test that diff --git a/noir-projects/aztec-nr/aztec/src/oracle/block_header.nr b/noir-projects/aztec-nr/aztec/src/oracle/block_header.nr index 3139b6d85a6..1f7d95863d7 100644 --- a/noir-projects/aztec-nr/aztec/src/oracle/block_header.nr +++ b/noir-projects/aztec-nr/aztec/src/oracle/block_header.nr @@ -35,6 +35,7 @@ pub fn get_block_header_at(block_number: u32, context: PrivateContext) -> BlockH ); // 3) Get the header hint of a given block from an oracle + /// Safety: The header is constrained to be in the archive tree below. let header = unsafe { get_block_header_at_internal(block_number) }; // 4) We make sure that the header hint we received from the oracle exists in the state tree and is the actual header @@ -61,6 +62,7 @@ fn constrain_get_block_header_at_internal( let block_hash = header_hint.hash(); // 2) Get the membership witness of the block in the archive tree + /// Safety: The witness is only used as a "magical value" that makes the merkle proof below pass. Hence it's safe. let witness = unsafe { get_archive_membership_witness(last_archive_block_number, block_hash) }; // 3) Check that the block is in the archive (i.e. the witness is valid) diff --git a/noir-projects/aztec-nr/aztec/src/oracle/enqueue_public_function_call.nr b/noir-projects/aztec-nr/aztec/src/oracle/enqueue_public_function_call.nr index 1bf5812d8b2..62a73c9f5d8 100644 --- a/noir-projects/aztec-nr/aztec/src/oracle/enqueue_public_function_call.nr +++ b/noir-projects/aztec-nr/aztec/src/oracle/enqueue_public_function_call.nr @@ -51,6 +51,8 @@ pub unconstrained fn set_public_teardown_function_call_internal( } pub fn notify_set_min_revertible_side_effect_counter(counter: u32) { + /// Safety: This oracle call returns nothing: we only call it for its side effects. It is therefore always safe + /// to call. unsafe { notify_set_min_revertible_side_effect_counter_oracle_wrapper(counter) }; } diff --git a/noir-projects/aztec-nr/aztec/src/oracle/get_contract_instance.nr b/noir-projects/aztec-nr/aztec/src/oracle/get_contract_instance.nr index 14066aba8ae..6c117ab0b89 100644 --- a/noir-projects/aztec-nr/aztec/src/oracle/get_contract_instance.nr +++ b/noir-projects/aztec-nr/aztec/src/oracle/get_contract_instance.nr @@ -18,10 +18,10 @@ unconstrained fn get_contract_instance_internal( // NOTE: this is for use in private only pub fn get_contract_instance(address: AztecAddress) -> ContractInstance { + /// Safety: The to_address function combines all values in the instance object to produce an address, + /// so by checking that we get the expected address we validate the entire struct. let instance = unsafe { ContractInstance::deserialize(get_contract_instance_internal(address)) }; - // The to_address function combines all values in the instance object to produce an address, so by checking that we - // get the expected address we validate the entire struct. assert_eq(instance.to_address(), address); instance @@ -59,6 +59,7 @@ pub unconstrained fn get_contract_instance_initialization_hash_internal_avm( } pub fn get_contract_instance_deployer_avm(address: AztecAddress) -> Option { + /// Safety: AVM opcodes are constrained by the AVM itself let (member, exists) = unsafe { get_contract_instance_deployer_internal_avm(address) }; if exists { Option::some(AztecAddress::from_field(member)) @@ -67,6 +68,7 @@ pub fn get_contract_instance_deployer_avm(address: AztecAddress) -> Option Option { + /// Safety: AVM opcodes are constrained by the AVM itself let (member, exists) = unsafe { get_contract_instance_class_id_internal_avm(address) }; if exists { Option::some(ContractClassId::from_field(member)) @@ -75,6 +77,7 @@ pub fn get_contract_instance_class_id_avm(address: AztecAddress) -> Option Option { + /// Safety: AVM opcodes are constrained by the AVM itself let (member, exists) = unsafe { get_contract_instance_initialization_hash_internal_avm(address) }; if exists { diff --git a/noir-projects/aztec-nr/aztec/src/oracle/notes.nr b/noir-projects/aztec-nr/aztec/src/oracle/notes.nr index 66b7c44af45..642e1281915 100644 --- a/noir-projects/aztec-nr/aztec/src/oracle/notes.nr +++ b/noir-projects/aztec-nr/aztec/src/oracle/notes.nr @@ -14,7 +14,8 @@ pub fn notify_created_note( note_hash: Field, counter: u32, ) { - // This oracle call returns nothing: we only call it for its side effects. It is therefore always safe to call. + /// Safety: This oracle call returns nothing: we only call it for its side effects. It is therefore always safe + /// to call. unsafe { notify_created_note_oracle_wrapper( storage_slot, @@ -30,7 +31,8 @@ pub fn notify_created_note( /// the same transaction. This note should only be removed to the non-volatile database if its nullifier is found in an /// actual block. pub fn notify_nullified_note(nullifier: Field, note_hash: Field, counter: u32) { - // This oracle call returns nothing: we only call it for its side effects. It is therefore always safe to call. + /// Safety: This oracle call returns nothing: we only call it for its side effects. It is therefore always safe + /// to call. unsafe { notify_nullified_note_oracle_wrapper(nullifier, note_hash, counter) }; } @@ -247,7 +249,8 @@ unconstrained fn get_indexed_tagging_secret_as_sender_oracle( /// otherwise e.g. a reverting transaction can cause the sender to accidentally skip indices and later produce notes /// that are not found by the recipient. pub fn increment_app_tagging_secret_index_as_sender(sender: AztecAddress, recipient: AztecAddress) { - // This oracle call returns nothing: we only call it for its side effects. It is therefore always safe to call. + /// Safety: This oracle call returns nothing: we only call it for its side effects. It is therefore always safe + /// to call. unsafe { increment_app_tagging_secret_index_as_sender_wrapper(sender, recipient); } @@ -269,7 +272,8 @@ unconstrained fn increment_app_tagging_secret_index_as_sender_oracle( /// Finds new notes that may have been sent to all registered accounts in PXE in the current contract and makes them available /// for later querying via the `get_notes` oracle. pub fn sync_notes() { - // This oracle call returns nothing: we only call it for its side effects. It is therefore always safe to call. + /// Safety: This oracle call returns nothing: we only call it for its side effects. It is therefore always safe + /// to call. unsafe { sync_notes_oracle_wrapper(); } diff --git a/noir-projects/aztec-nr/aztec/src/state_vars/private_mutable.nr b/noir-projects/aztec-nr/aztec/src/state_vars/private_mutable.nr index 4dd7d13dd2f..44a757b5b5b 100644 --- a/noir-projects/aztec-nr/aztec/src/state_vars/private_mutable.nr +++ b/noir-projects/aztec-nr/aztec/src/state_vars/private_mutable.nr @@ -85,16 +85,17 @@ where // docs:end:replace pub fn initialize_or_replace(self, note: &mut Note) -> NoteEmission { - // `check_nullifier_exists` is an unconstrained function - we can constrain a true value by providing an - // inclusion proof of the nullifier, but cannot constrain a false value since a non-inclusion proof would only - // be valid if done in public. - // Ultimately, this is not an issue ginen that we'll either: - // - initialize the state variable, which would fail if it was already initialized due to the duplicate - // nullifier, or - // - replace the current value, which would fail if it was not initialized since we wouldn't be able to produce - // an inclusion proof for the current note - // This means that an honest oracle will assist the prover to produce a valid proof, while a malicious oracle - // (i.e. one that returns an incorrect value for is_initialized) will simply fail to produce a proof. + /// Safety: `check_nullifier_exists` is an unconstrained function - we can constrain a true value + /// by providing an inclusion proof of the nullifier, but cannot constrain a false value since + /// a non-inclusion proof would only be valid if done in public. + /// Ultimately, this is not an issue given that we'll either: + /// - initialize the state variable, which would fail if it was already initialized due to the duplicate + /// nullifier, or + /// - replace the current value, which would fail if it was not initialized since we wouldn't be able + /// to produce an inclusion proof for the current note + /// This means that an honest oracle will assist the prover to produce a valid proof, while a malicious + /// oracle (i.e. one that returns an incorrect value for is_initialized) will simply fail to produce + /// a proof. let is_initialized = unsafe { check_nullifier_exists(self.compute_initialization_nullifier()) }; diff --git a/noir-projects/aztec-nr/aztec/src/state_vars/shared_mutable.nr b/noir-projects/aztec-nr/aztec/src/state_vars/shared_mutable.nr index 5e9a2f0a334..34ace291116 100644 --- a/noir-projects/aztec-nr/aztec/src/state_vars/shared_mutable.nr +++ b/noir-projects/aztec-nr/aztec/src/state_vars/shared_mutable.nr @@ -206,6 +206,7 @@ where // Instead, we get an oracle to provide us the correct values for both the value and delay changes, and instead // prove inclusion of their hash, which is both a much smaller proof (a single slot), and also independent of // the size of T. + /// Safety: The hints are checked to be a preimage of a hash obtained from constrained context. let (value_change_hint, delay_change_hint) = unsafe { get_public_storage_hints(address, self.storage_slot, historical_block_number) }; diff --git a/noir-projects/aztec-nr/aztec/src/utils/array/collapse.nr b/noir-projects/aztec-nr/aztec/src/utils/array/collapse.nr index 22ac88aeb33..0aa54da1451 100644 --- a/noir-projects/aztec-nr/aztec/src/utils/array/collapse.nr +++ b/noir-projects/aztec-nr/aztec/src/utils/array/collapse.nr @@ -9,11 +9,13 @@ pub fn collapse(input: [Option; N]) -> BoundedVec where T: Eq, { - // Computing the collpased BoundedVec would result in a very large number of constraints, since we'd need to loop + // Computing the collapsed BoundedVec would result in a very large number of constraints, since we'd need to loop // over the input array and conditionally write to a dynamic vec index, which is a very unfriendly pattern to the // proving backend. // Instead, we use an unconstrained function to produce the final collapsed array, along with some hints, and then // verify that the input and collapsed arrays are equivalent. + + /// Safety: The hints are verified by the `verify_collapse_hints` function. let (collapsed, collapsed_to_input_index_mapping) = unsafe { get_collapse_hints(input) }; verify_collapse_hints(input, collapsed, collapsed_to_input_index_mapping); collapsed diff --git a/noir-projects/aztec-nr/uint-note/src/uint_note.nr b/noir-projects/aztec-nr/uint-note/src/uint_note.nr index 45b1001932f..368f57bf30d 100644 --- a/noir-projects/aztec-nr/uint-note/src/uint_note.nr +++ b/noir-projects/aztec-nr/uint-note/src/uint_note.nr @@ -58,10 +58,10 @@ impl Eq for UintNote { impl UintNote { pub fn new(value: U128, owner: AztecAddress) -> Self { - // We use the randomness to preserve the privacy of the note recipient by preventing brute-forcing, so a - // malicious sender could use non-random values to make the note less private. But they already know the full - // note pre-image anyway, and so the recipient already trusts them to not disclose this information. We can - // therefore assume that the sender will cooperate in the random value generation. + /// Safety: We use the randomness to preserve the privacy of the note recipient by preventing brute-forcing, + /// so a malicious sender could use non-random values to make the note less private. But they already know + /// the full note pre-image anyway, and so the recipient already trusts them to not disclose this + /// information. We can therefore assume that the sender will cooperate in the random value generation. let randomness = unsafe { random() }; Self { value, owner, randomness, header: NoteHeader::empty() } } diff --git a/noir-projects/aztec-nr/value-note/src/value_note.nr b/noir-projects/aztec-nr/value-note/src/value_note.nr index 250a299f233..e5ebe068373 100644 --- a/noir-projects/aztec-nr/value-note/src/value_note.nr +++ b/noir-projects/aztec-nr/value-note/src/value_note.nr @@ -58,11 +58,12 @@ impl NullifiableNote for ValueNote { impl ValueNote { pub fn new(value: Field, owner: AztecAddress) -> Self { - // We use the randomness to preserve the privacy of the note recipient by preventing brute-forcing, so a - // malicious sender could use non-random values to make the note less private. But they already know the full - // note pre-image anyway, and so the recipient already trusts them to not disclose this information. We can - // therefore assume that the sender will cooperate in the random value generation. + /// Safety: We use the randomness to preserve the privacy of the note recipient by preventing brute-forcing, + /// so a malicious sender could use non-random values to make the note less private. But they already know + /// the full note pre-image anyway, and so the recipient already trusts them to not disclose this + /// information. We can therefore assume that the sender will cooperate in the random value generation. let randomness = unsafe { random() }; + let header = NoteHeader::empty(); ValueNote { value, owner, randomness, header } } diff --git a/noir-projects/noir-contracts/contracts/app_subscription_contract/src/subscription_note.nr b/noir-projects/noir-contracts/contracts/app_subscription_contract/src/subscription_note.nr index 65f1cdbbab5..4bcd36d90f3 100644 --- a/noir-projects/noir-contracts/contracts/app_subscription_contract/src/subscription_note.nr +++ b/noir-projects/noir-contracts/contracts/app_subscription_contract/src/subscription_note.nr @@ -44,10 +44,10 @@ impl NullifiableNote for SubscriptionNote { impl SubscriptionNote { pub fn new(owner: AztecAddress, expiry_block_number: Field, remaining_txs: Field) -> Self { - // We use the randomness to preserve the privacy of the note recipient by preventing brute-forcing, so a - // malicious sender could use non-random values to make the note less private. But they already know the full - // note pre-image anyway, and so the recipient already trusts them to not disclose this information. We can - // therefore assume that the sender will cooperate in the random value generation. + /// Safety: We use the randomness to preserve the privacy of the note recipient by preventing brute-forcing, + /// so a malicious sender could use non-random values to make the note less private. But they already know + /// the full note pre-image anyway, and so the recipient already trusts them to not disclose this + /// information. We can therefore assume that the sender will cooperate in the random value generation. let randomness = unsafe { random() }; Self { owner, expiry_block_number, remaining_txs, randomness, header: NoteHeader::empty() } } diff --git a/noir-projects/noir-contracts/contracts/easy_private_voting_contract/src/test/utils.nr b/noir-projects/noir-contracts/contracts/easy_private_voting_contract/src/test/utils.nr index 92917a7b023..4a531d2e6eb 100644 --- a/noir-projects/noir-contracts/contracts/easy_private_voting_contract/src/test/utils.nr +++ b/noir-projects/noir-contracts/contracts/easy_private_voting_contract/src/test/utils.nr @@ -2,10 +2,10 @@ use dep::aztec::{prelude::AztecAddress, test::helpers::test_environment::TestEnv use crate::EasyPrivateVoting; -pub fn setup() -> (&mut TestEnvironment, AztecAddress, AztecAddress) { - let mut env = unsafe { TestEnvironment::new() }; +pub unconstrained fn setup() -> (&mut TestEnvironment, AztecAddress, AztecAddress) { + let mut env = TestEnvironment::new(); - let admin = unsafe { env.create_account() }; + let admin = env.create_account(); let initializer_call_interface = EasyPrivateVoting::interface().constructor(admin); let voting_contract = unsafe { diff --git a/noir-projects/noir-contracts/contracts/nft_contract/src/main.nr b/noir-projects/noir-contracts/contracts/nft_contract/src/main.nr index b1d9ddacd5d..7a2fb059a01 100644 --- a/noir-projects/noir-contracts/contracts/nft_contract/src/main.nr +++ b/noir-projects/noir-contracts/contracts/nft_contract/src/main.nr @@ -203,6 +203,11 @@ contract NFT { // We create a setup payload with unpopulated/zero token id for 'to' // TODO(#7775): Manually fetching the randomness here is not great. If we decide to include randomness in all // notes we could just inject it in macros. + + /// Safety: We use the randomness to preserve the privacy of the note recipient by preventing brute-forcing, + /// so a malicious sender could use non-random values to make the note less private. But they already know + /// the full note pre-image anyway, and so the recipient already trusts them to not disclose this + /// information. We can therefore assume that the sender will cooperate in the random value generation. let note_randomness = unsafe { random() }; let note_setup_payload = NFTNote::setup_payload().new(to, note_randomness, to_note_slot); diff --git a/noir-projects/noir-contracts/contracts/nft_contract/src/types/nft_note.nr b/noir-projects/noir-contracts/contracts/nft_contract/src/types/nft_note.nr index 36a691be67f..fef53c69a0f 100644 --- a/noir-projects/noir-contracts/contracts/nft_contract/src/types/nft_note.nr +++ b/noir-projects/noir-contracts/contracts/nft_contract/src/types/nft_note.nr @@ -51,10 +51,10 @@ impl NullifiableNote for NFTNote { impl NFTNote { pub fn new(token_id: Field, owner: AztecAddress) -> Self { - // We use the randomness to preserve the privacy of the note recipient by preventing brute-forcing, so a - // malicious sender could use non-random values to make the note less private. But they already know the full - // note pre-image anyway, and so the recipient already trusts them to not disclose this information. We can - // therefore assume that the sender will cooperate in the random value generation. + /// Safety: We use the randomness to preserve the privacy of the note recipient by preventing brute-forcing, + /// so a malicious sender could use non-random values to make the note less private. But they already know + /// the full note pre-image anyway, and so the recipient already trusts them to not disclose this + /// information. We can therefore assume that the sender will cooperate in the random value generation. let randomness = unsafe { random() }; NFTNote { token_id, owner, randomness, header: NoteHeader::empty() } } diff --git a/noir-projects/noir-contracts/contracts/schnorr_account_contract/src/main.nr b/noir-projects/noir-contracts/contracts/schnorr_account_contract/src/main.nr index 09a935f95e3..43aed0e231b 100644 --- a/noir-projects/noir-contracts/contracts/schnorr_account_contract/src/main.nr +++ b/noir-projects/noir-contracts/contracts/schnorr_account_contract/src/main.nr @@ -63,7 +63,10 @@ contract SchnorrAccount { // Load public key from storage let storage = Storage::init(context); let public_key = storage.signing_public_key.get_note(); + // Load auth witness + /// Safety: The witness is only used as a "magical value" that makes the signature verification below pass. + /// Hence it's safe. let witness: [Field; 64] = unsafe { get_auth_witness(outer_hash) }; let mut signature: [u8; 64] = [0; 64]; for i in 0..64 { diff --git a/noir-projects/noir-contracts/contracts/schnorr_hardcoded_account_contract/src/main.nr b/noir-projects/noir-contracts/contracts/schnorr_hardcoded_account_contract/src/main.nr index 02582e3e097..e4365382f8d 100644 --- a/noir-projects/noir-contracts/contracts/schnorr_hardcoded_account_contract/src/main.nr +++ b/noir-projects/noir-contracts/contracts/schnorr_hardcoded_account_contract/src/main.nr @@ -38,6 +38,9 @@ contract SchnorrHardcodedAccount { #[contract_library_method] fn is_valid_impl(_context: &mut PrivateContext, outer_hash: Field) -> bool { // Load auth witness and format as an u8 array + + /// Safety: The witness is only used as a "magical value" that makes the signature verification below pass. + /// Hence it's safe. let witness: [Field; 64] = unsafe { get_auth_witness(outer_hash) }; let mut signature: [u8; 64] = [0; 64]; for i in 0..64 { diff --git a/noir-projects/noir-contracts/contracts/schnorr_single_key_account_contract/src/main.nr b/noir-projects/noir-contracts/contracts/schnorr_single_key_account_contract/src/main.nr index 3de21d464a1..7644e9f571c 100644 --- a/noir-projects/noir-contracts/contracts/schnorr_single_key_account_contract/src/main.nr +++ b/noir-projects/noir-contracts/contracts/schnorr_single_key_account_contract/src/main.nr @@ -29,6 +29,8 @@ contract SchnorrSingleKeyAccount { #[contract_library_method] fn is_valid_impl(context: &mut PrivateContext, outer_hash: Field) -> bool { + /// Safety: The witness is only used as a "magical value" that makes the signature verification + /// in `recover_address` and the address check below pass. Hence it's safe. let witness = unsafe { get_auth_witness(outer_hash) }; recover_address(outer_hash, witness).eq(context.this_address()) } diff --git a/noir-projects/noir-contracts/contracts/spam_contract/src/types/token_note.nr b/noir-projects/noir-contracts/contracts/spam_contract/src/types/token_note.nr index cf83b3eabf0..b4a97534fd6 100644 --- a/noir-projects/noir-contracts/contracts/spam_contract/src/types/token_note.nr +++ b/noir-projects/noir-contracts/contracts/spam_contract/src/types/token_note.nr @@ -63,10 +63,10 @@ impl Eq for TokenNote { impl OwnedNote for TokenNote { fn new(amount: U128, owner: AztecAddress) -> Self { - // We use the randomness to preserve the privacy of the note recipient by preventing brute-forcing, so a - // malicious sender could use non-random values to make the note less private. But they already know the full - // note pre-image anyway, and so the recipient already trusts them to not disclose this information. We can - // therefore assume that the sender will cooperate in the random value generation. + /// Safety: We use the randomness to preserve the privacy of the note recipient by preventing brute-forcing, + /// so a malicious sender could use non-random values to make the note less private. But they already know + /// the full note pre-image anyway, and so the recipient already trusts them to not disclose this + /// information. We can therefore assume that the sender will cooperate in the random value generation. let randomness = unsafe { random() }; Self { amount, owner, randomness, header: NoteHeader::empty() } } diff --git a/noir-projects/noir-contracts/contracts/token_blacklist_contract/src/types/token_note.nr b/noir-projects/noir-contracts/contracts/token_blacklist_contract/src/types/token_note.nr index 298835848f8..b1c644f50bf 100644 --- a/noir-projects/noir-contracts/contracts/token_blacklist_contract/src/types/token_note.nr +++ b/noir-projects/noir-contracts/contracts/token_blacklist_contract/src/types/token_note.nr @@ -61,10 +61,10 @@ impl Eq for TokenNote { impl OwnedNote for TokenNote { fn new(amount: U128, owner: AztecAddress) -> Self { - // We use the randomness to preserve the privacy of the note recipient by preventing brute-forcing, so a - // malicious sender could use non-random values to make the note less private. But they already know the full - // note pre-image anyway, and so the recipient already trusts them to not disclose this information. We can - // therefore assume that the sender will cooperate in the random value generation. + /// Safety: We use the randomness to preserve the privacy of the note recipient by preventing brute-forcing, + /// so a malicious sender could use non-random values to make the note less private. But they already know + /// the full note pre-image anyway, and so the recipient already trusts them to not disclose this + /// information. We can therefore assume that the sender will cooperate in the random value generation. let randomness = unsafe { random() }; Self { amount, owner, randomness, header: NoteHeader::empty() } } diff --git a/noir-projects/noir-contracts/contracts/token_contract/src/main.nr b/noir-projects/noir-contracts/contracts/token_contract/src/main.nr index d0e0f6f758a..52f63f7d03c 100644 --- a/noir-projects/noir-contracts/contracts/token_contract/src/main.nr +++ b/noir-projects/noir-contracts/contracts/token_contract/src/main.nr @@ -450,6 +450,11 @@ contract Token { // We create a setup payload with unpopulated/zero `amount` for 'to' // TODO(#7775): Manually fetching the randomness here is not great. If we decide to include randomness in all // notes we could just inject it in macros. + + /// Safety: We use the randomness to preserve the privacy of the note recipient by preventing brute-forcing, + /// so a malicious sender could use non-random values to make the note less private. But they already know + /// the full note pre-image anyway, and so the recipient already trusts them to not disclose this + /// information. We can therefore assume that the sender will cooperate in the random value generation. let note_randomness = unsafe { random() }; let note_setup_payload = UintNote::setup_payload().new(to, note_randomness, to_note_slot); diff --git a/noir-projects/noir-protocol-circuits/crates/blob/src/blob.nr b/noir-projects/noir-protocol-circuits/crates/blob/src/blob.nr index a4da452fe44..6b1903e8c5b 100644 --- a/noir-projects/noir-protocol-circuits/crates/blob/src/blob.nr +++ b/noir-projects/noir-protocol-circuits/crates/blob/src/blob.nr @@ -222,10 +222,8 @@ fn barycentric_evaluate_blob_at_z(z: F, ys: [F; FIELDS_PER_BLOB]) -> F { // /____ z - omega^i // i=0 let NUM_PARTIAL_SUMS = FIELDS_PER_BLOB / 8; - let partial_sums: [F; FIELDS_PER_BLOB / 8] = unsafe { - // Safety: This sum is checked by the following `BigNum::evaluate_quadratic_expression` calls. - __compute_partial_sums(fracs, ROOTS) - }; + /// Safety: This sum is checked by the following `BigNum::evaluate_quadratic_expression` calls. + let partial_sums: [F; FIELDS_PER_BLOB / 8] = unsafe { __compute_partial_sums(fracs, ROOTS) }; if !std::runtime::is_unconstrained() { // We split off the first term to check the initial sum @@ -312,10 +310,9 @@ fn compute_factor(z: F) -> F { let z_pow_d = t; - let factor = unsafe { - // Safety: We immediately check that this result is correct in the following `BigNum::evaluate_quadratic_expression` call. - __compute_factor_helper(z_pow_d) - }; + /// Safety: We immediately check that this result is correct in the following + /// `BigNum::evaluate_quadratic_expression` call. + let factor = unsafe { __compute_factor_helper(z_pow_d) }; // (z_pow_d - one) * (D_INV) - factor = 0 // z_pow_d * D_INV - D_INV - factor = 0 @@ -370,10 +367,9 @@ fn compute_fracs( ys: [F; FIELDS_PER_BLOB], ROOTS: [F; FIELDS_PER_BLOB], ) -> [F; FIELDS_PER_BLOB] { - let mut fracs: [F; FIELDS_PER_BLOB] = unsafe { - // Safety: We immediately constrain these `fracs` to be correct in the following call to `BigNum::evaluate_quadratic_expression`. - __compute_fracs(z, ys, ROOTS) - }; + /// Safety: We immediately constrain these `fracs` to be correct in the following call + /// to `BigNum::evaluate_quadratic_expression`. + let mut fracs: [F; FIELDS_PER_BLOB] = unsafe { __compute_fracs(z, ys, ROOTS) }; if !std::runtime::is_unconstrained() { for i in 0..FIELDS_PER_BLOB { diff --git a/noir-projects/noir-protocol-circuits/crates/types/src/abis/private_kernel_data.nr b/noir-projects/noir-protocol-circuits/crates/types/src/abis/private_kernel_data.nr index 5737775889e..b5022d3ec7f 100644 --- a/noir-projects/noir-protocol-circuits/crates/types/src/abis/private_kernel_data.nr +++ b/noir-projects/noir-protocol-circuits/crates/types/src/abis/private_kernel_data.nr @@ -23,6 +23,8 @@ impl PrivateKernelData { } else { self.vk_index }; + /// Safety: find_index_hint should return an index into allowed_indices where `index == index_in_allowed_list`. + /// The assertion below then verifies that the condition is met. let index_hint = unsafe { find_index_hint(allowed_indices, |index: u32| index == index_in_allowed_list) }; diff --git a/noir-projects/noir-protocol-circuits/crates/types/src/debug_log.nr b/noir-projects/noir-protocol-circuits/crates/types/src/debug_log.nr index 706139c8409..e2949cc6ec0 100644 --- a/noir-projects/noir-protocol-circuits/crates/types/src/debug_log.nr +++ b/noir-projects/noir-protocol-circuits/crates/types/src/debug_log.nr @@ -11,7 +11,8 @@ pub fn debug_log(msg: str) { /// debug_log_format("get_2(slot:{0}) =>\n\t0:{1}\n\t1:{2}", [storage_slot, note0_hash, note1_hash]); /// debug_log_format("whole array: {}", [e1, e2, e3, e4]); pub fn debug_log_format(msg: str, args: [Field; N]) { - // This oracle call returns nothing: we only call it for its side effects. It is therefore always safe to call. + /// Safety: This oracle call returns nothing: we only call it for its side effects. It is therefore always safe + /// to call. unsafe { debug_log_oracle_wrapper(msg, args) }; } diff --git a/noir-projects/noir-protocol-circuits/crates/types/src/merkle_tree/variable_merkle_tree.nr b/noir-projects/noir-protocol-circuits/crates/types/src/merkle_tree/variable_merkle_tree.nr index aa0973c11f4..afb3013553e 100644 --- a/noir-projects/noir-protocol-circuits/crates/types/src/merkle_tree/variable_merkle_tree.nr +++ b/noir-projects/noir-protocol-circuits/crates/types/src/merkle_tree/variable_merkle_tree.nr @@ -43,12 +43,10 @@ unconstrained fn get_next_power_exponent(input: u32, start: u8) -> u8 { /// # Returns /// The previous power of 2 fn get_prev_power_2(value: u32) -> u32 { - let next_power_exponent = unsafe { - //@safety This is a hint that we'll use to compute the next and previous powers of two, which we check to be - // larger and smaller than respectively. The get_next_power_exponent function happens to return exactly what - // we need. - get_next_power_exponent(value, 0) - }; + /// Safety: This is a hint that we'll use to compute the next and previous powers of two, which we check to be + /// larger and smaller than respectively. The get_next_power_exponent function happens to return exactly what + /// we need. + let next_power_exponent = unsafe { get_next_power_exponent(value, 0) }; let next_power_2 = 1 << next_power_exponent; let prev_power_2 = next_power_2 / 2; diff --git a/noir-projects/noir-protocol-circuits/crates/types/src/proof/vk_data.nr b/noir-projects/noir-protocol-circuits/crates/types/src/proof/vk_data.nr index cede48d01a5..efd4f9d1d8f 100644 --- a/noir-projects/noir-protocol-circuits/crates/types/src/proof/vk_data.nr +++ b/noir-projects/noir-protocol-circuits/crates/types/src/proof/vk_data.nr @@ -14,6 +14,8 @@ impl VkData { pub fn validate_in_vk_tree(self, vk_tree_root: Field, allowed_indices: [u32; N]) { self.vk.check_hash(); + /// Safety: find_index_hint should return an index into allowed_indices where + /// `index == index_in_allowed_list`. The assertion below then verifies that the condition is met. let index_hint = unsafe { find_index_hint(allowed_indices, |index: u32| index == self.vk_index) }; assert(index_hint < N, "Invalid vk index"); diff --git a/noir-projects/noir-protocol-circuits/crates/types/src/utils/arrays.nr b/noir-projects/noir-protocol-circuits/crates/types/src/utils/arrays.nr index 3f3c91c3885..d8093ba1fb6 100644 --- a/noir-projects/noir-protocol-circuits/crates/types/src/utils/arrays.nr +++ b/noir-projects/noir-protocol-circuits/crates/types/src/utils/arrays.nr @@ -99,11 +99,19 @@ where } // Helper function to count the number of non-empty elements in a validated array. -// Important: Only use it for validated arrays: validate_array(array) should be true. +// Important: Only use it for validated arrays where validate_array(array) returns true, +// which ensures that: +// 1. All elements before the first empty element are non-empty +// 2. All elements after and including the first empty element are empty +// 3. The array forms a contiguous sequence of non-empty elements followed by empty elements pub fn array_length(array: [T; N]) -> u32 where T: Empty + Eq, { + // We get the length by checking the index of the first empty element. + + /// Safety: This is safe because we have validated the array (see function doc above) and the emptiness + /// of the element and non-emptiness of the previous element is checked below. let length = unsafe { find_index_hint(array, |elem: T| is_empty(elem)) }; if length != 0 { assert(!is_empty(array[length - 1])); @@ -240,18 +248,18 @@ fn test_array_length_invalid_arrays() { } #[test] -fn find_index_greater_than_min() { +unconstrained fn find_index_greater_than_min() { let values = [10, 20, 30, 40]; let min = 22; - let index = unsafe { find_index_hint(values, |v: Field| min.lt(v)) }; + let index = find_index_hint(values, |v: Field| min.lt(v)); assert_eq(index, 2); } #[test] -fn find_index_not_found() { +unconstrained fn find_index_not_found() { let values = [10, 20, 30, 40]; let min = 100; - let index = unsafe { find_index_hint(values, |v: Field| min.lt(v)) }; + let index = find_index_hint(values, |v: Field| min.lt(v)); assert_eq(index, 4); } @@ -259,8 +267,8 @@ fn find_index_not_found() { fn test_array_concat() { let array0 = [1, 2, 3]; let array1 = [4, 5]; - let concated = array_concat(array0, array1); - assert_eq(concated, [1, 2, 3, 4, 5]); + let concatenated = array_concat(array0, array1); + assert_eq(concatenated, [1, 2, 3, 4, 5]); } #[test] diff --git a/noir-projects/noir-protocol-circuits/crates/types/src/utils/arrays/assert_combined_array.nr b/noir-projects/noir-protocol-circuits/crates/types/src/utils/arrays/assert_combined_array.nr index f2ad12fd38e..15b5326c2f1 100644 --- a/noir-projects/noir-protocol-circuits/crates/types/src/utils/arrays/assert_combined_array.nr +++ b/noir-projects/noir-protocol-circuits/crates/types/src/utils/arrays/assert_combined_array.nr @@ -90,9 +90,8 @@ mod tests { ); } - pub fn check_and_execute(self) { - let combined = - unsafe { combine_arrays(self.original_array_lt, self.original_array_gte) }; + pub unconstrained fn check_and_execute(self) { + let combined = combine_arrays(self.original_array_lt, self.original_array_gte); assert_eq(combined, self.combined_array); self.execute(); @@ -100,13 +99,13 @@ mod tests { } #[test] - fn assert_combined_array_empty_succeeds() { + unconstrained fn assert_combined_array_empty_succeeds() { let builder = TestBuilder::new_empty(); builder.check_and_execute(); } #[test] - fn assert_combined_array_succeeds() { + unconstrained fn assert_combined_array_succeeds() { let builder = TestBuilder::new(); builder.check_and_execute(); } diff --git a/noir-projects/noir-protocol-circuits/crates/types/src/utils/arrays/assert_combined_transformed_array.nr b/noir-projects/noir-protocol-circuits/crates/types/src/utils/arrays/assert_combined_transformed_array.nr index 082f33ebdd6..e96cc0ef5d7 100644 --- a/noir-projects/noir-protocol-circuits/crates/types/src/utils/arrays/assert_combined_transformed_array.nr +++ b/noir-projects/noir-protocol-circuits/crates/types/src/utils/arrays/assert_combined_transformed_array.nr @@ -115,14 +115,12 @@ mod tests { ); } - pub fn check_and_execute(self) { - let combined = unsafe { - combine_and_transform_arrays( - self.original_array_lt, - self.original_array_gte, - sum_two_values, - ) - }; + pub unconstrained fn check_and_execute(self) { + let combined = combine_and_transform_arrays( + self.original_array_lt, + self.original_array_gte, + sum_two_values, + ); assert_eq(combined, self.combined_array); self.execute(); @@ -130,19 +128,19 @@ mod tests { } #[test] - fn assert_combined_transformed_array_empty_succeeds() { + unconstrained fn assert_combined_transformed_array_empty_succeeds() { let builder = TestBuilder::new_empty(); builder.check_and_execute(); } #[test] - fn assert_combined_transformed_array_succeeds() { + unconstrained fn assert_combined_transformed_array_succeeds() { let builder = TestBuilder::new(); builder.check_and_execute(); } #[test(should_fail_with = "hinted item in the commbined array does not match")] - fn assert_combined_transformed_array_extra_item_fails() { + unconstrained fn assert_combined_transformed_array_extra_item_fails() { let mut builder = TestBuilder::new(); // Add random value to an empty item. @@ -152,7 +150,7 @@ mod tests { } #[test(should_fail_with = "hinted item in the commbined array does not match")] - fn assert_combined_transformed_array_missing_item_fails() { + unconstrained fn assert_combined_transformed_array_missing_item_fails() { let mut builder = TestBuilder::new(); // Clear the last item. @@ -162,7 +160,7 @@ mod tests { } #[test(should_fail_with = "hinted item in the commbined array does not match")] - fn assert_combined_transformed_array_unordered_fails() { + unconstrained fn assert_combined_transformed_array_unordered_fails() { let mut builder = TestBuilder::new(); // Swap the two items. diff --git a/noir-projects/noir-protocol-circuits/crates/types/src/utils/arrays/assert_exposed_sorted_transformed_value_array/get_order_hints.nr b/noir-projects/noir-protocol-circuits/crates/types/src/utils/arrays/assert_exposed_sorted_transformed_value_array/get_order_hints.nr index 25071de486d..f06d0c488c2 100644 --- a/noir-projects/noir-protocol-circuits/crates/types/src/utils/arrays/assert_exposed_sorted_transformed_value_array/get_order_hints.nr +++ b/noir-projects/noir-protocol-circuits/crates/types/src/utils/arrays/assert_exposed_sorted_transformed_value_array/get_order_hints.nr @@ -98,19 +98,19 @@ mod tests { } impl TestBuilder { - pub fn asc_to_equal(self, expected: [OrderHint; N]) { - let hints = unsafe { get_order_hints_asc(self.array) }; + pub unconstrained fn asc_to_equal(self, expected: [OrderHint; N]) { + let hints = get_order_hints_asc(self.array); assert_eq(hints, expected); } - pub fn desc_to_equal(self, expected: [OrderHint; N]) { - let hints = unsafe { get_order_hints_desc(self.array) }; + pub unconstrained fn desc_to_equal(self, expected: [OrderHint; N]) { + let hints = get_order_hints_desc(self.array); assert_eq(hints, expected); } } #[test] - fn get_order_hints_asc_full_non_empty() { + unconstrained fn get_order_hints_asc_full_non_empty() { let builder = TestBuilder::new(); let expected_hints = [ OrderHint { counter: 3, sorted_index: 2 }, @@ -121,7 +121,7 @@ mod tests { } #[test] - fn get_order_hints_asc_padded_empty() { + unconstrained fn get_order_hints_asc_padded_empty() { let builder = TestBuilder::new_padded(); let expected_hints = [ OrderHint { counter: 3, sorted_index: 2 }, @@ -134,7 +134,7 @@ mod tests { } #[test] - fn get_order_hints_desc_full_non_empty() { + unconstrained fn get_order_hints_desc_full_non_empty() { let builder = TestBuilder::new(); let expected_hints = [ OrderHint { counter: 9, sorted_index: 0 }, @@ -145,7 +145,7 @@ mod tests { } #[test] - fn get_order_hints_desc_padded_empty() { + unconstrained fn get_order_hints_desc_padded_empty() { let builder = TestBuilder::new_padded(); let expected_hints = [ OrderHint { counter: 9, sorted_index: 0 }, diff --git a/noir-projects/noir-protocol-circuits/crates/types/src/utils/arrays/assert_split_sorted_transformed_value_arrays/get_split_order_hints.nr b/noir-projects/noir-protocol-circuits/crates/types/src/utils/arrays/assert_split_sorted_transformed_value_arrays/get_split_order_hints.nr index 25a2d44ed9f..bd03e2a7289 100644 --- a/noir-projects/noir-protocol-circuits/crates/types/src/utils/arrays/assert_split_sorted_transformed_value_arrays/get_split_order_hints.nr +++ b/noir-projects/noir-protocol-circuits/crates/types/src/utils/arrays/assert_split_sorted_transformed_value_arrays/get_split_order_hints.nr @@ -131,13 +131,13 @@ mod tests { } impl TestBuilder { - pub fn asc_to_equal(self, expected: SplitOrderHints) { - let hints = unsafe { get_split_order_hints_asc(self.array, self.split_counter) }; + pub unconstrained fn asc_to_equal(self, expected: SplitOrderHints) { + let hints = get_split_order_hints_asc(self.array, self.split_counter); assert_eq(hints, expected); } - pub fn desc_to_equal(self, expected: SplitOrderHints) { - let hints = unsafe { get_split_order_hints_desc(self.array, self.split_counter) }; + pub unconstrained fn desc_to_equal(self, expected: SplitOrderHints) { + let hints = get_split_order_hints_desc(self.array, self.split_counter); assert_eq(hints, expected); } } @@ -145,7 +145,7 @@ mod tests { // asc #[test] - fn get_split_order_hints_asc_zero_split_counter_full() { + unconstrained fn get_split_order_hints_asc_zero_split_counter_full() { let builder = TestBuilder::new(); let expected_hints = SplitOrderHints { sorted_counters_lt: [0, 0, 0, 0, 0], @@ -156,7 +156,7 @@ mod tests { } #[test] - fn get_split_order_hints_asc_non_zero_split_counter_full() { + unconstrained fn get_split_order_hints_asc_non_zero_split_counter_full() { let mut builder = TestBuilder::new(); builder.split_counter = 9; @@ -169,7 +169,7 @@ mod tests { } #[test] - fn get_split_order_hints_asc_non_zero_split_counter_equal_full() { + unconstrained fn get_split_order_hints_asc_non_zero_split_counter_equal_full() { let mut builder = TestBuilder::new(); builder.split_counter = 11; @@ -182,7 +182,7 @@ mod tests { } #[test] - fn get_split_order_hints_asc_zero_split_counter_padded_empty() { + unconstrained fn get_split_order_hints_asc_zero_split_counter_padded_empty() { let builder = TestBuilder::new_padded(); let expected_hints = SplitOrderHints { @@ -194,7 +194,7 @@ mod tests { } #[test] - fn get_split_order_hints_asc_non_zero_split_counter_padded_empty() { + unconstrained fn get_split_order_hints_asc_non_zero_split_counter_padded_empty() { let mut builder = TestBuilder::new_padded(); builder.split_counter = 9; @@ -207,7 +207,7 @@ mod tests { } #[test] - fn get_split_order_hints_asc_non_zero_split_counter_equal_padded_empty() { + unconstrained fn get_split_order_hints_asc_non_zero_split_counter_equal_padded_empty() { let mut builder = TestBuilder::new_padded(); builder.split_counter = 11; @@ -222,7 +222,7 @@ mod tests { // desc #[test] - fn get_split_order_hints_desc_zero_split_counter_empty() { + unconstrained fn get_split_order_hints_desc_zero_split_counter_empty() { let builder = TestBuilder::new(); let expected_hints = SplitOrderHints { sorted_counters_lt: [0, 0, 0, 0, 0], @@ -233,7 +233,7 @@ mod tests { } #[test] - fn get_split_order_hints_desc_non_zero_split_counter_empty() { + unconstrained fn get_split_order_hints_desc_non_zero_split_counter_empty() { let mut builder = TestBuilder::new(); builder.split_counter = 9; @@ -246,7 +246,7 @@ mod tests { } #[test] - fn get_split_order_hints_desc_non_zero_split_counter_equal_empty() { + unconstrained fn get_split_order_hints_desc_non_zero_split_counter_equal_empty() { let mut builder = TestBuilder::new(); builder.split_counter = 11; @@ -259,7 +259,7 @@ mod tests { } #[test] - fn get_split_order_hints_desc_zero_split_counter_padded_empty() { + unconstrained fn get_split_order_hints_desc_zero_split_counter_padded_empty() { let builder = TestBuilder::new_padded(); let expected_hints = SplitOrderHints { sorted_counters_lt: [0, 0, 0, 0, 0, 0, 0], @@ -270,7 +270,7 @@ mod tests { } #[test] - fn get_split_order_hints_desc_non_zero_split_counter_padded_empty() { + unconstrained fn get_split_order_hints_desc_non_zero_split_counter_padded_empty() { let mut builder = TestBuilder::new_padded(); builder.split_counter = 9; @@ -283,7 +283,7 @@ mod tests { } #[test] - fn get_split_order_hints_desc_non_zero_split_counter_equal_padded_empty() { + unconstrained fn get_split_order_hints_desc_non_zero_split_counter_equal_padded_empty() { let mut builder = TestBuilder::new_padded(); builder.split_counter = 11; diff --git a/noir-projects/noir-protocol-circuits/crates/types/src/utils/arrays/assert_split_transformed_value_arrays.nr b/noir-projects/noir-protocol-circuits/crates/types/src/utils/arrays/assert_split_transformed_value_arrays.nr index 105ce5372b6..9b876ca98ff 100644 --- a/noir-projects/noir-protocol-circuits/crates/types/src/utils/arrays/assert_split_transformed_value_arrays.nr +++ b/noir-projects/noir-protocol-circuits/crates/types/src/utils/arrays/assert_split_transformed_value_arrays.nr @@ -59,6 +59,7 @@ where T: Ordered + Empty + Eq, S: Empty + Eq, { + /// Safety: The value is constrained by `assert_split_transformed_value_arrays_with_hint`. let num_non_revertibles = unsafe { find_index_hint(sorted_array, |n: T| n.counter() >= split_counter) }; diff --git a/noir-projects/noir-protocol-circuits/crates/types/src/utils/arrays/get_sorted_result.nr b/noir-projects/noir-protocol-circuits/crates/types/src/utils/arrays/get_sorted_result.nr index 6d0a31443b5..4bad623559d 100644 --- a/noir-projects/noir-protocol-circuits/crates/types/src/utils/arrays/get_sorted_result.nr +++ b/noir-projects/noir-protocol-circuits/crates/types/src/utils/arrays/get_sorted_result.nr @@ -24,37 +24,35 @@ pub unconstrained fn get_sorted_result( } #[test] -fn get_sorted_hints_asc_non_padded() { +unconstrained fn get_sorted_hints_asc_non_padded() { let values = [40, 60, 20, 50]; - let res = unsafe { get_sorted_result(values, |a: u32, b: u32| a < b) }; + let res = get_sorted_result(values, |a: u32, b: u32| a < b); assert_eq(res.sorted_array, [20, 40, 50, 60]); assert_eq(res.sorted_index_hints, [1, 3, 0, 2]); assert_eq(res.original_index_hints, [2, 0, 3, 1]); } #[test] -fn get_sorted_hints_desc_non_padded() { +unconstrained fn get_sorted_hints_desc_non_padded() { let values = [40, 20, 60, 50]; - let res = unsafe { get_sorted_result(values, |a: u32, b: u32| b < a) }; + let res = get_sorted_result(values, |a: u32, b: u32| b < a); assert_eq(res.sorted_array, [60, 50, 40, 20]); assert_eq(res.sorted_index_hints, [2, 3, 0, 1]); } #[test] -fn get_sorted_hints_asc_padded() { +unconstrained fn get_sorted_hints_asc_padded() { let values = [40, 60, 20, 50, 0, 0]; - let res = - unsafe { get_sorted_result(values, |a: u32, b: u32| (a != 0) & ((b == 0) | (a < b))) }; + let res = get_sorted_result(values, |a: u32, b: u32| (a != 0) & ((b == 0) | (a < b))); assert_eq(res.sorted_array, [20, 40, 50, 60, 0, 0]); assert_eq(res.sorted_index_hints, [1, 3, 0, 2, 4, 5]); assert_eq(res.original_index_hints, [2, 0, 3, 1, 4, 5]); } #[test] -fn get_sorted_hints_desc_padded() { +unconstrained fn get_sorted_hints_desc_padded() { let values = [40, 60, 20, 50, 0, 0]; - let res = - unsafe { get_sorted_result(values, |a: u32, b: u32| (a != 0) & ((b == 0) | (b < a))) }; + let res = get_sorted_result(values, |a: u32, b: u32| (a != 0) & ((b == 0) | (b < a))); assert_eq(res.sorted_array, [60, 50, 40, 20, 0, 0]); assert_eq(res.sorted_index_hints, [2, 0, 3, 1, 4, 5]); assert_eq(res.original_index_hints, [1, 3, 0, 2, 4, 5]); diff --git a/noir-projects/noir-protocol-circuits/crates/types/src/utils/arrays/get_sorted_tuple.nr b/noir-projects/noir-protocol-circuits/crates/types/src/utils/arrays/get_sorted_tuple.nr index 569983a9344..05286963796 100644 --- a/noir-projects/noir-protocol-circuits/crates/types/src/utils/arrays/get_sorted_tuple.nr +++ b/noir-projects/noir-protocol-circuits/crates/types/src/utils/arrays/get_sorted_tuple.nr @@ -20,7 +20,7 @@ pub unconstrained fn get_sorted_tuple( } #[test] -fn get_sorted_tuple_asc() { +unconstrained fn get_sorted_tuple_asc() { let original = [3, 2, 9, 5]; let expected = [ SortedTuple { elem: 2, original_index: 1 }, @@ -28,7 +28,7 @@ fn get_sorted_tuple_asc() { SortedTuple { elem: 5, original_index: 3 }, SortedTuple { elem: 9, original_index: 2 }, ]; - let sorted = unsafe { get_sorted_tuple(original, |a: u64, b: u64| a < b) }; + let sorted = get_sorted_tuple(original, |a: u64, b: u64| a < b); for i in 0..4 { assert_eq(sorted[i].elem, expected[i].elem); assert_eq(sorted[i].original_index, expected[i].original_index); diff --git a/noir-projects/noir-protocol-circuits/crates/types/src/utils/arrays/sort_by.nr b/noir-projects/noir-protocol-circuits/crates/types/src/utils/arrays/sort_by.nr index f8eaddc7a23..6a6c4ffe404 100644 --- a/noir-projects/noir-protocol-circuits/crates/types/src/utils/arrays/sort_by.nr +++ b/noir-projects/noir-protocol-circuits/crates/types/src/utils/arrays/sort_by.nr @@ -8,10 +8,10 @@ pub unconstrained fn sort_by( ordering: fn[Env](T, T) -> bool, ) -> [T; N] { let mut result = array; - let sorted_index = unsafe { get_sorting_index(array, ordering) }; + let sorted_index = get_sorting_index(array, ordering); // Ensure the indexes are correct for i in 0..N { - let pos = unsafe { find_index_hint(sorted_index, |index: u32| index == i) }; + let pos = find_index_hint(sorted_index, |index: u32| index == i); assert(sorted_index[pos] == i); } // Sort the array using the indexes diff --git a/noir-projects/noir-protocol-circuits/crates/types/src/utils/arrays/sort_by_counter.nr b/noir-projects/noir-protocol-circuits/crates/types/src/utils/arrays/sort_by_counter.nr index d4cd7176d87..04b0d5ed7e3 100644 --- a/noir-projects/noir-protocol-circuits/crates/types/src/utils/arrays/sort_by_counter.nr +++ b/noir-projects/noir-protocol-circuits/crates/types/src/utils/arrays/sort_by_counter.nr @@ -37,26 +37,26 @@ mod tests { }, }; - fn asc_values_to_equal(values: [u32; N], expected: [u32; N]) { + unconstrained fn asc_values_to_equal(values: [u32; N], expected: [u32; N]) { let items = values.map(|value| TestValue { value: value as Field, counter: value }); - let sorted = unsafe { sort_by_counter_asc(items).map(|item: TestValue| item.counter) }; + let sorted = sort_by_counter_asc(items).map(|item: TestValue| item.counter); assert_eq(sorted, expected); } - fn desc_values_to_equal(values: [u32; N], expected: [u32; N]) { + unconstrained fn desc_values_to_equal(values: [u32; N], expected: [u32; N]) { let items = values.map(|value| TestValue { value: value as Field, counter: value }); - let sorted = unsafe { sort_by_counter_desc(items).map(|item: TestValue| item.counter) }; + let sorted = sort_by_counter_desc(items).map(|item: TestValue| item.counter); assert_eq(sorted, expected); } - fn compare_test_items_asc(value_1: u32, value_2: u32) -> bool { + unconstrained fn compare_test_items_asc(value_1: u32, value_2: u32) -> bool { compare_by_counter_empty_padded_asc( TestValue { value: value_1 as Field, counter: value_1 }, TestValue { value: value_2 as Field, counter: value_2 }, ) } - fn compare_test_items_desc(value_1: u32, value_2: u32) -> bool { + unconstrained fn compare_test_items_desc(value_1: u32, value_2: u32) -> bool { compare_by_counter_empty_padded_desc( TestValue { value: value_1 as Field, counter: value_1 }, TestValue { value: value_2 as Field, counter: value_2 }, @@ -64,7 +64,7 @@ mod tests { } #[test] - fn compare_by_counter_empty_padded_asc_expected() { + unconstrained fn compare_by_counter_empty_padded_asc_expected() { assert_eq(compare_test_items_asc(1, 2), true); assert_eq(compare_test_items_asc(1, 1), false); assert_eq(compare_test_items_asc(2, 1), false); @@ -72,7 +72,7 @@ mod tests { } #[test] - fn compare_by_counter_empty_padded_desc_expected() { + unconstrained fn compare_by_counter_empty_padded_desc_expected() { assert_eq(compare_test_items_desc(1, 2), false); assert_eq(compare_test_items_desc(1, 1), true); assert_eq(compare_test_items_desc(2, 1), true); @@ -80,27 +80,27 @@ mod tests { } #[test] - fn sort_by_counter_asc_full_non_empty() { + unconstrained fn sort_by_counter_asc_full_non_empty() { asc_values_to_equal([4, 2, 1, 3, 5], [1, 2, 3, 4, 5]); } #[test] - fn sort_by_counter_desc_full_non_empty() { + unconstrained fn sort_by_counter_desc_full_non_empty() { desc_values_to_equal([4, 2, 1, 3, 5], [5, 4, 3, 2, 1]); } #[test] - fn sort_by_counter_asc_padded_empty() { + unconstrained fn sort_by_counter_asc_padded_empty() { asc_values_to_equal([4, 2, 0, 0, 1, 0, 3, 5], [1, 2, 3, 4, 5, 0, 0, 0]); } #[test] - fn sort_by_counter_desc_padded_empty() { + unconstrained fn sort_by_counter_desc_padded_empty() { desc_values_to_equal([4, 2, 0, 0, 1, 0, 3, 5], [5, 4, 3, 2, 1, 0, 0, 0]); } #[test] - fn sort_by_counter_asc_with_zero_counters() { + unconstrained fn sort_by_counter_asc_with_zero_counters() { let original = [ TestValue { value: 55, counter: 1 }, TestValue { value: 11, counter: 0 }, @@ -119,12 +119,12 @@ mod tests { TestValue::empty(), TestValue::empty(), ]; - let sorted = unsafe { sort_by_counter_asc(original) }; + let sorted = sort_by_counter_asc(original); assert_eq(sorted, expected); } #[test] - fn sort_by_counter_desc_with_zero_counters() { + unconstrained fn sort_by_counter_desc_with_zero_counters() { let original = [ TestValue { value: 55, counter: 1 }, TestValue { value: 11, counter: 0 }, @@ -143,7 +143,7 @@ mod tests { TestValue::empty(), TestValue::empty(), ]; - let sorted = unsafe { sort_by_counter_desc(original) }; + let sorted = sort_by_counter_desc(original); assert_eq(sorted, expected); } }