From a0085606b2f1d192d84a60cd30789140092ae070 Mon Sep 17 00:00:00 2001 From: LHerskind Date: Tue, 19 Sep 2023 21:58:17 +0000 Subject: [PATCH] docs: small misc changes + ordering changes --- docs/docs/dev_docs/contracts/layout.md | 28 ++++++- .../dev_docs/contracts/syntax/contract.md | 26 ------- .../dev_docs/contracts/{ => syntax}/events.md | 78 ++++++++++--------- .../dev_docs/contracts/syntax/functions.md | 1 + .../docs/dev_docs/contracts/syntax/globals.md | 4 +- docs/docs/dev_docs/contracts/syntax/main.md | 4 +- .../docs/dev_docs/contracts/syntax/storage.md | 4 + docs/sidebars.js | 9 +-- .../src/contracts/token_contract/src/main.nr | 2 + 9 files changed, 81 insertions(+), 75 deletions(-) delete mode 100644 docs/docs/dev_docs/contracts/syntax/contract.md rename docs/docs/dev_docs/contracts/{ => syntax}/events.md (67%) diff --git a/docs/docs/dev_docs/contracts/layout.md b/docs/docs/dev_docs/contracts/layout.md index a6db7b63a6d2..2e6420cf75d0 100644 --- a/docs/docs/dev_docs/contracts/layout.md +++ b/docs/docs/dev_docs/contracts/layout.md @@ -1,4 +1,27 @@ -# Layout +--- +title: Structure +--- + +A contract is a collection of persistent [state variables](./syntax/state_variables.md), and [functions](./syntax/functions) which may manipulate these variables. Functions and state variables within a contract's scope are said to belong to that contract. A contract can only access and modify its own state. If a contract wishes to access or modify another contract's state, it must make a call to an external function of that other contract. For anything to happen on the Aztec network, an external function of a contract needs to be called. + +# Contract + +A contract may be declared and given a name using the `contract` keyword (see snippet below). By convention, contracts are named in `PascalCase`. + +```rust title="contract keyword" +// highlight-next-line +contract MyContract { + + // Imports + + // Storage + + // Functions +} +``` +:::info A note for vanilla Noir devs +There is no [`main()`](https://noir-lang.org/getting_started/breakdown/#mainnr) function within a Noir `contract` scope. This is because more than one function of a contract may be called and proven as external (as opposed to inlined by the compiler). +::: ## Directory structure @@ -11,4 +34,5 @@ Here's a common layout for a basic Aztec.nr Contract project: └── Nargo.toml <-- package and dependency management ``` -> See the vanilla Noir docs for [more info on packages](https://noir-lang.org/modules_packages_crates/crates_and_packages). +- See the vanilla Noir docs for [more info on packages](https://noir-lang.org/modules_packages_crates/crates_and_packages). +- You can review the structure of a complete contract in the token contract tutorial [here](../getting_started/token_contract_tutorial.md). diff --git a/docs/docs/dev_docs/contracts/syntax/contract.md b/docs/docs/dev_docs/contracts/syntax/contract.md deleted file mode 100644 index 7091ec63c5f1..000000000000 --- a/docs/docs/dev_docs/contracts/syntax/contract.md +++ /dev/null @@ -1,26 +0,0 @@ ---- -title: Structure ---- - -# Contract - -A contract is a collection of persistent [state variables](./state_variables.md), and [functions](./functions) which may manipulate these variables. Functions and state variables within a contract's scope are said to belong to that contract. A contract can only access and modify its own state. If a contract wishes to access or modify another contract's state, it must make a call to an external function of that other contract. For anything to happen on the Aztec network, an external function of a contract needs to be called. - -A contract may be declared and given a name using the `contract` keyword (see snippet below). By convention, contracts are named in `PascalCase`. - -```rust title="contract keyword" -// highlight-next-line -contract MyContract { - - // Imports - - // Storage - - // Functions -} -``` -:::info A note for vanilla Noir devs -There is no [`main()`](https://noir-lang.org/getting_started/breakdown/#mainnr) function within a Noir `contract` scope. This is because more than one function of a contract may be called and proven as external (as opposed to inlined by the compiler). -::: - -You can review the structure of a complete contract in the token contract tutorial [here](../../getting_started/token_contract_tutorial.md). diff --git a/docs/docs/dev_docs/contracts/events.md b/docs/docs/dev_docs/contracts/syntax/events.md similarity index 67% rename from docs/docs/dev_docs/contracts/events.md rename to docs/docs/dev_docs/contracts/syntax/events.md index 36a809fde361..2c47231e139c 100644 --- a/docs/docs/dev_docs/contracts/events.md +++ b/docs/docs/dev_docs/contracts/syntax/events.md @@ -5,12 +5,14 @@ title: Events Events in Aztec work similarly to Ethereum events in the sense that they are a way for contracts to communicate with the outside world. They are emitted by contracts and stored inside each instance of an AztecNode. -> Aztec events are currently represented as raw data and are not ABI encoded. -> ABI encoded events are a feature that will be added in the future. +:::info +Aztec events are currently represented as raw data and are not ABI encoded. +ABI encoded events are a feature that will be added in the future. +::: Unlike on Ethereum, there are 2 types of events supported by Aztec: encrypted and unencrypted. -### Encrypted Events +## Encrypted Events Encrypted events can only be emitted by private functions and are encrypted using a public key of a recipient. For this reason it is necessary to register a recipient in the Aztec RPC Server before encrypting the events for them. @@ -51,13 +53,15 @@ await aztecRpc.registerRecipient(completeAddress); -> **NOTE**: If a note recipient is one of the accounts inside the Aztec RPC Server, we don't need to register it as a recipient because we already have the public key available. +:::info +If a note recipient is one of the accounts inside the Aztec RPC Server, we don't need to register it as a recipient because we already have the public key available. -> At this point the Sandbox only enables the emitting of encrypted note preimages through encrypted events. -> In the future we will allow emitting arbitrary information. -> (If you currently emit arbitrary information, Aztec RPC Server will fail to decrypt, process and store this data, so it will not be queryable). +At this point the Sandbox only enables the emitting of encrypted note preimages through encrypted events. +In the future we will allow emitting arbitrary information. +(If you currently emit arbitrary information, Aztec RPC Server will fail to decrypt, process and store this data, so it will not be queryable). +::: -To emit encrypted logs first import the `emit_encrypted_log` utility function inside your contract: +To emit encrypted logs first import the `emit_encrypted_log` utility function which wraps an oracle: #include_code encrypted_import /yarn-project/aztec-nr/value-note/src/utils.nr rust @@ -65,15 +69,33 @@ Then you can call the function: #include_code encrypted /yarn-project/aztec-nr/value-note/src/utils.nr rust -### Unencrypted Events +### Processing Encrypted Events -Unencrypted events are events which can be read by anyone. -They can be emitted by both public and private functions. +One function of Aztec RPC Server is constantly loading encrypted logs from AztecNode and trying to decrypt them. +When new encrypted logs are obtained, the Aztec RPC Server will try to decrypt them using the private encryption key of all the accounts registered inside Aztec RPC Server. +If the decryption is successful, the Aztec RPC Server will store the decrypted note inside a database. +If the decryption fails, the specific log will be discarded. + +For the Aztec RPC Server 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 Aztec RPC Server 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 function has private state variables, you **MUST** include a `compute_note_hash_and_nullifier` function to allow the RPC to process encrypted events. +::: + +Every time a new note is successfully decrypted, the Aztec RPC Server will expect the existence of a `compute_note_hash_and_nullifier` function, which must teach it how to correctly process the new note. -Emitting unencrypted events from private function is a significant privacy leak and it should be considered by the developer whether it is acceptable. +#include_code compute_note_hash_and_nullifier /yarn-project/noir-contracts/src/contracts/token_contract/src/main.nr rust + +## Unencrypted Events + +Unencrypted events are events which can be read by anyone. +They can be emitted by both public and private functions. + +:::danger +- Emitting unencrypted events from private function is a significant privacy leak and it should be considered by the developer whether it is acceptable. +- Unencrypted events are currently **NOT** linked to the contract emitting them, so it is practically a [`debug_log`](./functions.md#a-few-useful-inbuilt-oracles). ::: To emit unencrypted logs first import the `emit_unencrypted_log` utility function inside your contract: @@ -104,31 +126,11 @@ aztec-cli get-logs --from 5 --limit 1 All event data is pushed to Ethereum as calldata by the sequencer and for this reason the cost of emitting an event is non-trivial. -> Note: the cost of submitting calldata to Ethereum is currently 4 gas per byte. Currently, in the Sandbox, an encypted note has a fixed overhead of 4 field elements (to broadcast an ephemeral public key, a contract address, and a storage slot); plus a variable number of field elements depending on the type of note being emitted. -> A `ValueNote`, for example, currently uses 3 fields elements (plus the fixed overhead of 4). That's roughly `7 * 32 = 224` bytes of information, costing roughly 896 gas. - -> There are plans to compress encrypted note data further. -> There are plans to adopt EIP-4844 blobs to reduce the cost of data submission further. - -## Processing events - -Both the encrypted and unencrypted events are stored in AztecNode. -Unencrypted logs can be queried by anyone as we described above in the [Unencrypted Events](#unencrypted-events) section. - -Encrypted logs need to first be decrypted: - -### Decrypting - -One function of Aztec RPC Server is constantly loading encrypted logs from AztecNode and trying to decrypt them. -When new encrypted logs are obtained, the Aztec RPC Server will try to decrypt them using the private encryption key of all the accounts registered inside Aztec RPC Server. -If the decryption is successful, the Aztec RPC Server will store the decrypted note inside a database. -If the decryption fails, the specific log will be discarded. - -For the Aztec RPC Server 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 customise how a note's note hash and nullifier should be computed. Because of this customisability, and because there will be a potentially-unlimited number of smart contracts deployed to Aztec, an Aztec RPC Server 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. - -Every time a new note is successfully decrypted, the Aztec RPC Server will expect the existence of a `compute_note_hash_and_nullifier` function, which must teach it how to correctly process the new note. +:::info +the cost of submitting calldata to Ethereum is currently 4 gas per byte. Currently, in the Sandbox, an encrypted note has a fixed overhead of 4 field elements (to broadcast an ephemeral public key, a contract address, and a storage slot); plus a variable number of field elements depending on the type of note being emitted. -This is an example implementation inside the `PrivateTokenContract`: +A `ValueNote`, for example, currently uses 3 fields elements (plus the fixed overhead of 4). That's roughly `7 * 32 = 224` bytes of information. -#include_code compute_note_hash_and_nullifier /yarn-project/noir-contracts/src/contracts/private_token_contract/src/main.nr rust +- There are plans to compress encrypted note data further. +- There are plans to adopt EIP-4844 blobs to reduce the cost of data submission further. +::: \ No newline at end of file diff --git a/docs/docs/dev_docs/contracts/syntax/functions.md b/docs/docs/dev_docs/contracts/syntax/functions.md index c40926656ef5..97249f8a9659 100644 --- a/docs/docs/dev_docs/contracts/syntax/functions.md +++ b/docs/docs/dev_docs/contracts/syntax/functions.md @@ -102,6 +102,7 @@ Oracles introduce **non-determinism** into a circuit, and thus are `unconstraine - [`auth_witness`](https://github.com/AztecProtocol/aztec-packages/blob/master/yarn-project/aztec-nr/aztec/src/oracle/auth_witness.nr) - Provides a way to fetch the authentication witness for a given address. This is useful when building account contracts to support approve-like functionality. - [`get_l1_to_l2_message`](https://github.com/AztecProtocol/aztec-packages/blob/master/yarn-project/aztec-nr/aztec/src/oracle/get_l1_to_l2_message.nr) - Useful for application that receive messages from L1 to be consumed on L2, such as token bridges or other cross-chain applications. - [`notes`](https://github.com/AztecProtocol/aztec-packages/blob/master/yarn-project/aztec-nr/aztec/src/oracle/notes.nr) - Provides a lot of functions related to notes, such as fetches notes from storage etc, used behind the scenes for value notes and other pre-build note implementations. +- [`logs`](https://github.com/AztecProtocol/aztec-packages/blob/master/yarn-project/aztec-nr/aztec/src/oracle/logs.nr) - Provides the to log encrypted and unencrypted data. --- diff --git a/docs/docs/dev_docs/contracts/syntax/globals.md b/docs/docs/dev_docs/contracts/syntax/globals.md index 39dc32abdff6..1a50ac54066a 100644 --- a/docs/docs/dev_docs/contracts/syntax/globals.md +++ b/docs/docs/dev_docs/contracts/syntax/globals.md @@ -47,6 +47,6 @@ context.block_number(); ``` :::info *Why do the available global variables differ per execution environment?* -> The global variables are constrained by the proving environment. In the case of public functions, they are executed on a sequencer that will know the timestamp and number of the next block ( as they are the block producer ). -> In the case of private functions, we cannot be sure which block our transaction will be included in, hence we can not guarantee values for the timestamp or block number. +The global variables are constrained by the proving environment. In the case of public functions, they are executed on a sequencer that will know the timestamp and number of the next block ( as they are the block producer ). +In the case of private functions, we cannot be sure which block our transaction will be included in, hence we can not guarantee values for the timestamp or block number. ::: \ No newline at end of file diff --git a/docs/docs/dev_docs/contracts/syntax/main.md b/docs/docs/dev_docs/contracts/syntax/main.md index 870466e98de8..c47a2879cc91 100644 --- a/docs/docs/dev_docs/contracts/syntax/main.md +++ b/docs/docs/dev_docs/contracts/syntax/main.md @@ -11,10 +11,10 @@ Aztec.nr contains abstractions which remove the need to understand the low-level - Public and private [state variable types](./state_variables.md) - Some pre-designed notes - Functions for [emitting](../events.md) encrypted and unencrypted logs -- [Oracle functions](./functions.md#oracle-calls) for accessing: +- [Oracle functions](./functions.md#oracle-functions) for accessing: - private state - secrets -- Functions for communicating with Ethereum L1 +- Functions for communicating with [Ethereum L1](./messaging.md) To import Aztec.nr into your Aztec contract project, simply include it as a dependency. For example: diff --git a/docs/docs/dev_docs/contracts/syntax/storage.md b/docs/docs/dev_docs/contracts/syntax/storage.md index e809aa1faf7c..2e7019daeba3 100644 --- a/docs/docs/dev_docs/contracts/syntax/storage.md +++ b/docs/docs/dev_docs/contracts/syntax/storage.md @@ -7,6 +7,10 @@ An example of such a struct could be as follow: #include_code storage-struct-declaration /yarn-project/noir-contracts/src/contracts/docs_example_contract/src/main.nr rust +:::info +If your storage include private state variables it must include a `compute_note_hash_and_nullifier` function to allow the RPC to process encrypted events, see [encrypted events](./../events.md#processing-encrypted-events) for more. +::: + In here, we are setting up a mix of public and private state variables. The public state variables can be read by anyone, and functions manipulating them are executed by the sequence, we will see more to this in [functions](./functions.md#public-functions) in a few moments. The private state variables are only readable by their owner, or people whom the owner have shared the data with. As mentioned earlier in the foundational concepts ([state model](./../../../concepts/foundation/state_model.md) and [private/public execution](./../../../concepts/foundation/communication/public_private_calls.md)) private state are following a UTXO model where only the people knowing the pre-images of the commitments in the state will be able to use that knowledge. diff --git a/docs/sidebars.js b/docs/sidebars.js index 84995d845b59..099c6b0a2300 100644 --- a/docs/sidebars.js +++ b/docs/sidebars.js @@ -211,10 +211,6 @@ const sidebars = { items: [ "dev_docs/contracts/workflow", "dev_docs/contracts/layout", - "dev_docs/contracts/events", - "dev_docs/contracts/compiling", - "dev_docs/contracts/deploying", - "dev_docs/contracts/artifacts", { label: "Syntax", type: "category", @@ -223,9 +219,9 @@ const sidebars = { id: "dev_docs/contracts/syntax/main", }, items: [ - "dev_docs/contracts/syntax/contract", "dev_docs/contracts/syntax/storage", "dev_docs/contracts/syntax/state_variables", + "dev_docs/contracts/syntax/events", "dev_docs/contracts/syntax/functions", "dev_docs/contracts/syntax/context", "dev_docs/contracts/syntax/globals", @@ -246,6 +242,9 @@ const sidebars = { "dev_docs/contracts/portals/outbox", ], }, + "dev_docs/contracts/compiling", + "dev_docs/contracts/deploying", + "dev_docs/contracts/artifacts", // { // label: "Resources", // type: "category", diff --git a/yarn-project/noir-contracts/src/contracts/token_contract/src/main.nr b/yarn-project/noir-contracts/src/contracts/token_contract/src/main.nr index 5738b981346f..ef6a58c5bd1c 100644 --- a/yarn-project/noir-contracts/src/contracts/token_contract/src/main.nr +++ b/yarn-project/noir-contracts/src/contracts/token_contract/src/main.nr @@ -418,6 +418,7 @@ contract Token { // Below this point is the stuff of nightmares. // This should ideally not be required. What do we do if vastly different types of preimages? + // 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. @@ -429,5 +430,6 @@ contract Token { note_utils::compute_note_hash_and_nullifier(ValueNoteMethods, note_header, preimage) } } + // docs:end:compute_note_hash_and_nullifier }