From 286e708c1016d60278060bb01f5d997f9a0bdfba Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nicol=C3=A1s=20Venturo?= Date: Mon, 19 Feb 2024 08:35:32 -0300 Subject: [PATCH] feat!: autogenerate compute_note_hash_and_nullifier (#4610) Closes #2918. This adds a new macro function that processes the unresolved trait impls and injects a new function before name resolution takes place. This lets us leverage the parser and write the function in Noir instead of having to deal with more complicated processed objects. In order to find all of the note types we look for impls of the `NoteInterface` trait. This is a bit more involved than it seems, since other crates may also have structs that impl this trait, and those will have already been processed. We rely on the fact that the contract crate is processed last, and search for external crate impls via the NodeInterner and internal ones in the unresolved functions. This method is robust as long as we do the contract crate after all of its imports, which holds because the imports are recursively collected from the root crate. I also added a small escape hatch mechanism by skipping any code generation if the function is already defined by the user. This could use some polishing since it is possible for the user to e.g. provide the function but get the arity or parameter types wrong, in which case they'd get a duplicate definition error. Diagnosis and error messages can be improved here (https://github.com/AztecProtocol/aztec-packages/issues/4647), but I think a simple mechanism is sufficient for now. --- ### Minor issues - One of the function arguments is a fixed-size array, which should be as big as the largest note length. We are not yet pulling the note length (https://github.com/AztecProtocol/aztec-packages/issues/4649), so I'm passing a hardcoded value for now. 12 Fields ought to be enough for anyone. - Doing this introduces an implicit dependency on `AztecAddress` on all contracts. This won't be an issue once https://github.com/AztecProtocol/aztec-packages/pull/4496 is in, but I did have to manually add it to some of the account contracts for now. - I created a new macro function that is called on each crate after collection but before resolution. Due to some internal compilers shenanigans (mostly who holds mut references to what) I chose to specialize this function so that for now it only passes the collected unresolved functions, making it a bit niche for the current use case. @vezenovm and I are planning to generalize this down the road (https://github.com/AztecProtocol/aztec-packages/issues/4653). - I'm now importing in the macro from some places that were not previously used, notably the HIR and Noir errors. I am not sure if we might want to pull those differently - I simply imported what I needed. - I also introduced some getters to provide mutable access to private fields of the HIR Context and CrateDefMap. This is because we need to modify the contract module in the def map by declaring the new function (which is how we get things like duplicate definition detection). We're already mutating the HIR Context in the macros, so also mutating its members doesn't feel like a stretch. - Finally, I don't know enough about how Noir errors work to know how to produce a useful `Location` value for the new function, if indeed that can be done. Providing an empty span seems to at least not cause weird errors on the LSP plugin, so that's how I left it for now. --- avm-transpiler/Cargo.lock | 1 + boxes/blank/src/contracts/src/main.nr | 13 - boxes/token/src/contracts/src/main.nr | 27 +- .../src/contracts/src/types/token_note.nr | 22 +- .../contracts/references/storage/main.md | 19 -- .../resources/common_patterns/main.md | 11 - .../writing_contracts/events/emit_event.md | 10 +- .../storage/define_storage.md | 8 - .../developers/debugging/aztecnr-errors.md | 10 - .../aztecnr-getting-started.md | 10 +- .../token_portal/withdrawing_to_l1.md | 7 - .../writing_private_voting_contract.md | 10 - .../tutorials/writing_token_contract.md | 19 -- docs/docs/misc/migration_notes.md | 8 + .../aztec-nr/value-note/src/balance_utils.nr | 2 +- .../contracts/avm_test_contract/src/main.nr | 12 - .../benchmarking_contract/src/main.nr | 13 +- .../contracts/card_game_contract/src/cards.nr | 2 +- .../contracts/card_game_contract/src/main.nr | 14 - .../contracts/child_contract/src/main.nr | 13 +- .../contracts/counter_contract/src/main.nr | 13 - .../docs_example_contract/src/main.nr | 14 +- .../easy_private_token_contract/src/main.nr | 19 +- .../easy_private_voting_contract/src/main.nr | 11 - .../ecdsa_account_contract/src/main.nr | 21 +- .../contracts/escrow_contract/src/main.nr | 12 - .../inclusion_proofs_contract/src/main.nr | 16 +- .../contracts/lending_contract/src/main.nr | 11 - .../pending_commitments_contract/src/main.nr | 15 - .../contracts/price_feed_contract/src/main.nr | 11 - .../schnorr_account_contract/src/main.nr | 19 -- .../src/main.nr | 2 +- .../src/main.nr | 2 +- .../contracts/slow_tree_contract/src/main.nr | 12 +- .../stateful_test_contract/src/main.nr | 11 - .../contracts/test_contract/src/main.nr | 22 +- .../token_blacklist_contract/src/main.nr | 25 +- .../token_bridge_contract/src/main.nr | 13 - .../contracts/token_contract/src/main.nr | 27 +- .../contracts/uniswap_contract/src/main.nr | 11 - noir/Cargo.lock | 1 + noir/aztec_macros/Cargo.toml | 1 + noir/aztec_macros/src/lib.rs | 228 +++++++++++-- .../src/hir/def_collector/dc_crate.rs | 9 + .../noirc_frontend/src/hir/def_map/mod.rs | 5 + noir/compiler/noirc_frontend/src/hir/mod.rs | 4 + noir/compiler/noirc_frontend/src/lib.rs | 11 + .../noirc_frontend/src/node_interner.rs | 2 +- noir/noirc_macros/src/lib.rs | 12 + .../nargo_fmt/tests/expected/contract.nr | 11 - .../tooling/nargo_fmt/tests/input/contract.nr | 11 - .../__snapshots__/index.test.ts.snap | 302 ++++++++--------- .../src/class-registerer/index.ts | 2 +- .../__snapshots__/index.test.ts.snap | 312 +++++++++--------- .../src/instance-deployer/index.ts | 2 +- .../simulator/src/client/simulator.test.ts | 6 +- 56 files changed, 586 insertions(+), 851 deletions(-) diff --git a/avm-transpiler/Cargo.lock b/avm-transpiler/Cargo.lock index 7f2f7804f42..2a7ac18c9c2 100644 --- a/avm-transpiler/Cargo.lock +++ b/avm-transpiler/Cargo.lock @@ -319,6 +319,7 @@ version = "0.24.0" dependencies = [ "convert_case", "iter-extended", + "noirc_errors", "noirc_frontend", ] diff --git a/boxes/blank/src/contracts/src/main.nr b/boxes/blank/src/contracts/src/main.nr index d544c909a97..89c80306529 100644 --- a/boxes/blank/src/contracts/src/main.nr +++ b/boxes/blank/src/contracts/src/main.nr @@ -15,17 +15,4 @@ contract Blank { [pub_key.x, pub_key.y] } - - // A function which needs to be implemented by every contract working with storage. Replace it's content with your - // own logic once you start working with private storage. - // TODO: Remove this placeholder once https://github.com/AztecProtocol/aztec-packages/issues/2918 is implemented. - unconstrained fn compute_note_hash_and_nullifier( - contract_address: AztecAddress, - nonce: Field, - storage_slot: Field, - note_type_id: Field, - serialized_note: [Field; 0] - ) -> pub [Field; 4] { - [0, 0, 0, 0] - } } diff --git a/boxes/token/src/contracts/src/main.nr b/boxes/token/src/contracts/src/main.nr index f48b637ef6d..3c056a28ec0 100644 --- a/boxes/token/src/contracts/src/main.nr +++ b/boxes/token/src/contracts/src/main.nr @@ -41,7 +41,7 @@ contract Token { use crate::types::{ transparent_note::TransparentNote, - token_note::{TokenNote, TOKEN_NOTE_LEN}, + token_note::TokenNote, balances_map::BalancesMap }; // docs:end::imports @@ -379,30 +379,5 @@ contract Token { storage.public_balances.at(owner).read().value } // docs:end:balance_of_public - - // docs:start:compute_note_hash_and_nullifier - // Computes note hash and nullifier. - // Note 1: Needs to be defined by every contract producing logs. - // Note 2: Having it in all the contracts gives us the ability to compute the note hash and nullifier differently for different kind of notes. - unconstrained fn compute_note_hash_and_nullifier( - contract_address: AztecAddress, - nonce: Field, - storage_slot: Field, - note_type_id: Field, - serialized_note: [Field; TOKEN_NOTE_LEN] - ) -> pub [Field; 4] { - let note_header = NoteHeader::new(contract_address, nonce, storage_slot); - - if (note_type_id == TransparentNote::get_note_type_id()) { - note_utils::compute_note_hash_and_nullifier( - TransparentNote::deserialize_content, - note_header, - serialized_note - ) - } else { - note_utils::compute_note_hash_and_nullifier(TokenNote::deserialize_content, note_header, serialized_note) - } - } - // docs:end:compute_note_hash_and_nullifier } // docs:end:token_all diff --git a/boxes/token/src/contracts/src/types/token_note.nr b/boxes/token/src/contracts/src/types/token_note.nr index 7dfe681f395..2be2ecb3e00 100644 --- a/boxes/token/src/contracts/src/types/token_note.nr +++ b/boxes/token/src/contracts/src/types/token_note.nr @@ -1,23 +1,9 @@ use dep::aztec::{ - protocol_types::{ - address::AztecAddress, - constants::MAX_READ_REQUESTS_PER_CALL - }, - note::{ - note_header::NoteHeader, - note_interface::NoteInterface, - utils::compute_note_hash_for_consumption, - }, - context::PrivateContext, - state_vars::set::Set, - log::emit_encrypted_log, - hash::pedersen_hash, -}; -use dep::aztec::oracle::{ - rand::rand, - nullifier_key::get_nullifier_secret_key, - get_public_key::get_public_key, + protocol_types::{address::AztecAddress, constants::MAX_READ_REQUESTS_PER_CALL}, + note::{note_header::NoteHeader, note_interface::NoteInterface, utils::compute_note_hash_for_consumption}, + context::PrivateContext, state_vars::set::Set, log::emit_encrypted_log, hash::pedersen_hash }; +use dep::aztec::oracle::{rand::rand, nullifier_key::get_nullifier_secret_key, get_public_key::get_public_key}; use dep::safe_math::SafeU120; use dep::std::option::Option; diff --git a/docs/docs/developers/contracts/references/storage/main.md b/docs/docs/developers/contracts/references/storage/main.md index 6bc1f653915..f8260d0313f 100644 --- a/docs/docs/developers/contracts/references/storage/main.md +++ b/docs/docs/developers/contracts/references/storage/main.md @@ -129,8 +129,6 @@ contract Token { unconstrained fn balance_of_private(owner: AztecAddress) -> Field {} unconstrained fn balance_of_public(owner: AztecAddress) -> Field {} - - unconstrained fn compute_note_hash_and_nullifier(contract_address: Field, nonce: Field, storage_slot: Field, serialized_note: [Field; VALUE_NOTE_LEN]) -> [Field; 4] {} } ``` @@ -193,8 +191,6 @@ Aztec transactions can pass data to Ethereum contracts through the rollup via th Unconstrained functions can be thought of as view functions from Solidity--they only return information from the contract storage or compute and return data without modifying contract storage. -The `compute_note_hash_and_nullifier` function allows contract devs to specify how to compute notes and nullifiers. This must be included in every contract because it depends on the storage slots, which are defined when we set up storage. - ## Contract dependencies Before we can implement the functions, we need set up the contract storage, and before we do that we need to import the appropriate dependencies. @@ -435,21 +431,6 @@ A getter function for checking the public balance of the provided Aztec account. #include_code balance_of_public /noir-projects/noir-contracts/contracts/token_contract/src/main.nr rust -#### `compute_note_hash_and_nullifier` - -A getter function to compute the note hash and nullifier for notes in the contract's storage. - -This must be included in every contract because it depends on the storage slots, which are defined when we set up storage. - -#include_code compute_note_hash_and_nullifier /noir-projects/noir-contracts/contracts/token_contract/src/main.nr rust - -:::danger -If your contract works with storage (has Storage struct defined), you **MUST** include a `compute_note_hash_and_nullifier` function. -If you don't yet have any private state variables defined put there a placeholder function: - -#include_code compute_note_hash_and_nullifier_placeholder /noir-projects/noir-contracts/contracts/token_bridge_contract/src/main.nr rust -::: - ## Compiling Now that the contract is complete, you can compile it with `aztec-nargo`. See the [Sandbox reference page](../../../sandbox/references/sandbox-reference.md) for instructions on setting it up. diff --git a/docs/docs/developers/contracts/resources/common_patterns/main.md b/docs/docs/developers/contracts/resources/common_patterns/main.md index 10af032bbef..51b733e2ef0 100644 --- a/docs/docs/developers/contracts/resources/common_patterns/main.md +++ b/docs/docs/developers/contracts/resources/common_patterns/main.md @@ -121,17 +121,6 @@ Hence, it's necessary to add a "randomness" field to your note to prevent such a #include_code address_note_def noir-projects/aztec-nr/address-note/src/address_note.nr rust -### Working with `compute_note_hash_and_nullifier()` - -Currently, if you have storage defined, the compiler will error if you don't have a `compute_note_hash_and_nullifier()` defined. Without this, the PXE can't process encrypted events and discover your notes. - -If your contract doesn't have anything to do with notes (e.g. operates solely in the public domain), you can do the following: -#include_code compute_note_hash_and_nullifier_placeholder /noir-projects/noir-contracts/contracts/token_bridge_contract/src/main.nr rust - -Otherwise, you need this method to help the PXE with processing your notes. In our [demo token contract](../../../tutorials/writing_token_contract.md#compute_note_hash_and_nullifier), we work with 2 kinds of notes: `ValueNote` and `TransparentNote`. Hence this method must define how to work with both: - -#include_code compute_note_hash_and_nullifier /noir-projects/noir-contracts/contracts/token_contract/src/main.nr rust - ### L1 -- L2 interactions Refer to [Token Portal tutorial on bridging tokens between L1 and L2](../../../tutorials/token_portal/main.md) and/or [Uniswap tutorial that shows how to swap on L1 using funds on L2](../../../tutorials/uniswap/main.md). Both examples show how to: diff --git a/docs/docs/developers/contracts/writing_contracts/events/emit_event.md b/docs/docs/developers/contracts/writing_contracts/events/emit_event.md index 002f01284b4..0c4204b3d67 100644 --- a/docs/docs/developers/contracts/writing_contracts/events/emit_event.md +++ b/docs/docs/developers/contracts/writing_contracts/events/emit_event.md @@ -83,15 +83,7 @@ If the decryption is successful, the PXE will store the decrypted note inside a If the decryption fails, the specific log will be discarded. For the PXE to successfully process the decrypted note we need to compute the note's 'note hash' and 'nullifier'. -Aztec.nr enables smart contract developers to design custom notes, meaning developers can also customize how a note's note hash and nullifier should be computed. Because of this customizability, and because there will be a potentially-unlimited number of smart contracts deployed to Aztec, an PXE needs to be 'taught' how to compute the custom note hashes and nullifiers for a particular contract. Therefore, developers will need to implement a `compute_note_hash_and_nullifier` function inside their contracts. - -:::danger -If your contract works with storage (has Storage struct defined), you **MUST** include a `compute_note_hash_and_nullifier` function to allow PXE to process encrypted events. -::: - -Every time a new note is successfully decrypted, the PXE will expect the existence of a `compute_note_hash_and_nullifier` function, which must teach it how to correctly process the new note. - -#include_code compute_note_hash_and_nullifier /noir-projects/noir-contracts/contracts/token_contract/src/main.nr rust +Aztec.nr enables smart contract developers to design custom notes, meaning developers can also customize how a note's note hash and nullifier should be computed. Because of this customizability, and because there will be a potentially-unlimited number of smart contracts deployed to Aztec, an PXE needs to be 'taught' how to compute the custom note hashes and nullifiers for a particular contract. This is done by a function called `compute_note_hash_and_nullifier`, which is automatically injected into every contract when compiled. ## Unencrypted Events diff --git a/docs/docs/developers/contracts/writing_contracts/storage/define_storage.md b/docs/docs/developers/contracts/writing_contracts/storage/define_storage.md index 74e555622a4..fabb2a27d8a 100644 --- a/docs/docs/developers/contracts/writing_contracts/storage/define_storage.md +++ b/docs/docs/developers/contracts/writing_contracts/storage/define_storage.md @@ -19,14 +19,6 @@ struct Storage { } ``` -:::danger -If your contract uses storage (has Storage struct defined), you **MUST** include a `compute_note_hash_and_nullifier` function to allow PXE to process encrypted events. See [encrypted events](../events/emit_event.md#successfully-process-the-encrypted-event) for more. - -If you don't yet have any private state variables defined you can use this placeholder function: - -#include_code compute_note_hash_and_nullifier_placeholder /noir-projects/noir-contracts/contracts/token_bridge_contract/src/main.nr rust -::: - Since Aztec.nr is written in Noir, which is state-less, we need to specify how the storage struct should be initialized to read and write data correctly. This is done by specifying an `init` function that is run in functions that rely on reading or altering the state variables. This `init` function must declare the Storage struct with an instantiation defining how variables are accessed and manipulated. The function MUST be called `init` for the Aztec.nr library to properly handle it (this will be relaxed in the future). ```rust diff --git a/docs/docs/developers/debugging/aztecnr-errors.md b/docs/docs/developers/debugging/aztecnr-errors.md index 5045e564c24..47ee1974597 100644 --- a/docs/docs/developers/debugging/aztecnr-errors.md +++ b/docs/docs/developers/debugging/aztecnr-errors.md @@ -14,16 +14,6 @@ aztec = { git="https://github.com/AztecProtocol/aztec-packages/", tag="#include_ You can learn more about dependencies and their paths [here](../contracts/resources/dependencies.md). -#### `compute_note_hash_and_nullifier function not found. Define it in your contract` - -Any smart contract that works with storage must include a [`compute_note_hash_and_nullifier`](https://github.com/AztecProtocol/aztec-packages/blob/6c20b45993ee9cbd319ab8351e2722e0c912f427/noir-projects/aztec-nr/aztec/src/note/utils.nr#L69) function to allow the PXE to process encrypted events. - -This is an example of this function in the token contract: - -#include_code compute_note_hash_and_nullifier noir-projects/noir-contracts/contracts/token_contract/src/main.nr rust - -This error may also show if the `compute_note_hash_and_nullifier` function is not correct or sits outside of the contract. - #### `backend has encountered an error` This is likely due to a version mismatch or bad install of barretenberg. Try [reinstalling nargo](../updating.md) or uninstalling barretenberg: diff --git a/docs/docs/developers/getting_started/aztecnr-getting-started.md b/docs/docs/developers/getting_started/aztecnr-getting-started.md index 90bc58654cf..474dfa3ba8c 100644 --- a/docs/docs/developers/getting_started/aztecnr-getting-started.md +++ b/docs/docs/developers/getting_started/aztecnr-getting-started.md @@ -120,13 +120,7 @@ The `increment` function works very similarly to the `constructor`, but instead ## Prevent double spending -Because our counters are private, the network can't directly verify if a note was spent or not, which could lead to double-spending. To solve this, we use a nullifier - a unique identifier generated from each spent note and its owner. Although this isn't really an issue in this simple smart contract, Aztec requires a contract that has any private functions to include this function. - -Add a new function into your contract as shown below: - -#include_code nullifier /noir-projects/noir-contracts/contracts/counter_contract/src/main.nr rust - -Here, we're computing both the note hash and the nullifier. The nullifier computation uses Aztec’s `compute_note_hash_and_nullifier` function, which takes details about the note's attributes eg contract address, nonce, storage slot, type id, and preimage. +Because our counters are private, the network can't directly verify if a note was spent or not, which could lead to double-spending. To solve this, we use a nullifier - a unique identifier generated from each spent note and its owner. Although this isn't really an issue in this simple smart contract, Aztec injects a special function called `compute_note_hash_and_nullifier` to determine these values for any given note produced by this contract. ## Getting a counter @@ -172,7 +166,7 @@ This will return something like this: ```bash ➜ counter aztec-cli get-accounts -Accounts found: +Accounts found: Address: 0x2fd4503a9b855a852272945df53d7173297c1469cceda31048b85118364b09a3 Public Key: 0x27c20118733174347b8082f578a7d8fb84b3ad38be293715eee8119ee5cd8a6d0d6b7d8124b37359663e75bcd2756f544a93b821a06f8e33fba68cc8029794d9 diff --git a/docs/docs/developers/tutorials/token_portal/withdrawing_to_l1.md b/docs/docs/developers/tutorials/token_portal/withdrawing_to_l1.md index cacc357bfe0..1af146666ce 100644 --- a/docs/docs/developers/tutorials/token_portal/withdrawing_to_l1.md +++ b/docs/docs/developers/tutorials/token_portal/withdrawing_to_l1.md @@ -60,13 +60,6 @@ We need a function that lets us read the token value. Paste this into `main.nr`: #include_code read_token /noir-projects/noir-contracts/contracts/token_bridge_contract/src/main.nr rust -And the `compute_note_hash_and_nullifier` required on every contract: - -```rust -#include_code compute_note_hash_and_nullifier_placeholder /noir-projects/noir-contracts/contracts/token_bridge_contract/src/main.nr raw -} -``` - ## Compile code Congratulations, you have written all the contracts we need for this tutorial! Now let's compile them. diff --git a/docs/docs/developers/tutorials/writing_private_voting_contract.md b/docs/docs/developers/tutorials/writing_private_voting_contract.md index 22d0b99406f..46766c3da23 100644 --- a/docs/docs/developers/tutorials/writing_private_voting_contract.md +++ b/docs/docs/developers/tutorials/writing_private_voting_contract.md @@ -163,16 +163,6 @@ Paste this function in your contract: Here, we are asserting that the `msg_sender()` is equal to the admin stored in public state. We have to create an `AztecAddress` type from the `msg_sender()` in order to do a direct comparison. -## compute_note_hash_and_nullifier - -Every Aztec contract that has storage must have a `compute_note_hash_and_nullifier()` function. If you try to compile without this function, you will get an error. This is explained in more detail [here](../contracts/resources/common_patterns/main.md#working-with-compute_note_hash_and_nullifier). - -At the end of the contract, paste this: - -#include_code compute_note_hash_and_nullifier noir-projects/noir-contracts/contracts/easy_private_voting_contract/src/main.nr rust - -We can simply return `[0,0,0,0]` because we are not creating any notes in our contract. - ## Compiling and deploying The easiest way to compile the contract is with `aztec-nargo`. Run the following command in the directory with your Nargo.toml file: diff --git a/docs/docs/developers/tutorials/writing_token_contract.md b/docs/docs/developers/tutorials/writing_token_contract.md index 7a25ad513f5..7a0960f0f2d 100644 --- a/docs/docs/developers/tutorials/writing_token_contract.md +++ b/docs/docs/developers/tutorials/writing_token_contract.md @@ -129,8 +129,6 @@ contract Token { unconstrained fn balance_of_private(owner: AztecAddress) -> Field {} unconstrained fn balance_of_public(owner: AztecAddress) -> Field {} - - unconstrained fn compute_note_hash_and_nullifier(contract_address: Field, nonce: Field, storage_slot: Field, note_type_id: Field, serialized_note: [Field; VALUE_NOTE_LEN]) -> [Field; 4] {} } ``` @@ -193,8 +191,6 @@ Aztec transactions can pass data to Ethereum contracts through the rollup via th Unconstrained functions can be thought of as view functions from Solidity--they only return information from the contract storage or compute and return data without modifying contract storage. -The `compute_note_hash_and_nullifier` function allows contract devs to specify how to compute notes and nullifiers. This must be included in every contract because it depends on the storage slots, which are defined when we set up storage. - ## Contract dependencies Before we can implement the functions, we need set up the contract storage, and before we do that we need to import the appropriate dependencies. @@ -435,21 +431,6 @@ A getter function for checking the public balance of the provided Aztec account. #include_code balance_of_public /noir-projects/noir-contracts/contracts/token_contract/src/main.nr rust -#### `compute_note_hash_and_nullifier` - -A getter function to compute the note hash and nullifier for notes in the contract's storage. - -This must be included in every contract because it depends on the storage slots, which are defined when we set up storage. - -#include_code compute_note_hash_and_nullifier /noir-projects/noir-contracts/contracts/token_contract/src/main.nr rust - -:::danger -If your contract works with storage (has Storage struct defined), you **MUST** include a `compute_note_hash_and_nullifier` function. -If you don't yet have any private state variables defined put there a placeholder function: - -#include_code compute_note_hash_and_nullifier_placeholder /noir-projects/noir-contracts/contracts/token_bridge_contract/src/main.nr rust -::: - ## Compiling Now that the contract is complete, you can compile it with `aztec-nargo`. See the [Sandbox reference page](../sandbox/references/sandbox-reference.md) for instructions on setting it up. diff --git a/docs/docs/misc/migration_notes.md b/docs/docs/misc/migration_notes.md index 326beddd8bb..6009c4d2809 100644 --- a/docs/docs/misc/migration_notes.md +++ b/docs/docs/misc/migration_notes.md @@ -6,6 +6,14 @@ keywords: [sandbox, cli, aztec, notes, migration, updating, upgrading] Aztec is in full-speed development. Literally every version breaks compatibility with the previous ones. This page attempts to target errors and difficulties you might encounter when upgrading, and how to resolve them. +## TBD + +### Autogenerate `compute_note_hash_and_nullifier` + +Historically developers have been required to include a `compute_note_hash_and_nullifier` function in each of their contracts. This function is now automatically generated, and all instances of it in contract code can be safely removed. + +It is possible to provide a user-defined implementation, in which case auto-generation will be skipped (though there are no known use cases for this). + ## 0.24.0 ### Introduce Note Type IDs diff --git a/noir-projects/aztec-nr/value-note/src/balance_utils.nr b/noir-projects/aztec-nr/value-note/src/balance_utils.nr index 570f0cca729..6372d718902 100644 --- a/noir-projects/aztec-nr/value-note/src/balance_utils.nr +++ b/noir-projects/aztec-nr/value-note/src/balance_utils.nr @@ -1,6 +1,6 @@ use dep::aztec::note::{note_getter::view_notes, note_viewer_options::NoteViewerOptions}; use dep::aztec::state_vars::set::Set; -use crate::value_note::{VALUE_NOTE_LEN, ValueNote}; +use crate::value_note::ValueNote; unconstrained pub fn get_balance(set: Set) -> Field { get_balance_with_offset(set, 0) diff --git a/noir-projects/noir-contracts/contracts/avm_test_contract/src/main.nr b/noir-projects/noir-contracts/contracts/avm_test_contract/src/main.nr index 8f2d554a811..64fe1644489 100644 --- a/noir-projects/noir-contracts/contracts/avm_test_contract/src/main.nr +++ b/noir-projects/noir-contracts/contracts/avm_test_contract/src/main.nr @@ -113,20 +113,8 @@ contract AvmTest { fn getTimestamp() -> pub Field { context.timestamp() } - // #[aztec(public-vm)] // fn getContractCallDepth() -> pub Field { // context.contract_call_depth() // } - - // Function required for all contracts - unconstrained fn compute_note_hash_and_nullifier( - _contract_address: AztecAddress, - _nonce: Field, - _storage_slot: Field, - _note_type_id: Field, - _serialized_note: [Field; 1] - ) -> pub [Field; 4] { - [0, 0, 0, 0] - } } diff --git a/noir-projects/noir-contracts/contracts/benchmarking_contract/src/main.nr b/noir-projects/noir-contracts/contracts/benchmarking_contract/src/main.nr index 5cdbbe83d5f..86215deb334 100644 --- a/noir-projects/noir-contracts/contracts/benchmarking_contract/src/main.nr +++ b/noir-projects/noir-contracts/contracts/benchmarking_contract/src/main.nr @@ -5,7 +5,7 @@ // arise from code changes. contract Benchmarking { - use dep::value_note::{utils::{increment, decrement}, value_note::{VALUE_NOTE_LEN, ValueNote}}; + use dep::value_note::{utils::{increment, decrement}, value_note::ValueNote}; use dep::aztec::{ protocol_types::{abis::function_selector::FunctionSelector, address::AztecAddress}, @@ -60,15 +60,4 @@ contract Benchmarking { fn broadcast(owner: AztecAddress) { emit_unencrypted_log(&mut context, storage.balances.at(owner).read()); } - - unconstrained fn compute_note_hash_and_nullifier( - contract_address: AztecAddress, - nonce: Field, - storage_slot: Field, - note_type_id: Field, - serialized_note: [Field; VALUE_NOTE_LEN] - ) -> pub [Field; 4] { - let note_header = NoteHeader::new(contract_address, nonce, storage_slot); - note_utils::compute_note_hash_and_nullifier(ValueNote::deserialize_content, note_header, serialized_note) - } } diff --git a/noir-projects/noir-contracts/contracts/card_game_contract/src/cards.nr b/noir-projects/noir-contracts/contracts/card_game_contract/src/cards.nr index 9569a41b774..6b3e694768b 100644 --- a/noir-projects/noir-contracts/contracts/card_game_contract/src/cards.nr +++ b/noir-projects/noir-contracts/contracts/card_game_contract/src/cards.nr @@ -19,7 +19,7 @@ use dep::std::{ option::Option, }; use dep::value_note::{ - value_note::{ValueNote, VALUE_NOTE_LEN}, + value_note::ValueNote, }; struct Card { diff --git a/noir-projects/noir-contracts/contracts/card_game_contract/src/main.nr b/noir-projects/noir-contracts/contracts/card_game_contract/src/main.nr index ef3b161cfdd..8af2576846a 100644 --- a/noir-projects/noir-contracts/contracts/card_game_contract/src/main.nr +++ b/noir-projects/noir-contracts/contracts/card_game_contract/src/main.nr @@ -148,18 +148,4 @@ contract CardGame { unconstrained fn view_game(game: u32) -> pub Game { storage.games.at(game as Field).read() } - - // Computes note hash and nullifier. - // Note 1: Needs to be defined by every contract producing logs. - // Note 2: Having it in all the contracts gives us the ability to compute the note hash and nullifier differently for different kind of notes. - unconstrained fn compute_note_hash_and_nullifier( - contract_address: AztecAddress, - nonce: Field, - storage_slot: Field, - note_type_id: Field, - serialized_note: [Field; VALUE_NOTE_LEN] - ) -> pub [Field; 4] { - let note_header = NoteHeader::new(contract_address, nonce, storage_slot); - note_utils::compute_note_hash_and_nullifier(ValueNote::deserialize_content, note_header, serialized_note) - } } diff --git a/noir-projects/noir-contracts/contracts/child_contract/src/main.nr b/noir-projects/noir-contracts/contracts/child_contract/src/main.nr index e8384554369..8542bea1b84 100644 --- a/noir-projects/noir-contracts/contracts/child_contract/src/main.nr +++ b/noir-projects/noir-contracts/contracts/child_contract/src/main.nr @@ -6,7 +6,7 @@ contract Child { context::{PrivateContext, PublicContext, Context}, log::emit_unencrypted_log, state_vars::{public_state::PublicState, set::Set}, protocol_types::{abis::{function_selector::FunctionSelector, call_context::CallContext}, address::AztecAddress}, - note::{note_getter_options::NoteGetterOptions} + note::{note_getter_options::NoteGetterOptions, note_header::NoteHeader, utils as note_utils} }; use dep::value_note::value_note::{ValueNote, VALUE_NOTE_LEN}; @@ -111,15 +111,4 @@ contract Child { let pubSetValueSelector = FunctionSelector::from_signature("pubSetValue(Field)"); let _ret = context.call_public_function(context.this_address(), pubSetValueSelector, [10]); } - - // TODO: remove this placeholder once https://github.com/AztecProtocol/aztec-packages/issues/2918 is implemented - unconstrained fn compute_note_hash_and_nullifier( - contract_address: AztecAddress, - nonce: Field, - storage_slot: Field, - note_type_id: Field, - serialized_note: [Field; 0] - ) -> pub [Field; 4] { - [0, 0, 0, 0] - } } diff --git a/noir-projects/noir-contracts/contracts/counter_contract/src/main.nr b/noir-projects/noir-contracts/contracts/counter_contract/src/main.nr index 02059ef0d3f..a61f276d070 100644 --- a/noir-projects/noir-contracts/contracts/counter_contract/src/main.nr +++ b/noir-projects/noir-contracts/contracts/counter_contract/src/main.nr @@ -47,17 +47,4 @@ contract Counter { balance_utils::get_balance(counters.at(owner).set) } // docs:end:get_counter - - // docs:start:nullifier - unconstrained fn compute_note_hash_and_nullifier( - contract_address: AztecAddress, - nonce: Field, - storage_slot: Field, - note_type_id: Field, - serialized_note: [Field; VALUE_NOTE_LEN] - ) -> pub [Field; 4] { - let note_header = NoteHeader::new(contract_address, nonce, storage_slot); - note_utils::compute_note_hash_and_nullifier(ValueNote::deserialize_content, note_header, serialized_note) - } - // docs:end:nullifier } diff --git a/noir-projects/noir-contracts/contracts/docs_example_contract/src/main.nr b/noir-projects/noir-contracts/contracts/docs_example_contract/src/main.nr index 86c91046462..7c555eea3b5 100644 --- a/noir-projects/noir-contracts/contracts/docs_example_contract/src/main.nr +++ b/noir-projects/noir-contracts/contracts/docs_example_contract/src/main.nr @@ -30,7 +30,7 @@ contract DocsExample { // how to import methods from other files/folders within your workspace use crate::options::create_account_card_getter_options; use crate::types::{ - card_note::{CardNote, CARD_NOTE_LEN}, + card_note::CardNote, leader::Leader, }; @@ -232,18 +232,6 @@ contract DocsExample { storage.imm_singleton.is_initialized() } - // TODO: remove this placeholder once https://github.com/AztecProtocol/aztec-packages/issues/2918 is implemented - unconstrained fn compute_note_hash_and_nullifier( - contract_address: AztecAddress, - nonce: Field, - storage_slot: Field, - note_type_id: Field, - serialized_note: [Field; CARD_NOTE_LEN] - ) -> pub [Field; 4] { - let note_header = NoteHeader::new(contract_address, nonce, storage_slot); - note_utils::compute_note_hash_and_nullifier(CardNote::deserialize_content, note_header, serialized_note) - } - /// Macro equivalence section use dep::aztec::hasher::Hasher; diff --git a/noir-projects/noir-contracts/contracts/easy_private_token_contract/src/main.nr b/noir-projects/noir-contracts/contracts/easy_private_token_contract/src/main.nr index 29137e82834..336bae7e326 100644 --- a/noir-projects/noir-contracts/contracts/easy_private_token_contract/src/main.nr +++ b/noir-projects/noir-contracts/contracts/easy_private_token_contract/src/main.nr @@ -12,10 +12,7 @@ contract EasyPrivateToken { }; use dep::value_note::{ balance_utils, - value_note::{ - ValueNote, - VALUE_NOTE_LEN, - }, + value_note::ValueNote, }; use dep::easy_private_state::easy_private_state::EasyPrivateUint; @@ -57,19 +54,5 @@ contract EasyPrivateToken { // Return the sum of all notes in the set. balance_utils::get_balance(balances.at(owner).set) } - - // Computes note hash and nullifier. - // Note 1: Needs to be defined by every contract producing logs. - // Note 2: Having it in all the contracts gives us the ability to compute the note hash and nullifier differently for different kind of notes. - unconstrained fn compute_note_hash_and_nullifier( - contract_address: AztecAddress, - nonce: Field, - storage_slot: Field, - note_type_id: Field, - serialized_note: [Field; VALUE_NOTE_LEN] - ) -> pub [Field; 4] { - let note_header = NoteHeader::new(contract_address, nonce, storage_slot); - note_utils::compute_note_hash_and_nullifier(ValueNote::deserialize_content, note_header, serialized_note) - } } // docs:end:easy_private_token_contract diff --git a/noir-projects/noir-contracts/contracts/easy_private_voting_contract/src/main.nr b/noir-projects/noir-contracts/contracts/easy_private_voting_contract/src/main.nr index ea919f4eb7d..f53586132a0 100644 --- a/noir-projects/noir-contracts/contracts/easy_private_voting_contract/src/main.nr +++ b/noir-projects/noir-contracts/contracts/easy_private_voting_contract/src/main.nr @@ -66,15 +66,4 @@ contract EasyPrivateVoting { storage.tally.at(candidate).read() } // docs:end:get_vote - // docs:start:compute_note_hash_and_nullifier - unconstrained fn compute_note_hash_and_nullifier( - contract_address: AztecAddress, - nonce: Field, - storage_slot: Field, - note_type_id: Field, - serialized_note: [Field; 0] - ) -> pub [Field; 4] { - [0, 0, 0, 0] - } - // docs:end:compute_note_hash_and_nullifier } diff --git a/noir-projects/noir-contracts/contracts/ecdsa_account_contract/src/main.nr b/noir-projects/noir-contracts/contracts/ecdsa_account_contract/src/main.nr index dfe804c0fa3..d53117493fb 100644 --- a/noir-projects/noir-contracts/contracts/ecdsa_account_contract/src/main.nr +++ b/noir-projects/noir-contracts/contracts/ecdsa_account_contract/src/main.nr @@ -16,7 +16,7 @@ contract EcdsaAccount { auth_witness::get_auth_witness }; - use crate::ecdsa_public_key_note::{EcdsaPublicKeyNote, ECDSA_PUBLIC_KEY_NOTE_LEN}; + use crate::ecdsa_public_key_note::EcdsaPublicKeyNote; struct Storage { public_key: ImmutableSingleton, @@ -78,23 +78,4 @@ contract EcdsaAccount { true } - - // Computes note hash and nullifier. - // Note 1: Needs to be defined by every contract producing logs. - // Note 2: Having it in all the contracts gives us the ability to compute the note hash and nullifier differently for different kind of notes. - unconstrained fn compute_note_hash_and_nullifier( - contract_address: AztecAddress, - nonce: Field, - storage_slot: Field, - note_type_id: Field, - serialized_note: [Field; ECDSA_PUBLIC_KEY_NOTE_LEN] - ) -> pub [Field; 4] { - assert(storage_slot == storage.public_key.get_storage_slot()); - let note_header = NoteHeader::new(contract_address, nonce, storage_slot); - note_utils::compute_note_hash_and_nullifier( - EcdsaPublicKeyNote::deserialize_content, - note_header, - serialized_note - ) - } } diff --git a/noir-projects/noir-contracts/contracts/escrow_contract/src/main.nr b/noir-projects/noir-contracts/contracts/escrow_contract/src/main.nr index 1bc3de696df..d04a1599aea 100644 --- a/noir-projects/noir-contracts/contracts/escrow_contract/src/main.nr +++ b/noir-projects/noir-contracts/contracts/escrow_contract/src/main.nr @@ -48,16 +48,4 @@ contract Escrow { [this.to_field(), recipient.to_field(), amount, 0] ); } - - unconstrained fn compute_note_hash_and_nullifier( - contract_address: AztecAddress, - nonce: Field, - storage_slot: Field, - note_type_id: Field, - serialized_note: [Field; ADDRESS_NOTE_LEN] - ) -> pub [Field; 4] { - let note_header = NoteHeader::new(contract_address, nonce, storage_slot); - assert(storage_slot == storage.owners.get_storage_slot()); - note_utils::compute_note_hash_and_nullifier(AddressNote::deserialize_content, note_header, serialized_note) - } } diff --git a/noir-projects/noir-contracts/contracts/inclusion_proofs_contract/src/main.nr b/noir-projects/noir-contracts/contracts/inclusion_proofs_contract/src/main.nr index 913a0a5b161..eaaf052f99d 100644 --- a/noir-projects/noir-contracts/contracts/inclusion_proofs_contract/src/main.nr +++ b/noir-projects/noir-contracts/contracts/inclusion_proofs_contract/src/main.nr @@ -28,7 +28,7 @@ contract InclusionProofs { }; // docs:end:imports // docs:start:value_note_imports - use dep::value_note::value_note::{ValueNote, VALUE_NOTE_LEN}; + use dep::value_note::value_note::ValueNote; // docs:end:value_note_imports struct Storage { private_values: Map>, @@ -250,18 +250,4 @@ contract InclusionProofs { ); // Here typically the factory would add the contract address to its internal map of deployed contracts. } - - // Computes note hash and nullifier. - // Note 1: Needs to be defined by every contract producing logs. - // Note 2: Having it in all the contracts gives us the ability to compute the note hash and nullifier differently for different kind of notes. - unconstrained fn compute_note_hash_and_nullifier( - contract_address: AztecAddress, - nonce: Field, - storage_slot: Field, - note_type_id: Field, - serialized_note: [Field; VALUE_NOTE_LEN] - ) -> pub [Field; 4] { - let note_header = NoteHeader::new(contract_address, nonce, storage_slot); - note_utils::compute_note_hash_and_nullifier(ValueNote::deserialize_content, note_header, serialized_note) - } } diff --git a/noir-projects/noir-contracts/contracts/lending_contract/src/main.nr b/noir-projects/noir-contracts/contracts/lending_contract/src/main.nr index c617053ae6a..40c16ecf6f6 100644 --- a/noir-projects/noir-contracts/contracts/lending_contract/src/main.nr +++ b/noir-projects/noir-contracts/contracts/lending_contract/src/main.nr @@ -299,15 +299,4 @@ contract Lending { unconstrained fn get_assets() -> pub [AztecAddress; 2] { [storage.collateral_asset.read(), storage.stable_coin.read()] } - - // TODO: remove this placeholder once https://github.com/AztecProtocol/aztec-packages/issues/2918 is implemented - unconstrained fn compute_note_hash_and_nullifier( - contract_address: AztecAddress, - nonce: Field, - storage_slot: Field, - note_type_id: Field, - serialized_note: [Field; 0] - ) -> pub [Field; 4] { - [0, 0, 0, 0] - } } diff --git a/noir-projects/noir-contracts/contracts/pending_commitments_contract/src/main.nr b/noir-projects/noir-contracts/contracts/pending_commitments_contract/src/main.nr index 6c09247b61a..69b504d2c6d 100644 --- a/noir-projects/noir-contracts/contracts/pending_commitments_contract/src/main.nr +++ b/noir-projects/noir-contracts/contracts/pending_commitments_contract/src/main.nr @@ -244,7 +244,6 @@ contract PendingCommitments { [owner.to_field()] ); } - // Confirm cannot get/read a pending commitment in a nested call // that is created/inserted later in execution but in the parent. // NOTE: This test is especially important in an end-to-end context because the parent call @@ -259,18 +258,4 @@ contract PendingCommitments { // get_then_nullify_fn_selector: FunctionSelector, //) { //} - - // Computes note hash and nullifier. - // Note 1: Needs to be defined by every contract producing logs. - // Note 2: Having it in all the contracts gives us the ability to compute the note hash and nullifier differently for different kind of notes. - unconstrained fn compute_note_hash_and_nullifier( - contract_address: AztecAddress, - nonce: Field, - storage_slot: Field, - note_type_id: Field, - serialized_note: [Field; VALUE_NOTE_LEN] - ) -> pub [Field; 4] { - let note_header = NoteHeader::new(contract_address, nonce, storage_slot); - note_utils::compute_note_hash_and_nullifier(ValueNote::deserialize_content, note_header, serialized_note) - } } diff --git a/noir-projects/noir-contracts/contracts/price_feed_contract/src/main.nr b/noir-projects/noir-contracts/contracts/price_feed_contract/src/main.nr index 5d2ef98e196..a86fcc73bfd 100644 --- a/noir-projects/noir-contracts/contracts/price_feed_contract/src/main.nr +++ b/noir-projects/noir-contracts/contracts/price_feed_contract/src/main.nr @@ -34,15 +34,4 @@ contract PriceFeed { unconstrained fn fetch_price(assetId: Field) -> pub Asset { storage.assets.at(assetId).read() } - - // TODO: remove this placeholder once https://github.com/AztecProtocol/aztec-packages/issues/2918 is implemented - unconstrained fn compute_note_hash_and_nullifier( - contract_address: AztecAddress, - nonce: Field, - storage_slot: Field, - note_type_id: Field, - serialized_note: [Field; 0] - ) -> pub [Field; 4] { - [0, 0, 0, 0] - } } 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 9eb15c3f691..1539c038b4a 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 @@ -88,23 +88,4 @@ contract SchnorrAccount { // docs:end:entrypoint true } - - // Computes notes hash and nullifier. - // Note 1: Needs to be defined by every contract producing logs. - // Note 2: Having it in all the contracts gives us the ability to compute the note hash and nullifier differently for different kind of notes. - unconstrained fn compute_note_hash_and_nullifier( - contract_address: AztecAddress, - nonce: Field, - storage_slot: Field, - note_type_id: Field, - serialized_note: [Field; PUBLIC_KEY_NOTE_LEN] - ) -> pub [Field; 4] { - assert(storage_slot == storage.signing_public_key.get_storage_slot()); - let note_header = NoteHeader::new(contract_address, nonce, storage_slot); - note_utils::compute_note_hash_and_nullifier( - PublicKeyNote::deserialize_content, - note_header, - serialized_note - ) - } } 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 c5293bac24c..88f6b7cf5e7 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 @@ -2,7 +2,7 @@ // Account contract that uses Schnorr signatures for authentication using a hardcoded public key. contract SchnorrHardcodedAccount { use dep::std; - use dep::aztec::context::PrivateContext; + use dep::aztec::{context::PrivateContext, protocol_types::address::AztecAddress}; use dep::authwit::{ entrypoint::{app::AppPayload, fee::FeePayload}, account::AccountActions, 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 f01b3237bfe..b5633b70009 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 @@ -3,7 +3,7 @@ mod auth_oracle; contract SchnorrSingleKeyAccount { use dep::std::{option::Option}; - use dep::aztec::context::{PrivateContext, PublicContext, Context}; + use dep::aztec::{context::{PrivateContext, PublicContext, Context}, protocol_types::address::AztecAddress}; use dep::authwit::{entrypoint::{app::AppPayload, fee::FeePayload}, account::AccountActions}; diff --git a/noir-projects/noir-contracts/contracts/slow_tree_contract/src/main.nr b/noir-projects/noir-contracts/contracts/slow_tree_contract/src/main.nr index b8a0d2525d8..7950c230be8 100644 --- a/noir-projects/noir-contracts/contracts/slow_tree_contract/src/main.nr +++ b/noir-projects/noir-contracts/contracts/slow_tree_contract/src/main.nr @@ -8,7 +8,7 @@ mod types; contract SlowTree { use dep::aztec::protocol_types::{abis::function_selector::FunctionSelector, address::AztecAddress}; use dep::std::option::Option; - use dep::value_note::{balance_utils, utils::{increment, decrement}, value_note::{VALUE_NOTE_LEN, ValueNote}}; + use dep::value_note::{balance_utils, utils::{increment, decrement}, value_note::ValueNote}; use dep::aztec::{ context::{PrivateContext, PublicContext, Context}, note::{note_header::NoteHeader, utils as note_utils}, @@ -140,14 +140,4 @@ contract SlowTree { unconstrained fn un_read_root(address: AztecAddress) -> pub Leaf { storage.trees.at(address.to_field()).read_root() } - - unconstrained fn compute_note_hash_and_nullifier( - _contract_address: AztecAddress, - _nonce: Field, - _storage_slot: Field, - _note_type_id: Field, - _serialized_note: [Field; 4] - ) -> pub [Field; 4] { - [0x0d, 0x0e, 0x0a, 0x0d] - } } diff --git a/noir-projects/noir-contracts/contracts/stateful_test_contract/src/main.nr b/noir-projects/noir-contracts/contracts/stateful_test_contract/src/main.nr index 8c00312a3b7..b276253718f 100644 --- a/noir-projects/noir-contracts/contracts/stateful_test_contract/src/main.nr +++ b/noir-projects/noir-contracts/contracts/stateful_test_contract/src/main.nr @@ -55,15 +55,4 @@ contract StatefulTest { unconstrained fn get_public_value(owner: AztecAddress) -> pub Field { storage.public_values.at(owner).read() } - - unconstrained fn compute_note_hash_and_nullifier( - contract_address: AztecAddress, - nonce: Field, - storage_slot: Field, - note_type_id: Field, - serialized_note: [Field; VALUE_NOTE_LEN] - ) -> pub [Field; 4] { - let note_header = NoteHeader::new(contract_address, nonce, storage_slot); - note_utils::compute_note_hash_and_nullifier(ValueNote::deserialize_content, note_header, serialized_note) - } } diff --git a/noir-projects/noir-contracts/contracts/test_contract/src/main.nr b/noir-projects/noir-contracts/contracts/test_contract/src/main.nr index b0c081ead82..88a5c9d3956 100644 --- a/noir-projects/noir-contracts/contracts/test_contract/src/main.nr +++ b/noir-projects/noir-contracts/contracts/test_contract/src/main.nr @@ -23,7 +23,7 @@ contract Test { }; use dep::token_portal_content_hash_lib::{get_mint_private_content_hash, get_mint_public_content_hash}; use dep::field_note::field_note::FieldNote; - use dep::value_note::value_note::{ValueNote, VALUE_NOTE_LEN}; + use dep::value_note::value_note::ValueNote; #[event] struct ExampleEvent { @@ -353,24 +353,4 @@ contract Test { a_note: DummyNote, many_notes: [DummyNote; 3], } - - // Computes note hash and nullifier. - // Note 1: Needs to be defined by every contract producing logs. - // Note 2: Having it in all the contracts gives us the ability to compute the note hash and nullifier differently for different kind of notes. - unconstrained fn compute_note_hash_and_nullifier( - contract_address: AztecAddress, - nonce: Field, - storage_slot: Field, - note_type_id: Field, - serialized_note: [Field; VALUE_NOTE_LEN] // must fit either a FieldNote or a ValueNote - ) -> pub [Field; 4] { - let note_header = NoteHeader::new(contract_address, nonce, storage_slot); - - if (note_type_id == FieldNote::get_note_type_id()) { - note_utils::compute_note_hash_and_nullifier(FieldNote::deserialize_content, note_header, serialized_note) - } else { - let note_header = NoteHeader::new(contract_address, nonce, storage_slot); - note_utils::compute_note_hash_and_nullifier(ValueNote::deserialize_content, note_header, serialized_note) - } - } } diff --git a/noir-projects/noir-contracts/contracts/token_blacklist_contract/src/main.nr b/noir-projects/noir-contracts/contracts/token_blacklist_contract/src/main.nr index 0a0394289a1..92cb8dbab85 100644 --- a/noir-projects/noir-contracts/contracts/token_blacklist_contract/src/main.nr +++ b/noir-projects/noir-contracts/contracts/token_blacklist_contract/src/main.nr @@ -32,7 +32,7 @@ contract TokenBlacklist { state_vars::{map::Map, public_state::PublicState, set::Set, immutable_singleton::ImmutableSingleton}, }; - use dep::field_note::field_note::{FieldNote, FIELD_NOTE_LEN}; + use dep::field_note::field_note::FieldNote; use dep::authwit::{ auth::{ @@ -43,7 +43,7 @@ contract TokenBlacklist { use crate::types::{ transparent_note::TransparentNote, - token_note::{TokenNote, TOKEN_NOTE_LEN}, + token_note::TokenNote, balances_map::{BalancesMap}, roles::UserFlags, }; @@ -333,25 +333,4 @@ contract TokenBlacklist { unconstrained fn balance_of_public(owner: AztecAddress) -> pub u120 { storage.public_balances.at(owner).read().value } - - // Computes note hash and nullifier. - // Note 1: Needs to be defined by every contract producing logs. - // Note 2: Having it in all the contracts gives us the ability to compute the note hash and nullifier differently for different kind of notes. - unconstrained fn compute_note_hash_and_nullifier( - contract_address: AztecAddress, - nonce: Field, - storage_slot: Field, - note_type_id: Field, - preimage: [Field; TOKEN_NOTE_LEN] - ) -> pub [Field; 4] { - let note_header = NoteHeader::new(contract_address, nonce, storage_slot); - - if (note_type_id == TransparentNote::get_note_type_id()) { - note_utils::compute_note_hash_and_nullifier(TransparentNote::deserialize_content, note_header, preimage) - } else if (note_type_id == FieldNote::get_note_type_id()) { - note_utils::compute_note_hash_and_nullifier(FieldNote::deserialize_content, note_header, preimage) - } else { - note_utils::compute_note_hash_and_nullifier(TokenNote::deserialize_content, note_header, preimage) - } - } } diff --git a/noir-projects/noir-contracts/contracts/token_bridge_contract/src/main.nr b/noir-projects/noir-contracts/contracts/token_bridge_contract/src/main.nr index 73d26880356..0bd5613d2ca 100644 --- a/noir-projects/noir-contracts/contracts/token_bridge_contract/src/main.nr +++ b/noir-projects/noir-contracts/contracts/token_bridge_contract/src/main.nr @@ -154,17 +154,4 @@ contract TokenBridge { assert(storage.token.read().eq(token), "Token address is not the same as seen in storage"); } // docs:end:assert_token_is_same - - // TODO: remove this placeholder once https://github.com/AztecProtocol/aztec-packages/issues/2918 is implemented - // docs:start:compute_note_hash_and_nullifier_placeholder - unconstrained fn compute_note_hash_and_nullifier( - contract_address: AztecAddress, - nonce: Field, - storage_slot: Field, - note_type_id: Field, - serialized_note: [Field; 0] - ) -> pub [Field; 4] { - [0, 0, 0, 0] - } - // docs:end:compute_note_hash_and_nullifier_placeholder } 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 f48b637ef6d..3c056a28ec0 100644 --- a/noir-projects/noir-contracts/contracts/token_contract/src/main.nr +++ b/noir-projects/noir-contracts/contracts/token_contract/src/main.nr @@ -41,7 +41,7 @@ contract Token { use crate::types::{ transparent_note::TransparentNote, - token_note::{TokenNote, TOKEN_NOTE_LEN}, + token_note::TokenNote, balances_map::BalancesMap }; // docs:end::imports @@ -379,30 +379,5 @@ contract Token { storage.public_balances.at(owner).read().value } // docs:end:balance_of_public - - // docs:start:compute_note_hash_and_nullifier - // Computes note hash and nullifier. - // Note 1: Needs to be defined by every contract producing logs. - // Note 2: Having it in all the contracts gives us the ability to compute the note hash and nullifier differently for different kind of notes. - unconstrained fn compute_note_hash_and_nullifier( - contract_address: AztecAddress, - nonce: Field, - storage_slot: Field, - note_type_id: Field, - serialized_note: [Field; TOKEN_NOTE_LEN] - ) -> pub [Field; 4] { - let note_header = NoteHeader::new(contract_address, nonce, storage_slot); - - if (note_type_id == TransparentNote::get_note_type_id()) { - note_utils::compute_note_hash_and_nullifier( - TransparentNote::deserialize_content, - note_header, - serialized_note - ) - } else { - note_utils::compute_note_hash_and_nullifier(TokenNote::deserialize_content, note_header, serialized_note) - } - } - // docs:end:compute_note_hash_and_nullifier } // docs:end:token_all diff --git a/noir-projects/noir-contracts/contracts/uniswap_contract/src/main.nr b/noir-projects/noir-contracts/contracts/uniswap_contract/src/main.nr index 9567f948ee1..311c0e349e8 100644 --- a/noir-projects/noir-contracts/contracts/uniswap_contract/src/main.nr +++ b/noir-projects/noir-contracts/contracts/uniswap_contract/src/main.nr @@ -227,15 +227,4 @@ contract Uniswap { unconstrained fn nonce_for_burn_approval() -> pub Field { storage.nonce_for_burn_approval.read() } - - // TODO: remove this placeholder once https://github.com/AztecProtocol/aztec-packages/issues/2918 is implemented - unconstrained fn compute_note_hash_and_nullifier( - contract_address: AztecAddress, - nonce: Field, - storage_slot: Field, - note_type_id: Field, - serialized_note: [Field; 0] - ) -> pub [Field; 4] { - [0, 0, 0, 0] - } } diff --git a/noir/Cargo.lock b/noir/Cargo.lock index a7d721ef097..84d7f5052db 100644 --- a/noir/Cargo.lock +++ b/noir/Cargo.lock @@ -420,6 +420,7 @@ version = "0.24.0" dependencies = [ "convert_case 0.6.0", "iter-extended", + "noirc_errors", "noirc_frontend", ] diff --git a/noir/aztec_macros/Cargo.toml b/noir/aztec_macros/Cargo.toml index 5e908b2e672..ed9821fabcf 100644 --- a/noir/aztec_macros/Cargo.toml +++ b/noir/aztec_macros/Cargo.toml @@ -11,5 +11,6 @@ repository.workspace = true [dependencies] noirc_frontend.workspace = true +noirc_errors.workspace = true iter-extended.workspace = true convert_case = "0.6.0" diff --git a/noir/aztec_macros/src/lib.rs b/noir/aztec_macros/src/lib.rs index 0f054c262bf..a0a63d8a9a8 100644 --- a/noir/aztec_macros/src/lib.rs +++ b/noir/aztec_macros/src/lib.rs @@ -3,6 +3,8 @@ use std::vec; use convert_case::{Case, Casing}; use iter_extended::vecmap; +use noirc_frontend::hir::def_collector::dc_crate::{UnresolvedFunctions, UnresolvedTraitImpl}; +use noirc_frontend::hir::def_map::{LocalModuleId, ModuleId}; use noirc_frontend::macros_api::FieldElement; use noirc_frontend::macros_api::{ BlockExpression, CallExpression, CastExpression, Distinctness, Expression, ExpressionKind, @@ -16,9 +18,10 @@ use noirc_frontend::macros_api::{ use noirc_frontend::macros_api::{CrateId, FileId}; use noirc_frontend::macros_api::{MacroError, MacroProcessor}; use noirc_frontend::macros_api::{ModuleDefId, NodeInterner, SortedModule, StructId}; -use noirc_frontend::node_interner::{TraitId, TraitImplKind}; +use noirc_frontend::node_interner::{FuncId, TraitId, TraitImplId, TraitImplKind}; use noirc_frontend::Lambda; - +use noirc_frontend::macros_api::parse_program; +use noirc_errors::Location; pub struct AztecMacro; impl MacroProcessor for AztecMacro { @@ -31,6 +34,20 @@ impl MacroProcessor for AztecMacro { transform(ast, crate_id, context) } + fn process_unresolved_traits_impls( + &self, + crate_id: &CrateId, + context: &mut HirContext, + unresolved_traits_impls: &Vec, + collected_functions: &mut Vec, + ) -> Result<(), (MacroError, FileId)> { + if has_aztec_dependency(crate_id, context) { + inject_compute_note_hash_and_nullifier(crate_id, context, unresolved_traits_impls, collected_functions) + } else { + Ok(()) + } + } + fn process_typed_ast( &self, crate_id: &CrateId, @@ -46,7 +63,6 @@ const MAX_CONTRACT_FUNCTIONS: usize = 2_usize.pow(FUNCTION_TREE_HEIGHT); #[derive(Debug, Clone)] pub enum AztecMacroError { AztecDepNotFound, - ComputeNoteHashAndNullifierNotFound { span: Span }, ContractHasTooManyFunctions { span: Span }, ContractConstructorMissing { span: Span }, UnsupportedFunctionArgumentType { span: Span, typ: UnresolvedTypeData }, @@ -63,11 +79,6 @@ impl From for MacroError { secondary_message: None, span: None, }, - AztecMacroError::ComputeNoteHashAndNullifierNotFound { span } => MacroError { - primary_message: "compute_note_hash_and_nullifier function not found. Define it in your contract. For more information go to https://docs.aztec.network/developers/debugging/aztecnr-errors#compute_note_hash_and_nullifier-function-not-found-define-it-in-your-contract".to_owned(), - secondary_message: None, - span: Some(span), - }, AztecMacroError::ContractHasTooManyFunctions { span } => MacroError { primary_message: format!("Contract can only have a maximum of {} functions", MAX_CONTRACT_FUNCTIONS), secondary_message: None, @@ -313,15 +324,17 @@ fn check_for_aztec_dependency( crate_id: &CrateId, context: &HirContext, ) -> Result<(), (MacroError, FileId)> { - let crate_graph = &context.crate_graph[crate_id]; - let has_aztec_dependency = crate_graph.dependencies.iter().any(|dep| dep.as_name() == "aztec"); - if has_aztec_dependency { + if has_aztec_dependency(crate_id, context) { Ok(()) } else { - Err((AztecMacroError::AztecDepNotFound.into(), crate_graph.root_file_id)) + Err((AztecMacroError::AztecDepNotFound.into(), context.crate_graph[crate_id].root_file_id)) } } +fn has_aztec_dependency(crate_id: &CrateId, context: &HirContext) -> bool { + context.crate_graph[crate_id].dependencies.iter().any(|dep| dep.as_name() == "aztec") +} + // Check to see if the user has defined a storage struct fn check_for_storage_definition(module: &SortedModule) -> bool { module.types.iter().any(|r#struct| r#struct.name.0.contents == "Storage") @@ -338,27 +351,27 @@ fn check_for_storage_implementation(module: &SortedModule) -> bool { } // Check if "compute_note_hash_and_nullifier(AztecAddress,Field,Field,Field,[Field; N]) -> [Field; 4]" is defined -fn check_for_compute_note_hash_and_nullifier_definition(module: &SortedModule) -> bool { - module.functions.iter().any(|func| { - func.def.name.0.contents == "compute_note_hash_and_nullifier" - && func.def.parameters.len() == 5 - && match &func.def.parameters[0].typ.typ { +fn check_for_compute_note_hash_and_nullifier_definition(functions_data: &Vec<(LocalModuleId, FuncId, NoirFunction)>, module_id: LocalModuleId) -> bool { + functions_data.iter().filter(|func_data| func_data.0 == module_id).any(|func_data| { + func_data.2.def.name.0.contents == "compute_note_hash_and_nullifier" + && func_data.2.def.parameters.len() == 5 + && match &func_data.2.def.parameters[0].typ.typ { UnresolvedTypeData::Named(path, _, _) => path.segments.last().unwrap().0.contents == "AztecAddress", _ => false, } - && func.def.parameters[1].typ.typ == UnresolvedTypeData::FieldElement - && func.def.parameters[2].typ.typ == UnresolvedTypeData::FieldElement - && func.def.parameters[3].typ.typ == UnresolvedTypeData::FieldElement + && func_data.2.def.parameters[1].typ.typ == UnresolvedTypeData::FieldElement + && func_data.2.def.parameters[2].typ.typ == UnresolvedTypeData::FieldElement + && func_data.2.def.parameters[3].typ.typ == UnresolvedTypeData::FieldElement // checks if the 5th parameter is an array and the Box in // Array(Option, Box) contains only fields - && match &func.def.parameters[4].typ.typ { + && match &func_data.2.def.parameters[4].typ.typ { UnresolvedTypeData::Array(_, inner_type) => { matches!(inner_type.typ, UnresolvedTypeData::FieldElement) }, _ => false, } // We check the return type the same way as we did the 5th parameter - && match &func.def.return_type { + && match &func_data.2.def.return_type { FunctionReturnType::Default(_) => false, FunctionReturnType::Ty(unresolved_type) => { match &unresolved_type.typ { @@ -401,13 +414,6 @@ fn transform_module( generate_storage_implementation(module).map_err(|err| (err, crate_graph.root_file_id))?; } - if storage_defined && !check_for_compute_note_hash_and_nullifier_definition(module) { - return Err(( - AztecMacroError::ComputeNoteHashAndNullifierNotFound { span: Span::default() }, - crate_graph.root_file_id, - )); - } - for structure in module.types.iter() { if structure.attributes.iter().any(|attr| matches!(attr, SecondaryAttribute::Event)) { module.impls.push(generate_selector_impl(structure)); @@ -596,7 +602,7 @@ fn generate_storage_implementation(module: &mut SortedModule) -> Result<(), Azte /// If it does, it will insert the following things: /// - A new Input that is provided for a kernel app circuit, named: {Public/Private}ContextInputs /// - Hashes all of the function input variables -/// - This instantiates a helper function +/// - This instantiates a helper function fn transform_function( ty: &str, func: &mut NoirFunction, @@ -1601,3 +1607,165 @@ fn event_signature(event: &StructType) -> String { let fields = vecmap(event.get_fields(&[]), |(_, typ)| signature_of_type(&typ)); format!("{}({})", event.name.0.contents, fields.join(",")) } + +fn inject_compute_note_hash_and_nullifier( + crate_id: &CrateId, + context: &mut HirContext, + unresolved_traits_impls: &Vec, + collected_functions: &mut Vec, +) -> Result<(), (MacroError, FileId)> { + // We first fetch modules in this crate which correspond to contracts, along with their file id. + let contract_module_file_ids: Vec<(LocalModuleId, FileId)> = context.def_map(crate_id).expect("ICE: Missing crate in def_map") + .modules().iter() + .filter(|(_, module)| module.is_contract) + .map(|(idx, module)| (LocalModuleId(idx), module.location.file)) + .collect(); + + // If the current crate does not contain a contract module we simply skip it. + if contract_module_file_ids.len() == 0 { + return Ok(()); + } else if contract_module_file_ids.len() != 1 { + panic!("Found multiple contracts in the same crate"); + } + + let (module_id, file_id) = contract_module_file_ids[0]; + + // If compute_note_hash_and_nullifier is already defined by the user, we skip auto-generation in order to provide an + // escape hatch for this mechanism. + // TODO(#4647): improve this diagnosis and error messaging. + if collected_functions.iter().any(|coll_funcs_data| check_for_compute_note_hash_and_nullifier_definition(&coll_funcs_data.functions, module_id)) { + return Ok(()); + } + + // In order to implement compute_note_hash_and_nullifier, we need to know all of the different note types the + // contract might use. These are the types that implement the NoteInterface trait, which provides the + // get_note_type_id function. + let note_types = fetch_struct_trait_impls(context, unresolved_traits_impls, "NoteInterface"); + + // We can now generate a version of compute_note_hash_and_nullifier tailored for the contract in this crate. + let func = generate_compute_note_hash_and_nullifier(¬e_types); + + // And inject the newly created function into the contract. + + // TODO(#4373): We don't have a reasonable location for the source code of this autogenerated function, so we simply + // pass an empty span. This function should not produce errors anyway so this should not matter. + let location = Location::new(Span::empty(0), file_id); + + // These are the same things the ModCollector does when collecting functions: we push the function to the + // NodeInterner, declare it in the module (which checks for duplicate definitions), and finally add it to the list + // on collected but unresolved functions. + + let func_id = context.def_interner.push_empty_fn(); + context.def_interner.push_function(func_id, &func.def, ModuleId { krate: *crate_id, local_id: module_id }, location); + + context.def_map_mut(crate_id).unwrap() + .modules_mut()[module_id.0] + .declare_function( + func.name_ident().clone(), func_id + ).expect( + "Failed to declare the autogenerated compute_note_hash_and_nullifier function, likely due to a duplicate definition. See https://github.com/AztecProtocol/aztec-packages/issues/4647." + ); + + collected_functions.iter_mut() + .find(|fns| fns.file_id == file_id).expect("ICE: no functions found in contract file") + .push_fn(module_id, func_id, func.clone()); + + Ok(()) +} + +// Fetches the name of all structs that implement trait_name, both in the current crate and all of its dependencies. +fn fetch_struct_trait_impls( + context: &mut HirContext, + unresolved_traits_impls: &Vec, + trait_name: &str +) -> Vec { + let mut struct_typenames: Vec = Vec::new(); + + // These structs can be declared in either external crates or the current one. External crates that contain + // dependencies have already been processed and resolved, but are available here via the NodeInterner. Note that + // crates on which the current crate does not depend on may not have been processed, and will be ignored. + for trait_impl_id in 0..(&context.def_interner.next_trait_impl_id()).0 { + let trait_impl = &context.def_interner.get_trait_implementation(TraitImplId(trait_impl_id)); + + if trait_impl.borrow().ident.0.contents == *trait_name { + if let Type::Struct(s, _) = &trait_impl.borrow().typ { + struct_typenames.push(s.borrow().name.0.contents.clone()); + } else { + panic!("Found impl for {} on non-Struct", trait_name); + } + } + } + + // This crate's traits and impls have not yet been resolved, so we look for impls in unresolved_trait_impls. + struct_typenames.extend(unresolved_traits_impls.iter() + .filter(|trait_impl| + trait_impl.trait_path.segments.last().expect("ICE: empty trait_impl path").0.contents == *trait_name) + .filter_map(|trait_impl| match &trait_impl.object_type.typ { + UnresolvedTypeData::Named(path, _, _) => Some(path.segments.last().unwrap().0.contents.clone()), + _ => None, + })); + + struct_typenames +} + +fn generate_compute_note_hash_and_nullifier(note_types: &Vec) -> NoirFunction { + let function_source = generate_compute_note_hash_and_nullifier_source(note_types); + + let (function_ast, errors) = parse_program(&function_source); + if !errors.is_empty() { + dbg!(errors.clone()); + } + assert_eq!(errors.len(), 0, "Failed to parse Noir macro code. This is either a bug in the compiler or the Noir macro code"); + + let mut function_ast = function_ast.into_sorted(); + function_ast.functions.remove(0) +} + +fn generate_compute_note_hash_and_nullifier_source(note_types: &Vec) -> String { + // TODO(#4649): The serialized_note parameter is a fixed-size array, but we don't know what length it should have. + // For now we hardcode it to 20, which is the same as MAX_NOTE_FIELDS_LENGTH. + + if note_types.len() == 0 { + // TODO(#4520): Even if the contract does not include any notes, other parts of the stack expect for this + // function to exist, so we include a dummy version. We likely should error out here instead. + " + unconstrained fn compute_note_hash_and_nullifier( + contract_address: AztecAddress, + nonce: Field, + storage_slot: Field, + note_type_id: Field, + serialized_note: [Field; 20] + ) -> pub [Field; 4] { + [0, 0, 0, 0] + }".to_string() + } else { + // For contracts that include notes we do a simple if-else chain comparing note_type_id with the different + // get_note_type_id of each of the note types. + + let if_statements: Vec = note_types.iter().map(|note_type| format!( + "if (note_type_id == {0}::get_note_type_id()) {{ + note_utils::compute_note_hash_and_nullifier({0}::deserialize_content, note_header, serialized_note) + }}" + , note_type)).collect(); + + // TODO(#4520): error out on the else instead of returning a zero array + let full_if_statement = if_statements.join(" else ") + " + else { + [0, 0, 0, 0] + }"; + + format!(" + unconstrained fn compute_note_hash_and_nullifier( + contract_address: AztecAddress, + nonce: Field, + storage_slot: Field, + note_type_id: Field, + serialized_note: [Field; 20] + ) -> pub [Field; 4] {{ + let note_header = NoteHeader::new(contract_address, nonce, storage_slot); + + {} + }}", full_if_statement) +} + +} \ No newline at end of file diff --git a/noir/compiler/noirc_frontend/src/hir/def_collector/dc_crate.rs b/noir/compiler/noirc_frontend/src/hir/def_collector/dc_crate.rs index 0d1dd1b4337..b10ca0f0769 100644 --- a/noir/compiler/noirc_frontend/src/hir/def_collector/dc_crate.rs +++ b/noir/compiler/noirc_frontend/src/hir/def_collector/dc_crate.rs @@ -256,6 +256,15 @@ impl DefCollector { // Add the current crate to the collection of DefMaps context.def_maps.insert(crate_id, def_collector.def_map); + // TODO(#4653): generalize this function + for macro_processor in ¯o_processors { + macro_processor.process_unresolved_traits_impls(&crate_id, context, &def_collector.collected_traits_impls, &mut def_collector.collected_functions).unwrap_or_else( + |(macro_err, file_id)| { + errors.push((macro_err.into(), file_id)); + }, + ); + } + inject_prelude(crate_id, context, crate_root, &mut def_collector.collected_imports); for submodule in submodules { inject_prelude( diff --git a/noir/compiler/noirc_frontend/src/hir/def_map/mod.rs b/noir/compiler/noirc_frontend/src/hir/def_map/mod.rs index 8c985e88e0b..0372edfd031 100644 --- a/noir/compiler/noirc_frontend/src/hir/def_map/mod.rs +++ b/noir/compiler/noirc_frontend/src/hir/def_map/mod.rs @@ -135,6 +135,11 @@ impl CrateDefMap { pub fn modules(&self) -> &Arena { &self.modules } + + pub fn modules_mut(&mut self) -> &mut Arena { + &mut self.modules + } + pub fn krate(&self) -> CrateId { self.krate } diff --git a/noir/compiler/noirc_frontend/src/hir/mod.rs b/noir/compiler/noirc_frontend/src/hir/mod.rs index 4d3800f1a50..00bcb0cdebf 100644 --- a/noir/compiler/noirc_frontend/src/hir/mod.rs +++ b/noir/compiler/noirc_frontend/src/hir/mod.rs @@ -91,6 +91,10 @@ impl Context<'_, '_> { self.def_maps.get(crate_id) } + pub fn def_map_mut(&mut self, crate_id: &CrateId) -> Option<&mut CrateDefMap> { + self.def_maps.get_mut(crate_id) + } + /// Return the CrateId for each crate that has been compiled /// successfully pub fn crates(&self) -> impl Iterator + '_ { diff --git a/noir/compiler/noirc_frontend/src/lib.rs b/noir/compiler/noirc_frontend/src/lib.rs index eb00a61adf6..55f5fb1ca40 100644 --- a/noir/compiler/noirc_frontend/src/lib.rs +++ b/noir/compiler/noirc_frontend/src/lib.rs @@ -45,6 +45,7 @@ pub mod macros_api { pub use noirc_errors::Span; pub use crate::graph::CrateId; + use crate::hir::def_collector::dc_crate::{UnresolvedFunctions, UnresolvedTraitImpl}; pub use crate::hir::def_collector::errors::MacroError; pub use crate::hir_def::expr::{HirExpression, HirLiteral}; pub use crate::hir_def::stmt::HirStatement; @@ -74,6 +75,16 @@ pub mod macros_api { crate_id: &CrateId, context: &HirContext, ) -> Result; + + // TODO(#4653): generalize this function + fn process_unresolved_traits_impls( + &self, + _crate_id: &CrateId, + _context: &mut HirContext, + _unresolved_traits_impls: &Vec, + _collected_functions: &mut Vec, + ) -> Result<(), (MacroError, FileId)>; + /// Function to manipulate the AST after type checking has been completed. /// The AST after type checking has been done is called the HIR. fn process_typed_ast( diff --git a/noir/compiler/noirc_frontend/src/node_interner.rs b/noir/compiler/noirc_frontend/src/node_interner.rs index 0051c1b4f5f..1841c4b790c 100644 --- a/noir/compiler/noirc_frontend/src/node_interner.rs +++ b/noir/compiler/noirc_frontend/src/node_interner.rs @@ -371,7 +371,7 @@ impl TraitId { } #[derive(Debug, Eq, PartialEq, Hash, Clone, Copy)] -pub struct TraitImplId(usize); +pub struct TraitImplId(pub usize); #[derive(Debug, Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)] pub struct TraitMethodId { diff --git a/noir/noirc_macros/src/lib.rs b/noir/noirc_macros/src/lib.rs index 4337214d69f..55109dbfba0 100644 --- a/noir/noirc_macros/src/lib.rs +++ b/noir/noirc_macros/src/lib.rs @@ -1,3 +1,5 @@ +use noirc_frontend::hir::def_collector::dc_crate::UnresolvedFunctions; +use noirc_frontend::hir::def_collector::dc_crate::UnresolvedTraitImpl; use noirc_frontend::macros_api::parse_program; use noirc_frontend::macros_api::HirContext; use noirc_frontend::macros_api::SortedModule; @@ -16,6 +18,16 @@ impl MacroProcessor for AssertMessageMacro { transform(ast, crate_id) } + fn process_unresolved_traits_impls( + &self, + _crate_id: &CrateId, + _context: &mut HirContext, + _unresolevd_traits_impls: &Vec, + _collected_functions: &mut Vec, + ) -> Result<(), (MacroError, FileId)> { + Ok(()) + } + // This macro does not need to process any information after name resolution fn process_typed_ast( &self, diff --git a/noir/tooling/nargo_fmt/tests/expected/contract.nr b/noir/tooling/nargo_fmt/tests/expected/contract.nr index b80efeeb692..226667619f5 100644 --- a/noir/tooling/nargo_fmt/tests/expected/contract.nr +++ b/noir/tooling/nargo_fmt/tests/expected/contract.nr @@ -74,17 +74,6 @@ contract Benchmarking { fn broadcast(owner: Field) { emit_unencrypted_log(&mut context, storage.balances.at(owner).read()); } - - unconstrained fn compute_note_hash_and_nullifier( - contract_address: AztecAddress, - nonce: Field, - storage_slot: Field, - note_type_id: Field, - preimage: [Field; VALUE_NOTE_LEN] - ) -> [Field; 4] { - let note_header = NoteHeader::new(contract_address, nonce, storage_slot); - note_utils::compute_note_hash_and_nullifier(ValueNoteMethods, note_header, preimage) - } } // Uses the token bridge contract, which tells which input token we need to talk to and handles the exit funds to L1 diff --git a/noir/tooling/nargo_fmt/tests/input/contract.nr b/noir/tooling/nargo_fmt/tests/input/contract.nr index d10bfb745b6..f863a9dc955 100644 --- a/noir/tooling/nargo_fmt/tests/input/contract.nr +++ b/noir/tooling/nargo_fmt/tests/input/contract.nr @@ -70,17 +70,6 @@ contract Benchmarking { fn broadcast(owner: Field) { emit_unencrypted_log(&mut context, storage.balances.at(owner).read()); } - - unconstrained fn compute_note_hash_and_nullifier( - contract_address: AztecAddress, - nonce: Field, - storage_slot: Field, - note_type_id: Field, - preimage: [Field; VALUE_NOTE_LEN] - ) -> [Field; 4] { - let note_header = NoteHeader::new(contract_address, nonce, storage_slot); - note_utils::compute_note_hash_and_nullifier(ValueNoteMethods, note_header, preimage) - } } // Uses the token bridge contract, which tells which input token we need to talk to and handles the exit funds to L1 diff --git a/yarn-project/protocol-contracts/src/class-registerer/__snapshots__/index.test.ts.snap b/yarn-project/protocol-contracts/src/class-registerer/__snapshots__/index.test.ts.snap index 1e0c5fe95d3..763c59c36e7 100644 --- a/yarn-project/protocol-contracts/src/class-registerer/__snapshots__/index.test.ts.snap +++ b/yarn-project/protocol-contracts/src/class-registerer/__snapshots__/index.test.ts.snap @@ -3,122 +3,122 @@ exports[`ClassRegisterer returns canonical protocol contract 1`] = ` { "address": AztecAddress { - "asBigInt": 18885476286413803383198269230770707720385495724494788057562984154330701020921n, + "asBigInt": 12165572618278205561135319266893715614363974000709547171863995507972204751528n, "asBuffer": { "data": [ - 41, - 192, - 205, - 0, - 0, - 149, + 26, + 229, + 120, + 87, + 210, + 210, + 52, + 118, + 43, + 74, + 102, + 9, + 134, + 217, + 77, + 250, + 54, 27, - 186, - 138, - 245, - 32, - 173, - 85, + 6, + 163, + 9, + 77, + 124, + 119, + 221, + 13, 19, - 204, - 83, - 217, - 240, - 65, - 60, - 90, - 36, - 167, - 42, - 75, - 168, - 193, + 48, 120, - 148, - 192, - 190, - 249, + 81, + 106, + 168, ], "type": "Buffer", }, }, "contractClass": { "artifactHash": Fr { - "asBigInt": 12987763002449886210249343009930617501836480764589027470816200726753333386077n, + "asBigInt": 1364372310007692800585626333523730889923819928339145276751574119320046781646n, "asBuffer": { "data": [ - 28, - 182, - 208, - 56, - 31, - 162, - 181, - 120, - 194, - 173, - 145, - 119, - 205, - 23, - 92, - 17, - 148, - 169, - 179, - 132, + 3, + 4, + 53, + 21, + 164, + 29, + 233, + 4, + 193, + 147, 128, - 198, - 168, - 109, - 135, + 246, + 235, + 216, 242, - 101, - 160, - 56, - 218, - 155, - 93, + 66, + 55, + 97, + 55, + 80, + 84, + 212, + 24, + 60, + 24, + 13, + 127, + 108, + 77, + 239, + 196, + 206, ], "type": "Buffer", }, }, "id": Fr { - "asBigInt": 15610368298223903406763887747663253144646438360806401290390854210044956890867n, + "asBigInt": 15823458103336768291861774407076060748007261259174556009564711604871953507490n, "asBuffer": { "data": [ 34, - 131, - 39, - 197, - 16, - 11, - 210, - 53, - 13, - 80, - 129, - 233, - 196, - 47, - 124, - 112, - 85, - 229, - 146, - 176, - 58, - 247, - 80, + 251, + 194, + 137, + 56, + 109, + 23, + 89, + 23, 38, - 80, - 100, - 198, - 59, - 105, - 209, - 214, + 147, + 215, + 101, + 203, + 138, + 228, + 237, + 145, 243, + 61, + 16, + 54, + 71, + 253, + 142, + 222, + 138, + 190, + 21, + 41, + 228, + 162, ], "type": "Buffer", }, @@ -403,81 +403,81 @@ exports[`ClassRegisterer returns canonical protocol contract 1`] = ` }, "instance": { "address": AztecAddress { - "asBigInt": 18885476286413803383198269230770707720385495724494788057562984154330701020921n, + "asBigInt": 12165572618278205561135319266893715614363974000709547171863995507972204751528n, "asBuffer": { "data": [ - 41, - 192, - 205, - 0, - 0, - 149, + 26, + 229, + 120, + 87, + 210, + 210, + 52, + 118, + 43, + 74, + 102, + 9, + 134, + 217, + 77, + 250, + 54, 27, - 186, - 138, - 245, - 32, - 173, - 85, + 6, + 163, + 9, + 77, + 124, + 119, + 221, + 13, 19, - 204, - 83, - 217, - 240, - 65, - 60, - 90, - 36, - 167, - 42, - 75, - 168, - 193, + 48, 120, - 148, - 192, - 190, - 249, + 81, + 106, + 168, ], "type": "Buffer", }, }, "contractClassId": Fr { - "asBigInt": 15610368298223903406763887747663253144646438360806401290390854210044956890867n, + "asBigInt": 15823458103336768291861774407076060748007261259174556009564711604871953507490n, "asBuffer": { "data": [ 34, - 131, - 39, - 197, - 16, - 11, - 210, - 53, - 13, - 80, - 129, - 233, - 196, - 47, - 124, - 112, - 85, - 229, - 146, - 176, - 58, - 247, - 80, + 251, + 194, + 137, + 56, + 109, + 23, + 89, + 23, 38, - 80, - 100, - 198, - 59, - 105, - 209, - 214, + 147, + 215, + 101, + 203, + 138, + 228, + 237, + 145, 243, + 61, + 16, + 54, + 71, + 253, + 142, + 222, + 138, + 190, + 21, + 41, + 228, + 162, ], "type": "Buffer", }, diff --git a/yarn-project/protocol-contracts/src/class-registerer/index.ts b/yarn-project/protocol-contracts/src/class-registerer/index.ts index a4885c7d975..09d3cda26fc 100644 --- a/yarn-project/protocol-contracts/src/class-registerer/index.ts +++ b/yarn-project/protocol-contracts/src/class-registerer/index.ts @@ -13,5 +13,5 @@ export function getCanonicalClassRegisterer(): ProtocolContract { * @remarks This should not change often, hence we hardcode it to save from having to recompute it every time. */ export const ClassRegistererAddress = AztecAddress.fromString( - '0x29c0cd0000951bba8af520ad5513cc53d9f0413c5a24a72a4ba8c17894c0bef9', + '0x1ae57857d2d234762b4a660986d94dfa361b06a3094d7c77dd0d133078516aa8', ); diff --git a/yarn-project/protocol-contracts/src/instance-deployer/__snapshots__/index.test.ts.snap b/yarn-project/protocol-contracts/src/instance-deployer/__snapshots__/index.test.ts.snap index 3ca5288c62a..a30fc6c0ba0 100644 --- a/yarn-project/protocol-contracts/src/instance-deployer/__snapshots__/index.test.ts.snap +++ b/yarn-project/protocol-contracts/src/instance-deployer/__snapshots__/index.test.ts.snap @@ -3,122 +3,122 @@ exports[`InstanceDeployer returns canonical protocol contract 1`] = ` { "address": AztecAddress { - "asBigInt": 10674890382018500410451173831129216683967953609468942304388116555335197334423n, + "asBigInt": 3292754564016949916718877297115777598905061669834342030471417311460989046852n, "asBuffer": { "data": [ - 23, - 153, - 198, - 26, - 161, - 4, - 48, - 191, + 7, + 71, + 162, + 14, + 208, + 200, + 96, + 53, + 228, + 78, + 165, + 96, 111, - 236, + 48, + 222, + 69, + 159, + 64, + 181, + 92, + 94, + 130, + 1, + 38, + 64, + 170, + 85, + 69, 70, - 103, - 156, - 76, - 183, - 108, - 62, - 209, - 44, - 216, - 182, - 231, - 62, - 215, - 56, - 157, - 90, - 226, - 150, - 173, - 27, - 151, + 175, + 144, + 68, ], "type": "Buffer", }, }, "contractClass": { "artifactHash": Fr { - "asBigInt": 1546915255081493429210407932347415325264105027406908683791229627179136590869n, + "asBigInt": 14004623793815794786663172306952806542169300491436089583084017080489106515164n, "asBuffer": { "data": [ - 3, - 107, - 133, - 227, - 201, - 147, - 91, - 223, - 147, - 254, - 104, - 143, - 129, - 46, - 252, - 227, - 185, - 74, - 0, - 254, - 227, - 11, - 162, - 57, - 80, - 42, - 239, - 244, - 210, - 18, + 30, + 246, + 86, + 15, 36, - 21, + 14, + 183, + 120, + 232, + 198, + 163, + 244, + 175, + 96, + 159, + 150, + 221, + 22, + 123, + 72, + 71, + 20, + 2, + 134, + 20, + 176, + 2, + 23, + 9, + 248, + 164, + 220, ], "type": "Buffer", }, }, "id": Fr { - "asBigInt": 9040462806880011027940587321357143576447825874691049241890511565119666681463n, + "asBigInt": 18800681354736902372207149850261995699819228885238981092036778984892317144615n, "asBuffer": { "data": [ - 19, - 252, - 184, - 122, - 123, - 192, - 60, - 228, - 9, - 114, - 248, - 85, - 174, - 199, - 235, - 168, - 55, - 50, - 74, - 245, - 238, - 197, - 231, 41, - 29, - 105, - 17, - 105, + 144, + 206, + 253, + 46, + 88, + 237, + 15, + 191, + 185, + 124, 36, - 126, - 118, - 119, + 46, + 254, + 144, + 100, + 66, + 216, + 236, + 181, + 113, + 20, + 163, + 159, + 75, + 11, + 21, + 162, + 80, + 133, + 150, + 39, ], "type": "Buffer", }, @@ -311,81 +311,81 @@ exports[`InstanceDeployer returns canonical protocol contract 1`] = ` }, "instance": { "address": AztecAddress { - "asBigInt": 10674890382018500410451173831129216683967953609468942304388116555335197334423n, + "asBigInt": 3292754564016949916718877297115777598905061669834342030471417311460989046852n, "asBuffer": { "data": [ - 23, - 153, - 198, - 26, - 161, - 4, - 48, - 191, + 7, + 71, + 162, + 14, + 208, + 200, + 96, + 53, + 228, + 78, + 165, + 96, 111, - 236, + 48, + 222, + 69, + 159, + 64, + 181, + 92, + 94, + 130, + 1, + 38, + 64, + 170, + 85, + 69, 70, - 103, - 156, - 76, - 183, - 108, - 62, - 209, - 44, - 216, - 182, - 231, - 62, - 215, - 56, - 157, - 90, - 226, - 150, - 173, - 27, - 151, + 175, + 144, + 68, ], "type": "Buffer", }, }, "contractClassId": Fr { - "asBigInt": 9040462806880011027940587321357143576447825874691049241890511565119666681463n, + "asBigInt": 18800681354736902372207149850261995699819228885238981092036778984892317144615n, "asBuffer": { "data": [ - 19, - 252, - 184, - 122, - 123, - 192, - 60, - 228, - 9, - 114, - 248, - 85, - 174, - 199, - 235, - 168, - 55, - 50, - 74, - 245, - 238, - 197, - 231, 41, - 29, - 105, - 17, - 105, + 144, + 206, + 253, + 46, + 88, + 237, + 15, + 191, + 185, + 124, 36, - 126, - 118, - 119, + 46, + 254, + 144, + 100, + 66, + 216, + 236, + 181, + 113, + 20, + 163, + 159, + 75, + 11, + 21, + 162, + 80, + 133, + 150, + 39, ], "type": "Buffer", }, diff --git a/yarn-project/protocol-contracts/src/instance-deployer/index.ts b/yarn-project/protocol-contracts/src/instance-deployer/index.ts index 9605acf2def..5b4d71f37d6 100644 --- a/yarn-project/protocol-contracts/src/instance-deployer/index.ts +++ b/yarn-project/protocol-contracts/src/instance-deployer/index.ts @@ -9,5 +9,5 @@ export function getCanonicalInstanceDeployer(): ProtocolContract { } export const InstanceDeployerAddress = AztecAddress.fromString( - '0x1799c61aa10430bf6fec46679c4cb76c3ed12cd8b6e73ed7389d5ae296ad1b97', + '0x0747a20ed0c86035e44ea5606f30de459f40b55c5e82012640aa554546af9044', ); diff --git a/yarn-project/simulator/src/client/simulator.test.ts b/yarn-project/simulator/src/client/simulator.test.ts index a3924578b59..4eec4b5a3d1 100644 --- a/yarn-project/simulator/src/client/simulator.test.ts +++ b/yarn-project/simulator/src/client/simulator.test.ts @@ -42,7 +42,7 @@ describe('Simulator', () => { const contractAddress = AztecAddress.random(); const nonce = Fr.random(); const storageSlot = Fr.random(); - const noteTypeId = Fr.random(); + const noteTypeId = new Fr(8411110710111078111116101n); // TokenNote const createNote = (amount = 123n) => new Note([new Fr(amount), owner.toField(), Fr.random()]); @@ -50,8 +50,8 @@ describe('Simulator', () => { oracle.getFunctionArtifactByName.mockResolvedValue(artifact); const note = createNote(); - const valueNoteHash = hashFields(note.items); - const innerNoteHash = hashFields([storageSlot, valueNoteHash]); + const tokenNoteHash = hashFields(note.items); + const innerNoteHash = hashFields([storageSlot, tokenNoteHash]); const siloedNoteHash = siloCommitment(contractAddress, innerNoteHash); const uniqueSiloedNoteHash = computeUniqueCommitment(nonce, siloedNoteHash); const innerNullifier = hashFields([