Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat!: autogenerate compute_note_hash_and_nullifier #4610

Merged
merged 19 commits into from
Feb 19, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions avm-transpiler/Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

13 changes: 0 additions & 13 deletions boxes/blank/src/contracts/src/main.nr
Original file line number Diff line number Diff line change
Expand Up @@ -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]
}
}
27 changes: 1 addition & 26 deletions boxes/token/src/contracts/src/main.nr
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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
nventuro marked this conversation as resolved.
Show resolved Hide resolved
// 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
22 changes: 4 additions & 18 deletions boxes/token/src/contracts/src/types/token_note.nr
Original file line number Diff line number Diff line change
@@ -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;

Expand Down
19 changes: 0 additions & 19 deletions docs/docs/developers/contracts/references/storage/main.md
Original file line number Diff line number Diff line change
Expand Up @@ -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] {}
}
```

Expand Down Expand Up @@ -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.
Expand Down Expand Up @@ -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.
Expand Down
11 changes: 0 additions & 11 deletions docs/docs/developers/contracts/resources/common_patterns/main.md
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
10 changes: 0 additions & 10 deletions docs/docs/developers/debugging/aztecnr-errors.md
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down
10 changes: 2 additions & 8 deletions docs/docs/developers/getting_started/aztecnr-getting-started.md
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down Expand Up @@ -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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
10 changes: 0 additions & 10 deletions docs/docs/developers/tutorials/writing_private_voting_contract.md
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down
19 changes: 0 additions & 19 deletions docs/docs/developers/tutorials/writing_token_contract.md
Original file line number Diff line number Diff line change
Expand Up @@ -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] {}
}
```

Expand Down Expand Up @@ -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.
Expand Down Expand Up @@ -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.
Expand Down
8 changes: 8 additions & 0 deletions docs/docs/misc/migration_notes.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
2 changes: 1 addition & 1 deletion noir-projects/aztec-nr/value-note/src/balance_utils.nr
Original file line number Diff line number Diff line change
@@ -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<ValueNote>) -> Field {
get_balance_with_offset(set, 0)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -113,20 +113,8 @@ contract AvmTest {
fn getTimestamp() -> pub Field {
context.timestamp()
}

// #[aztec(public-vm)]
// fn getContractCallDepth() -> pub Field {
// context.contract_call_depth()
// }
nventuro marked this conversation as resolved.
Show resolved Hide resolved

// 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]
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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},
Expand Down Expand Up @@ -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)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ use dep::std::{
option::Option,
};
use dep::value_note::{
value_note::{ValueNote, VALUE_NOTE_LEN},
value_note::ValueNote,
};

struct Card {
Expand Down
Loading
Loading