diff --git a/docs/pages/aztec-js/creating-accounts.md b/docs/pages/aztec-js/creating-accounts.md new file mode 100644 index 0000000..525e85f --- /dev/null +++ b/docs/pages/aztec-js/creating-accounts.md @@ -0,0 +1,2 @@ +# Creating Accounts + diff --git a/docs/pages/aztec-js/deploying-contracts.md b/docs/pages/aztec-js/deploying-contracts.md new file mode 100644 index 0000000..c1582f5 --- /dev/null +++ b/docs/pages/aztec-js/deploying-contracts.md @@ -0,0 +1,2 @@ +# Deploying Contracts + diff --git a/docs/pages/aztec-js/getting-started.md b/docs/pages/aztec-js/getting-started.md deleted file mode 100644 index e69de29..0000000 diff --git a/docs/pages/aztec-js/installation.md b/docs/pages/aztec-js/installation.md new file mode 100644 index 0000000..4026317 --- /dev/null +++ b/docs/pages/aztec-js/installation.md @@ -0,0 +1,2 @@ +# Installation + diff --git a/docs/pages/aztec-js/paying-fees.md b/docs/pages/aztec-js/paying-fees.md new file mode 100644 index 0000000..2a07fa8 --- /dev/null +++ b/docs/pages/aztec-js/paying-fees.md @@ -0,0 +1,2 @@ +# Paying Fees + diff --git a/docs/pages/aztec-js/sending-transactions.md b/docs/pages/aztec-js/sending-transactions.md new file mode 100644 index 0000000..2817ede --- /dev/null +++ b/docs/pages/aztec-js/sending-transactions.md @@ -0,0 +1,2 @@ +# Sending Transactions + diff --git a/docs/pages/aztec-js/simulating-transactions.md b/docs/pages/aztec-js/simulating-transactions.md new file mode 100644 index 0000000..f0d9687 --- /dev/null +++ b/docs/pages/aztec-js/simulating-transactions.md @@ -0,0 +1,2 @@ +# Simulating Transactions + diff --git a/docs/pages/aztec-js/testing-contracts.md b/docs/pages/aztec-js/testing-contracts.md new file mode 100644 index 0000000..a941258 --- /dev/null +++ b/docs/pages/aztec-js/testing-contracts.md @@ -0,0 +1,2 @@ +# Testing Contracts + diff --git a/docs/pages/aztec-js/using-authorizing-other-accounts.md b/docs/pages/aztec-js/using-authorizing-other-accounts.md new file mode 100644 index 0000000..0d4e768 --- /dev/null +++ b/docs/pages/aztec-js/using-authorizing-other-accounts.md @@ -0,0 +1,2 @@ +# Using Authorizing Other Accounts + diff --git a/docs/pages/aztec-nargo/getting-started.md b/docs/pages/aztec-nargo/getting-started.md deleted file mode 100644 index e69de29..0000000 diff --git a/docs/pages/aztec-nargo/overview.md b/docs/pages/aztec-nargo/overview.md deleted file mode 100644 index 9103638..0000000 --- a/docs/pages/aztec-nargo/overview.md +++ /dev/null @@ -1 +0,0 @@ -# Aztec Nargo Overview \ No newline at end of file diff --git a/docs/pages/aztec-nargo/reference.md b/docs/pages/aztec-nargo/reference.md deleted file mode 100644 index 1d0bbbc..0000000 --- a/docs/pages/aztec-nargo/reference.md +++ /dev/null @@ -1 +0,0 @@ -# Aztec Nargo Reference \ No newline at end of file diff --git a/docs/pages/aztec-nr/advanced-features/custom-notes.md b/docs/pages/aztec-nr/advanced-features/custom-notes.md deleted file mode 100644 index 1443568..0000000 --- a/docs/pages/aztec-nr/advanced-features/custom-notes.md +++ /dev/null @@ -1,338 +0,0 @@ -# Custom Notes - -This guide shows you how to create custom note types for storing specialized private data in your Aztec contracts. Notes are the fundamental data structure in Aztec when working with private state. - -## Prerequisites - -- Basic understanding of Aztec private state -- Familiarity with notes and UTXOs -- Aztec development environment set up - -## Why create custom notes? - -You may want to create your own note type if you need to: - -- Use a specific type of private data or struct not already implemented in Aztec.nr -- Experiment with custom note hashing and nullifier schemes -- Store multiple pieces of related data together (e.g., a card in a game with multiple attributes) -- Optimize storage by combining data that's used together - - -:::info Built-in Note Types -Aztec.nr provides pre-built note types for common use cases: - -**ValueNote** - For numeric values like token balances: -```toml -# In Nargo.toml -value_note = { git="https://github.com/AztecProtocol/aztec-packages/", tag="#include_aztec_version", directory="noir-projects/smart-contracts/value-note" } -``` -```rust -use value_note::value_note::ValueNote; -let note = ValueNote::new(100, owner); -``` - -**AddressNote** - For storing Aztec addresses: -```toml -# In Nargo.toml -address_note = { git="https://github.com/AztecProtocol/aztec-packages/", tag="#include_aztec_version", directory="noir-projects/smart-contracts/address-note" } -``` -```rust -use address_note::address_note::AddressNote; -let note = AddressNote::new(stored_address, owner); -``` - -If these don't meet your needs, continue reading to create your own custom note type. -::: - - -## Standard note implementation - -### Creating a custom note struct - -Define your custom note with the `#[note]` macro: - -```rust -use aztec::{ - macros::notes::note, - oracle::random::random, - protocol_types::{address::AztecAddress, traits::Packable}, -}; - -// The #[note] macro marks this struct as a note type -// Required traits: -// - Eq: Allows equality comparisons between notes -// - Packable: Enables efficient packing/unpacking of the note's data -#[derive(Eq, Packable)] -#[note] -pub struct CustomNote { - // Application-specific data - value: Field, - data: u32, - // Required fields for all notes - owner: AztecAddress, // Used for access control and nullifier generation - randomness: Field, // Prevents brute-force attacks on note contents -} -``` - -The `#[note]` macro automatically implements other required traits for your note type (ex. the `NoteHash` trait). - -### Required fields - -Every custom note needs these essential fields: - -1. **Application data**: Your specific fields (e.g., `value`, `amount`, `token_id`) -2. **Owner**: Used for nullifier generation and access control (must be `AztecAddress` type) -3. **Randomness**: Prevents brute-force attacks on note contents (must be `Field` type) - -The order of fields doesn't matter, but convention is to put application data first, then owner, then randomness: - -```rust -#[derive(Eq, Packable)] -#[note] -pub struct MyNote { - // Application-specific data - data: Field, - amount: u128, - - // Required fields - owner: AztecAddress, - randomness: Field, -} -``` - -### Why randomness matters - -Without randomness, note contents can be guessed through brute force. For example, if you know someone's Aztec address, you could try hashing it with many potential values to find which note hash in the tree belongs to them. - -### Why owner is important - -The `owner` field provides two critical functions: - -1. **Access control**: Ensures only the owner can spend the note -2. **Privacy from sender**: Prevents the sender from tracking when a note is spent - -Without using the owner's nullifier key, a sender could derive the nullifier offchain and monitor when it appears in the nullifier tree, breaking privacy. - -### Implementing note methods - -A note is just a Struct, so you can add whatever methods you need. For example, you can add a constructor and helper methods: - -```rust -impl CustomNote { - pub fn new(value: Field, data: u32, owner: AztecAddress) -> Self { - // Safety: We use randomness to preserve privacy. The sender already knows - // the full note pre-image, so we trust them to cooperate in random generation - let randomness = unsafe { random() }; - - CustomNote { value, data, owner, randomness } - } - - pub fn get_value(self) -> Field { - self.value - } - - pub fn get_data(self) -> u32 { - self.data - } -} -``` - -## Custom note with custom hashing - -For complete control over note hashing and nullifier generation, use the `#[custom_note]` macro: - -```rust -use dep::aztec::{ - context::PrivateContext, - macros::notes::custom_note, - note::note_interface::NoteHash, - protocol_types::{ - constants::{GENERATOR_INDEX__NOTE_HASH, GENERATOR_INDEX__NOTE_NULLIFIER}, - hash::poseidon2_hash_with_separator, - traits::Packable, - }, -}; - -// TransparentNote for public-to-private transitions -// No owner field needed - security comes from secret knowledge -#[derive(Eq, Packable)] -#[custom_note] -pub struct TransparentNote { - amount: u128, - secret_hash: Field, // Hash of a secret that must be known to spend -} - -impl NoteHash for TransparentNote { - fn compute_note_hash(self, storage_slot: Field) -> Field { - let inputs = self.pack().concat([storage_slot]); - poseidon2_hash_with_separator(inputs, GENERATOR_INDEX__NOTE_HASH) - } - - // Custom nullifier that doesn't use owner's key - // Security is enforced by requiring the secret preimage - fn compute_nullifier( - self, - _context: &mut PrivateContext, - note_hash_for_nullify: Field, - ) -> Field { - poseidon2_hash_with_separator( - [note_hash_for_nullify], - GENERATOR_INDEX__NOTE_NULLIFIER as Field, - ) - } - - unconstrained fn compute_nullifier_unconstrained( - self, - note_hash_for_nullify: Field - ) -> Field { - self.compute_nullifier(zeroed(), note_hash_for_nullify) - } -} -``` - -This pattern is useful for "shielding" tokens - creating notes in public that can be redeemed in private by anyone who knows the secret. - -## Using custom notes in storage - -Declare your custom note type in contract storage: - -```rust -#[storage] -struct Storage { - // Map from owner address to their notes - private_notes: Map, Context>, - - // Single immutable note - config_note: PrivateImmutable, -} -``` - -## Working with custom notes - -### Creating and storing notes - -```rust -#[private] -fn create_note(owner: AztecAddress, value: Field, data: u32) { - // Create the note - let note = CustomNote::new(value, data, owner); - - // Store it in the owner's note set - storage.private_notes.at(owner).insert(note); -} -``` - -### Reading notes - -```rust -use aztec::note::note_getter_options::NoteGetterOptions; - -#[private] -fn get_notes(owner: AztecAddress) -> BoundedVec { - // Get all notes for the owner - let notes = storage.private_notes.at(owner).get_notes( - NoteGetterOptions::new() - ); - - notes -} - -#[private] -fn find_note_by_value(owner: AztecAddress, target_value: Field) -> CustomNote { - let options = NoteGetterOptions::new() - .select(CustomNote::properties().value, target_value, Option::none()) - .set_limit(1); - - let notes = storage.private_notes.at(owner).get_notes(options); - assert(notes.len() == 1, "Note not found"); - - notes.get(0) -} -``` - -### Transferring notes - -To transfer a custom note between users: - -```rust -#[private] -fn transfer_note(from: AztecAddress, to: AztecAddress, value: Field) { - // Find and remove from sender (nullifies the old note) - let note = find_note_by_value(from, value); - storage.private_notes.at(from).remove(note); - - // Create new note for recipient with same value but new owner - let new_note = CustomNote::new(note.value, note.data, to); - storage.private_notes.at(to).insert(new_note); -} -``` - -## Common patterns - -### Singleton notes - -For data that should have only one instance per user: - -```rust -#[note] -pub struct ProfileNote { - owner: AztecAddress, - data: Field, - randomness: Field, -} - -#[private] -fn update_profile(new_data: Field) { - let owner = context.msg_sender(); - - // Remove old profile if exists - let old_notes = storage.profiles.at(owner).get_notes( - NoteGetterOptions::new().set_limit(1) - ); - if old_notes.len() > 0 { - storage.profiles.at(owner).remove(old_notes[0]); - } - - // Create new profile - let new_profile = ProfileNote::new(owner, new_data); - storage.profiles.at(owner).insert(new_profile); -} -``` - -### Filtering notes - -For efficient lookups by specific fields: - -```rust -use aztec::note::note_getter_options::{NoteGetterOptions, PropertySelector}; - -#[derive(Eq, Packable)] -#[note] -pub struct OrderNote { - order_id: Field, // Field we want to filter by - amount: u128, - owner: AztecAddress, - randomness: Field, -} - -// Usage - filter by order_id -fn get_order(owner: AztecAddress, target_id: Field) -> OrderNote { - let options = NoteGetterOptions::new() - .select(OrderNote::properties().order_id, target_id, Option::none()) - .set_limit(1); - - let notes = storage.orders.at(owner).get_notes(options); - assert(notes.len() == 1, "Order not found"); - notes.get(0) -} -``` - -## Further reading - -- What the `#[note]` macro does - See concepts section -- Note lifecycle and nullifiers - See concepts section -- Advanced note patterns - See advanced features -- Note portals for L1 communication - See cross-chain interactions -- Macros reference - See reference section -- Keys, including npk_m_hash - See concepts section - diff --git a/docs/pages/aztec-nr/advanced-features/index.md b/docs/pages/aztec-nr/advanced-features/index.md deleted file mode 100644 index 212bb75..0000000 --- a/docs/pages/aztec-nr/advanced-features/index.md +++ /dev/null @@ -1,18 +0,0 @@ -# Advanced Features - -Advanced features and patterns for Aztec.nr smart contracts. - -## Overview - -This section covers advanced capabilities in Aztec.nr for developers who want to build more sophisticated applications. - -## Topics - -### Custom Notes -Learn how to create custom note types for specialized privacy requirements. Notes are the fundamental building blocks of private state in Aztec. - -[Learn about Custom Notes →](./custom-notes) - -## Coming Soon - -More advanced features will be documented here as they become available. diff --git a/docs/pages/aztec-nr/common-patterns.md b/docs/pages/aztec-nr/common-patterns.md index 0ca0fcc..45c00f5 100644 --- a/docs/pages/aztec-nr/common-patterns.md +++ b/docs/pages/aztec-nr/common-patterns.md @@ -1,5 +1,7 @@ # Common Patterns +## Mike's notes + - Mutating Private State Variables via Private Functions - Mutating your own state - Mutating someone else's state diff --git a/docs/pages/aztec-nr/common-use-cases.md b/docs/pages/aztec-nr/common-use-cases.md deleted file mode 100644 index e12feb6..0000000 --- a/docs/pages/aztec-nr/common-use-cases.md +++ /dev/null @@ -1,21 +0,0 @@ -# Common Use Cases - -1. Account Contract -2. Account Abstraction (concept) -6. Storing a Public Key -7. Authwits (point to Authwits section) -8. Callable by someone else. -9. Multisigs -10. Key Rotation -11. Storing Secret Keys -12. Examples -2. Bridging -3. Escrow -4. Fee-Payment Contracts -5. Fee Abstraction (concept) -6. Risks -7. Bridging Fee Juice into Aztec -8. Deciding whether to accept a user's fee -9. Execution phases -5. Paymasters - - Point to Fee-Payment Contracts \ No newline at end of file diff --git a/docs/pages/aztec-nr/compile.md b/docs/pages/aztec-nr/compile.md index e3cee10..e219b55 100644 --- a/docs/pages/aztec-nr/compile.md +++ b/docs/pages/aztec-nr/compile.md @@ -1,89 +1,10 @@ # Compile +## Mike's notes + 1. Tools 2. Commands 3. Unravelling Macros 4. Contract Artifacts 5. Structure of an Artifact -5. Common Errors - -This guide shows you how to compile your Aztec contracts into artifacts ready for deployment and interaction. - -## Prerequisites - -- An Aztec contract written in Aztec.nr -- `aztec-nargo` installed (included with the sandbox) -- Contract project with proper `Nargo.toml` configuration - -## Compile your contract - -### Step 1: Compile to JSON artifacts - -Compile your Noir contracts to generate JSON artifacts: - -```bash -aztec-nargo compile -``` - -This outputs contract artifacts to the `target` folder. - -### Step 2: Process for Aztec - -Process the artifacts for Aztec compatibility: - -```bash -aztec-postprocess-contract -``` - -This step: -- Transpiles functions for the Aztec VM -- Generates verification keys for private functions -- Caches keys for faster subsequent compilations - -:::note -The `aztec-nargo compile` command looks for `Nargo.toml` files by ascending up the parent directories, and will compile the top-most Nargo.toml file it finds. -Eg: if you are in `/hobbies/cool-game/contracts/easter-egg/`, and both `cool-game` and `easter-egg` contain a Nargo.toml file, then `aztec-nargo compile` will be performed on `cool-game/Nargo.toml` and compile the project(s) specified within it. Eg - -``` -[workspace] -members = [ - "contracts/easter-egg", -] -``` - -The `aztec-postprocess-contract` command will process all contract artifacts it finds in `target` directories within the current directory tree. -::: - -## Use generated interfaces - -The compiler automatically generates type-safe interfaces for contract interaction. - -### Import and use contract interfaces - -Use generated interfaces instead of manual function calls: - -```rust -contract FPC { - use dep::token::Token; - - #[private] - fn fee_entrypoint_private(amount: Field, asset: AztecAddress, secret_hash: Field, nonce: Field) { - assert(asset == storage.other_asset.read()); - Token::at(asset).transfer_to_public(context.msg_sender(), context.this_address(), amount, nonce).call(&mut context); - FPC::at(context.this_address()).pay_fee_with_shielded_rebate(amount, asset, secret_hash).enqueue(&mut context); - } -} -``` - -:::warning -Do not import generated interfaces from the same project as the source contract to avoid circular references. -::: - -## Next steps - -After compilation, use the generated artifacts to: - -- Deploy contracts with the `Contract` class from `aztec.js` -- Interact with deployed contracts using type-safe interfaces -- Import contracts in other Aztec.nr projects - +5. Common Errors \ No newline at end of file diff --git a/docs/pages/aztec-nr/debugging.md b/docs/pages/aztec-nr/debugging.md index e59889f..1e584c9 100644 --- a/docs/pages/aztec-nr/debugging.md +++ b/docs/pages/aztec-nr/debugging.md @@ -1,190 +1 @@ # Debugging - -This guide shows you how to debug issues in your Aztec development environment. - -**Prerequisites:** - -- Aztec sandbox running -- Aztec.nr contract or aztec.js application -- Basic understanding of Aztec architecture - -## How to Enable Logging - -### In Aztec.nr Contracts - -```rust -// Import debug logging -use dep::aztec::oracle::debug_log::{ debug_log, debug_log_format, debug_log_field, debug_log_array }; - -// Log simple messages -debug_log("checkpoint reached"); - -// Log field values with context -debug_log_format("slot:{0}, hash:{1}", [storage_slot, note_hash]); - -// Log single field -debug_log_field(my_field); - -// Log arrays -debug_log_array(my_array); -``` - -### In Sandbox - -```bash -# Set log level (options: fatal, error, warn, info, verbose, debug, trace) -LOG_LEVEL=debug aztec start --sandbox - -# Different levels for different services -LOG_LEVEL="verbose;info:sequencer" aztec start --sandbox -``` - -## How to Debug Common Errors - -### Contract Errors - -| Error | Solution | -| -------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| `Aztec dependency not found` | Add to Nargo.toml: `aztec = { git="https://github.com/AztecProtocol/aztec-packages/", tag="#include_aztec_version", directory="noir-projects/aztec-nr/aztec" }` | -| `Public state writes only supported in public functions` | Move state writes to public functions | -| `Unknown contract 0x0` | Call `pxe.addContracts(...)` to register contract | -| `No public key registered for address` | Call `pxe.registerRecipient(...)` or `pxe.registerAccount(...)` | -| `Failed to solve brillig function` | Check function parameters and note validity | - -### Circuit Errors - -| Error Code | Meaning | Fix | -| ----------- | ---------------------------- | -------------------------------------------------- | -| `2002` | Invalid contract address | Ensure contract is deployed and address is correct | -| `2005/2006` | Static call violations | Remove state modifications from static calls | -| `2017` | User intent mismatch | Verify transaction parameters match function call | -| `3001` | Unsupported operation | Check if operation is supported in current context | -| `3005` | Non-empty private call stack | Ensure private functions complete before public | -| `4007/4008` | Chain ID/version mismatch | Verify L1 chain ID and Aztec version | -| `7008` | Membership check failed | Ensure using valid historical state | -| `7009` | Array overflow | Reduce number of operations in transaction | - -### Quick Fixes for Common Issues - -```bash -# Archiver sync issues - force progress with dummy transactions -aztec-wallet send transfer --from test0 --to test0 --amount 0 -aztec-wallet send transfer --from test0 --to test0 --amount 0 - -# L1 to L2 message pending - wait for inclusion -# Messages need 2 blocks to be processed -``` - -## How to Debug WASM Errors - -### Enable Debug WASM - -```javascript -// In vite.config.ts or similar -export default { - define: { - "process.env.BB_WASM_PATH": JSON.stringify("https://debug.wasm.url"), - }, -}; -``` - -### Profile Transactions - -```javascript -import { serializePrivateExecutionSteps } from "@aztec/stdlib"; - -// Profile the transaction -const profileTx = await contract.methods - .myMethod(param1, param2) - .profile({ profileMode: "execution-steps" }); - -// Serialize for debugging -const ivcMessagePack = serializePrivateExecutionSteps(profileTx.executionSteps); - -// Download debug file -const blob = new Blob([ivcMessagePack]); -const url = URL.createObjectURL(blob); -const link = document.createElement("a"); -link.href = url; -link.download = "debug-steps.msgpack"; -link.click(); -``` - -⚠️ **Warning:** Debug files may contain private data. Use only in development. - -## How to Interpret Error Messages - -### Kernel Circuit Errors (2xxx) - -- **Private kernel errors (2xxx)**: Issues with private function execution -- **Public kernel errors (3xxx)**: Issues with public function execution -- **Rollup errors (4xxx)**: Block production issues -- **Generic errors (7xxx)**: Resource limits or state validation - -### Transaction Limits - -Current limits that trigger `7009 - ARRAY_OVERFLOW`: - -- Max new notes per tx: Check `MAX_NOTE_HASHES_PER_TX` -- Max nullifiers per tx: Check `MAX_NULLIFIERS_PER_TX` -- Max function calls: Check call stack size limits -- Max L2→L1 messages: Check message limits - -## How to Debug Sequencer Issues - -### Common Sequencer Errors - -| Error | Cause | Solution | -| ------------------------------------ | --------------------- | ------------------------------------------ | -| `tree root mismatch` | State inconsistency | Restart sandbox or check state transitions | -| `next available leaf index mismatch` | Tree corruption | Verify tree updates are sequential | -| `Public call stack size exceeded` | Too many public calls | Reduce public function calls | -| `Failed to publish block` | L1 submission failed | Check L1 connection and gas | - -## How to Report Issues - -When debugging fails: - -1. Collect error messages and codes -2. Generate transaction profile (if applicable) -3. Note your environment setup -4. Create issue at [aztec-packages](https://github.com/AztecProtocol/aztec-packages/issues/new) - -## Quick Reference - -### Enable Verbose Logging - -```bash -LOG_LEVEL=verbose aztec start --sandbox -``` - -### Common Debug Imports - -```rust -use dep::aztec::oracle::debug_log::{ debug_log, debug_log_format }; -``` - -### Check Contract Registration - -```javascript -await pxe.getContracts(); -await pxe.getRegisteredAccounts(); -``` - -### Decode L1 Errors - -Check hex errors against [Errors.sol](https://github.com/AztecProtocol/aztec-packages/blob/master/l1-contracts/src/core/libraries/Errors.sol) - -## Tips - -- Always check logs before diving into circuit errors -- State-related errors often indicate timing issues -- Array overflow errors mean you hit transaction limits -- Use debug WASM for detailed stack traces -- Profile transactions when errors are unclear - -## Related Resources - -- Circuit Architecture - See concepts section -- Private-Public Execution - See concepts section -- Aztec.nr Dependencies - See reference section diff --git a/docs/pages/aztec-nr/deploying.md b/docs/pages/aztec-nr/deploying.md index 9939800..37b789c 100644 --- a/docs/pages/aztec-nr/deploying.md +++ b/docs/pages/aztec-nr/deploying.md @@ -1,5 +1,7 @@ # Deploying +## Mike's notes + 9. Deploy 1. Contract Classes & Instances 2. Registering a new Contract Class diff --git a/docs/pages/aztec-nr/framework-description/authentication-witness.md b/docs/pages/aztec-nr/framework-description/authentication-witness.md new file mode 100644 index 0000000..df50209 --- /dev/null +++ b/docs/pages/aztec-nr/framework-description/authentication-witness.md @@ -0,0 +1 @@ +# Authentication Witness \ No newline at end of file diff --git a/docs/pages/aztec-nr/language-description/aztec-attributes.md b/docs/pages/aztec-nr/framework-description/aztec-attributes.md similarity index 81% rename from docs/pages/aztec-nr/language-description/aztec-attributes.md rename to docs/pages/aztec-nr/framework-description/aztec-attributes.md index a3e8063..583b5c2 100644 --- a/docs/pages/aztec-nr/language-description/aztec-attributes.md +++ b/docs/pages/aztec-nr/framework-description/aztec-attributes.md @@ -1,5 +1,8 @@ # Aztec Attributes +## Mike's Notes + - Quick reference Table of attributes - aztec, public, private, utility, view, internal, initializer, noinitcheck, storage, note, contract_library_method ←- these are subject to change -- Differences of Aztec attributes vs Function visibility modifiers in Solidity \ No newline at end of file +- Differences of Aztec attributes vs Function visibility modifiers in Solidity +- Combine with macros available \ No newline at end of file diff --git a/docs/pages/aztec-nr/framework-description/calling-other-contracts.md b/docs/pages/aztec-nr/framework-description/calling-other-contracts.md new file mode 100644 index 0000000..ed5fabc --- /dev/null +++ b/docs/pages/aztec-nr/framework-description/calling-other-contracts.md @@ -0,0 +1,27 @@ +# Calling Other Contracts + +## Mike's comments + +4. Calling other Contracts + - Contract Address & Function Selector + - (Noting that function selectors are only part of the protocol for private functions) + - Importing another Contract's Interface + - External call to self + - E.g. recursive token spends + - Limitations of this syntax (ugly) + - Call + - Staticcall + - Delegatecall + - It does not exist. Point the user to contract upgrades, in the "deploy" section. + - Calling Private Getter Functions of other Contracts + - Restrictions, to prevent private state leakage. + - Private -> Public Calls + - Privacy Leakage + - Avoid Passing Addresses + - Setting msg_sender to Option::none + - Stealthifying Addresses + - Custom Stealth Addresses + +## Notes from chat with Mike + +- Also include other call types? diff --git a/docs/pages/aztec-nr/framework-description/capsules.md b/docs/pages/aztec-nr/framework-description/capsules.md new file mode 100644 index 0000000..90fa0cf --- /dev/null +++ b/docs/pages/aztec-nr/framework-description/capsules.md @@ -0,0 +1,5 @@ +# Capsules + +## Mike's super helpful (hmm) notes +14. Storing and Retrieving Local Data in Private - I don’t know what I was trying to say with this + 1. PXE DB (Capsules) \ No newline at end of file diff --git a/docs/pages/aztec-nr/framework-description/contract-scope.md b/docs/pages/aztec-nr/framework-description/contract-scope.md new file mode 100644 index 0000000..68e4437 --- /dev/null +++ b/docs/pages/aztec-nr/framework-description/contract-scope.md @@ -0,0 +1,14 @@ +# Contract Scope + +## Mike's Notes + +- Discuss: + - how to declare a contract. + - Differences vs conventional Noir program. + - Essential imports + - Describe limitations (no inheritance, currently, so big monolithic contract files) + - This will change significantly next year, after Noir 1.0 is audited and launched. + +## Ciara's notes + +Literally like they syntax of how to create a contract in Noir. E.g. in solidity we would explain the `contract` keyword, explain the scope that its sorta like a class and that you have to name it :) \ No newline at end of file diff --git a/docs/pages/aztec-nr/framework-description/contract-structure.md b/docs/pages/aztec-nr/framework-description/contract-structure.md new file mode 100644 index 0000000..bde8e39 --- /dev/null +++ b/docs/pages/aztec-nr/framework-description/contract-structure.md @@ -0,0 +1,12 @@ +# Contract Structure + +## Mike's comments + +1. High-level structure + - Imports + - Contract Scope + - State Variables + - Functions + - Events + - See the dedicated section below + diff --git a/docs/pages/aztec-nr/framework-description/cosnarks.md b/docs/pages/aztec-nr/framework-description/cosnarks.md new file mode 100644 index 0000000..5d34623 --- /dev/null +++ b/docs/pages/aztec-nr/framework-description/cosnarks.md @@ -0,0 +1,11 @@ +# CoSnarks + +## Mike's comments + +- Explain when cosnarks are useful. +- Links to hackmd & example repo. + +## Ciara's notes + +- cosnarks are decentralized PXE's in my mind hehe +- Shared Private State: I want this shared secret we all can accessw but no-one knows \ No newline at end of file diff --git a/docs/pages/aztec-nr/framework-description/cross-chain-interactions.md b/docs/pages/aztec-nr/framework-description/cross-chain-interactions.md new file mode 100644 index 0000000..44738a5 --- /dev/null +++ b/docs/pages/aztec-nr/framework-description/cross-chain-interactions.md @@ -0,0 +1,9 @@ +# Cross-chain Interactions + +## Mike's comments + +12. Interacting with other Chains + - Portal Contracts + - Bridging (also explained in Common Use Cases, below) + - Point to L1-L2 messaging + - Rahul has thought a lot about this. diff --git a/docs/pages/aztec-nr/language-description/error-handling.md b/docs/pages/aztec-nr/framework-description/error-handling.md similarity index 88% rename from docs/pages/aztec-nr/language-description/error-handling.md rename to docs/pages/aztec-nr/framework-description/error-handling.md index 2bbc0a2..a621bf9 100644 --- a/docs/pages/aztec-nr/language-description/error-handling.md +++ b/docs/pages/aztec-nr/framework-description/error-handling.md @@ -1,5 +1,7 @@ # Error Handling +## Mike's notes + 2. Private Functions cannot catch errors. 3. Syntax for catching errors in Public. 4. Common Errors. / Common pitfalls \ No newline at end of file diff --git a/docs/pages/aztec-nr/framework-description/ethereum-aztec-messaging.md b/docs/pages/aztec-nr/framework-description/ethereum-aztec-messaging.md new file mode 100644 index 0000000..744c408 --- /dev/null +++ b/docs/pages/aztec-nr/framework-description/ethereum-aztec-messaging.md @@ -0,0 +1,15 @@ +# L1-L2 Messaging + +## Mike's notes + +11. L1\<\>L2 Messaging + 1. L2->L1 Messages + - Creating an L2->L1 Message + - Consuming an L2->L1 Message + 2. L1->L2 Messages + - Creating an L1->L2 Message + - Consuming an L1->L2 Message + +## Ciara's notes + +- There is LOADS in the current docs but idk if it is up-to-date or not (literally across guides, references and concepts rip) \ No newline at end of file diff --git a/docs/pages/aztec-nr/framework-description/events-and-logs.md b/docs/pages/aztec-nr/framework-description/events-and-logs.md new file mode 100644 index 0000000..eb37ae4 --- /dev/null +++ b/docs/pages/aztec-nr/framework-description/events-and-logs.md @@ -0,0 +1,20 @@ +# Events and Logs + +## Mike's notes + +8. Events + - Event logs vs note logs + - Public Events + - Private Events +9. Logs +5. Tradeoffs +- Cost vs Recoverability vs Interactivity etc. + 1. Types of logs + 4. Public Logs + 5. Private Logs + 5. Contract Class Logs + 2. Where do the logs end up? + 1. Emit to L1 + 2. Offchain Logs + 3. Shadow Logs + 1. (Future) diff --git a/docs/pages/aztec-nr/language-description/functions.md b/docs/pages/aztec-nr/framework-description/functions.md similarity index 99% rename from docs/pages/aztec-nr/language-description/functions.md rename to docs/pages/aztec-nr/framework-description/functions.md index 57d7628..4d5229f 100644 --- a/docs/pages/aztec-nr/language-description/functions.md +++ b/docs/pages/aztec-nr/framework-description/functions.md @@ -1,5 +1,7 @@ # Functions +## Mike's notes + 3. Functions 1. Public, Private, Utility - Concepts diff --git a/docs/pages/aztec-nr/framework-description/global-variables.md b/docs/pages/aztec-nr/framework-description/global-variables.md new file mode 100644 index 0000000..ac0fb59 --- /dev/null +++ b/docs/pages/aztec-nr/framework-description/global-variables.md @@ -0,0 +1,7 @@ +# Global Variables + +## Mike's Notes + +- See all of the things that can be accessed in the Context, and explain them. +- Public Functions +- Private Functions \ No newline at end of file diff --git a/docs/pages/aztec-nr/framework-description/index.md b/docs/pages/aztec-nr/framework-description/index.md new file mode 100644 index 0000000..ec35ff4 --- /dev/null +++ b/docs/pages/aztec-nr/framework-description/index.md @@ -0,0 +1,5 @@ +# Framework Description + +## Ciara's notes + +- An overview of the contents (like a bullet point clickable list) \ No newline at end of file diff --git a/docs/pages/aztec-nr/framework-description/libraries.md b/docs/pages/aztec-nr/framework-description/libraries.md new file mode 100644 index 0000000..87f2817 --- /dev/null +++ b/docs/pages/aztec-nr/framework-description/libraries.md @@ -0,0 +1,18 @@ +# Libraries + +## Mike's notes + +- Common Aztecnr libraries + - UintNote + - See "References->Smart Contract Reference->Importing Aztecnr" in today's docs + - BigNum + - BigCurve + - Link to Awesome Noir + - Cryptographic functions + - Poseidon2 + - Pedersen (more expensive, but homomorphic) + - Some are not available to Public functions. + +## Ciara's notes + +- As per Mike & Nico's feedback, also include the macros??? Or was that in the attributes one i can't remember \ No newline at end of file diff --git a/docs/pages/aztec-nr/language-description/private-messaging.md b/docs/pages/aztec-nr/framework-description/private-messaging.md similarity index 92% rename from docs/pages/aztec-nr/language-description/private-messaging.md rename to docs/pages/aztec-nr/framework-description/private-messaging.md index ceb51d4..4db3a8b 100644 --- a/docs/pages/aztec-nr/language-description/private-messaging.md +++ b/docs/pages/aztec-nr/framework-description/private-messaging.md @@ -1,5 +1,7 @@ # Private Messaging +## Mike's notes + - Reiterate Lifecycle concepts: - Create, Choose Recipient(s), Encrypt, Emit, Discover, Decrypt, Process, Store, Read, Nullify 1. Message Delivery @@ -44,4 +46,8 @@ - See Mike's Hackmd on constrained delivery 4. Advanced - Establishing an ECDH shared secret - - Custom log layouts \ No newline at end of file + - Custom log layouts + +## Ciara's notes + +- This replaces note discovery so we should make that crystal clear \ No newline at end of file diff --git a/docs/pages/aztec-nr/framework-description/project-structure.md b/docs/pages/aztec-nr/framework-description/project-structure.md new file mode 100644 index 0000000..6e67b91 --- /dev/null +++ b/docs/pages/aztec-nr/framework-description/project-structure.md @@ -0,0 +1,8 @@ +# Project Structure + +## Mike's notes + +- Project Structure + - Folders etc + - Toml + - Import Aztec lib \ No newline at end of file diff --git a/docs/pages/aztec-nr/framework-description/protocol-oracles.md b/docs/pages/aztec-nr/framework-description/protocol-oracles.md new file mode 100644 index 0000000..28ab645 --- /dev/null +++ b/docs/pages/aztec-nr/framework-description/protocol-oracles.md @@ -0,0 +1,11 @@ +# Protocol Oracles + +## Mike's notes + +- List them? +- Why would you use them +- How would you use them in a smart contract + +## Ciara's notes + +- How to use them and not get f*cked \ No newline at end of file diff --git a/docs/pages/aztec-nr/framework-description/reading-historic-state.md b/docs/pages/aztec-nr/framework-description/reading-historic-state.md new file mode 100644 index 0000000..b7332fd --- /dev/null +++ b/docs/pages/aztec-nr/framework-description/reading-historic-state.md @@ -0,0 +1 @@ +# Reading Historic State \ No newline at end of file diff --git a/docs/pages/aztec-nr/language-description/state-variables.md b/docs/pages/aztec-nr/framework-description/state-variables.md similarity index 89% rename from docs/pages/aztec-nr/language-description/state-variables.md rename to docs/pages/aztec-nr/framework-description/state-variables.md index 8d19bb0..330ba98 100644 --- a/docs/pages/aztec-nr/language-description/state-variables.md +++ b/docs/pages/aztec-nr/framework-description/state-variables.md @@ -1,5 +1,7 @@ # State Variables +## Mike's notes (this one is massive and that's ok) + 1. Storage - Declaring Contract Storage - #[storage] @@ -42,7 +44,7 @@ - zcash-style - plume-style - project-tachyon-style - 5. How to share the private data with people + 5. How to share the private data with people (maybe link to private messaging doc) - Explain 6. PrivateMutable - Initialisation dangers @@ -73,4 +75,8 @@ 3. Authorizing State Changes 1. Authwits 2. Auth keypair vs Nullifier keypair - 3. zcash vs plume nullifiers \ No newline at end of file + 3. zcash vs plume nullifiers + +## Ciara's notes + +Also needs section on custom notes &&&&& Partial notes pretty please (I can write theory on partial notes cos I've understood the source code - NOTES ARE LIKE ONIONS) \ No newline at end of file diff --git a/docs/pages/aztec-nr/framework-description/upgradeable-contracts.md b/docs/pages/aztec-nr/framework-description/upgradeable-contracts.md new file mode 100644 index 0000000..26aeebd --- /dev/null +++ b/docs/pages/aztec-nr/framework-description/upgradeable-contracts.md @@ -0,0 +1,13 @@ +# Upgrabeable Contracts + +## Mike's notes + +1. Immutability vs Upgradeability - tradeoffs +2. Risks of Upgrading + - Storage slot contamination + - Bricking of state / notes + - Upgades are not immediate + - They're necessarily delayed. + - Don't rely on them to beat an attacker in a race. +3. Upgrade + - Give details of the `upgrade` function in the ContractInstancePublisher \ No newline at end of file diff --git a/docs/pages/aztec-nr/getting-started.md b/docs/pages/aztec-nr/getting-started.md deleted file mode 100644 index e61e0c4..0000000 --- a/docs/pages/aztec-nr/getting-started.md +++ /dev/null @@ -1,21 +0,0 @@ -# Getting Started - -## Noir VSCode Extension - - -Install the [Noir Language Support extension](https://marketplace.visualstudio.com/items?itemName=noir-lang.vscode-noir) to get syntax highlighting, syntax error detection and go-to definitions for your Aztec contracts. - -Once the extension is installed, check your nargo binary by hovering over Nargo in the status bar on the bottom right of the application window. Click to choose the path to aztec-nargo (or regular nargo, if you have that installed). - -You can print the path of your `aztec-nargo` executable by running: - -```bash -which aztec-nargo -``` - -To specify a custom nargo executable, go to the VSCode settings and search for "noir", or click extension settings on the `noir-lang` LSP plugin. Update the `Noir: Nargo Path` field to point to your desired `aztec-nargo` executable. - - -## Creating an Aztec.nr Project - -## Building \ No newline at end of file diff --git a/docs/pages/aztec-nr/installation.md b/docs/pages/aztec-nr/installation.md index 265bfc7..2cbac88 100644 --- a/docs/pages/aztec-nr/installation.md +++ b/docs/pages/aztec-nr/installation.md @@ -1,5 +1,9 @@ # Installation +## Ciara's Notes + +I've added some here but needs finishing probs + Before learning how to create Aztec smart contracts, we must first install some tools we will use in the development process: - **Aztec Tools**: - `aztec`: to manage infrastructure subsystems (sandbox, sequencer, prover, pxe, etc) and provides utility commands to interact with the network diff --git a/docs/pages/aztec-nr/introduction-to-private-smart-contracts/account-contract.md b/docs/pages/aztec-nr/introduction-to-private-smart-contracts/account-contract.md new file mode 100644 index 0000000..90f8a5d --- /dev/null +++ b/docs/pages/aztec-nr/introduction-to-private-smart-contracts/account-contract.md @@ -0,0 +1,2 @@ +# Account Contract + diff --git a/docs/pages/aztec-nr/introduction-to-private-smart-contracts/index.md b/docs/pages/aztec-nr/introduction-to-private-smart-contracts/index.md index 2f26fc9..e69de29 100644 --- a/docs/pages/aztec-nr/introduction-to-private-smart-contracts/index.md +++ b/docs/pages/aztec-nr/introduction-to-private-smart-contracts/index.md @@ -1,23 +0,0 @@ -# Introduction to Private Smart Contracts - -Learn how to write private smart contracts on Aztec using Aztec.nr. - -## What are Private Smart Contracts? - -Private smart contracts on Aztec allow you to build applications where transaction details, state, and logic can remain private while still being verifiable on a public blockchain. - -## Getting Started - -- A Simple Aztec Smart Contract - Start with a basic example -- Example Aztec Smart Contracts - Explore more complex patterns - -## Key Concepts - -Private smart contracts on Aztec differ from traditional Ethereum contracts in several ways: - -- **Private State** - Contract state can be kept private using notes -- **Private Functions** - Function execution can be private, with only proofs posted on-chain -- **Public Functions** - Standard public functions are also supported -- **Hybrid State** - Contracts can use both private and public state together - -Navigate to the sections above to learn how to build your first Aztec smart contract. diff --git a/docs/pages/aztec-nr/introduction-to-private-smart-contracts/private-token-contract.md b/docs/pages/aztec-nr/introduction-to-private-smart-contracts/private-token-contract.md new file mode 100644 index 0000000..92cce42 --- /dev/null +++ b/docs/pages/aztec-nr/introduction-to-private-smart-contracts/private-token-contract.md @@ -0,0 +1,2 @@ +# Private Token Contract + diff --git a/docs/pages/aztec-nr/language-description/authentication-witness.md b/docs/pages/aztec-nr/language-description/authentication-witness.md deleted file mode 100644 index 1fa3692..0000000 --- a/docs/pages/aztec-nr/language-description/authentication-witness.md +++ /dev/null @@ -1,92 +0,0 @@ -# Authentication Witness - - -Authentication witnesses (authwit) allow other contracts to execute actions on behalf of your account. This guide shows you how to implement and use authwits in your Aztec smart contracts. - -## Prerequisites - -- An Aztec contract project set up with `aztec-nr` dependency -- Understanding of private and public functions in Aztec -- Access to the `authwit` library in your contract - -For conceptual background, see Authentication Witnesses in the concepts section. - -## Set up the authwit library - -Add the `authwit` library to your `Nargo.toml` file: - -```toml -[dependencies] -aztec = { git="https://github.com/AztecProtocol/aztec-packages/", tag="#include_aztec_version", directory="noir-projects/smart-contracts/aztec" } -``` - -Import the authwit library in your contract: - -```rust -use aztec::authwit::auth::compute_authwit_nullifier; -``` - -## Implement authwit in private functions - -### Validate authentication in a private function - -Check if the current call is authenticated using the `authorize_once` macro: - -```rust -#[authorize_once("from", "authwit_nonce")] -#[private] -fn execute_private_action( - from: AztecAddress, - to: AztecAddress, - value: u128, - authwit_nonce: Field, -) { - storage.values.at(from).sub(from, value).emit(encode_and_encrypt_note(&mut context, from)); - storage.values.at(to).add(to, value).emit(encode_and_encrypt_note(&mut context, to)); -} -``` - -This allows anyone with a valid authwit (created by `from`) to execute an action on its behalf. - -## Set approval state from contracts - -Enable contracts to approve actions on their behalf by updating the public auth registry: - -1. Compute the message hash using `compute_authwit_message_hash_from_call` -2. Set the authorization using `set_authorized` - -This pattern is commonly used in bridge contracts (like the [uniswap example contract](https://github.com/AztecProtocol/aztec-packages/tree/next/noir-projects/noir-contracts/contracts/app/uniswap_contract)) where one contract needs to authorize another to perform actions on its behalf: - -```rust -#[public] -#[internal] -fn _approve_and_execute_action( - target_contract: AztecAddress, - bridge_contract: AztecAddress, - value: u128, -) { - // Since we will authorize and instantly execute the action, all in public, we can use the same nonce - // every interaction. In practice, the authwit should be squashed, so this is also cheap! - let authwit_nonce = 0xdeadbeef; - - let selector = FunctionSelector::from_signature("execute_action((Field),u128,Field)"); - let message_hash = compute_authwit_message_hash_from_call( - bridge_contract, - target_contract, - context.chain_id(), - context.version(), - selector, - [context.this_address().to_field(), value as Field, authwit_nonce], - ); - - // We need to make a call to update it. - set_authorized(&mut context, message_hash, true); - - let this_address = storage.my_address.read(); - // Execute the action! - OtherContract::at(bridge_contract) - .execute_external_action(this_address, value, this_address, authwit_nonce) - .call(&mut context) -} -``` - diff --git a/docs/pages/aztec-nr/language-description/calling-other-contracts.md b/docs/pages/aztec-nr/language-description/calling-other-contracts.md deleted file mode 100644 index 5c62e8d..0000000 --- a/docs/pages/aztec-nr/language-description/calling-other-contracts.md +++ /dev/null @@ -1,91 +0,0 @@ -# Calling Other Contracts - -4. Calling other Contracts - - Contract Address & Function Selector - - (Noting that function selectors are only part of the protocol for private functions) - - Importing another Contract's Interface - - External call to self - - E.g. recursive token spends - - Limitations of this syntax (ugly) - - Call - - Staticcall - - Delegatecall - - It does not exist. Point the user to contract upgrades, in the "deploy" section. - - Calling Private Getter Functions of other Contracts - - Restrictions, to prevent private state leakage. - - Private -> Public Calls - - Privacy Leakage - - Avoid Passing Addresses - - Setting msg_sender to Option::none - - Stealthifying Addresses - - Custom Stealth Addresses - - -This guide shows you how to call functions in other contracts from your Aztec smart contracts, enabling contract composability and interaction. - -## Prerequisites - -- An Aztec contract project with dependencies properly configured -- Access to the target contract's source code or ABI -- Understanding of Aztec contract compilation and deployment - -## Add the target contract as a dependency - -Add the contract you want to call to your `Nargo.toml` dependencies: - -```toml -other_contract = { git="https://github.com/your-repo/", tag="v1.0.0", directory="path/to/contract" } -``` - -## Import the contract interface - -Import the contract at the top of your contract file: - -```rust -use other_contract::OtherContract; -``` - -## Call contract functions - -Use this pattern to call functions in other contracts: - -1. Specify the contract address: `Contract::at(contract_address)` -2. Call the function: `.function_name(param1, param2)` -3. Execute the call: `.call(&mut context)` - -### Make private function calls - -Call private functions directly using `.call()`: - -```rust -OtherContract::at(contract_address).private_function(param1, param2).call(&mut context); -``` - -### Make public-to-public calls - -Call public functions from other public functions using `.call()`: - -```rust -let result = OtherContract::at(contract_address) - .public_function(param1, param2, param3) - .call(&mut context); -``` - -### Make private-to-public calls - -Enqueue public functions to be executed after private execution completes: - -```rust -OtherContract::at(contract_address) - .public_function(param1, param2) - .enqueue(&mut context); -``` - -:::info -Public functions always execute after private execution completes. Learn more in the concepts overview. -::: - -### Use other call types - -Explore additional call types for specialized use cases in the call types reference. - diff --git a/docs/pages/aztec-nr/language-description/contract-structure.md b/docs/pages/aztec-nr/language-description/contract-structure.md deleted file mode 100644 index f0af307..0000000 --- a/docs/pages/aztec-nr/language-description/contract-structure.md +++ /dev/null @@ -1,44 +0,0 @@ -# Contract Structure - -1. High-level structure - - Imports - - Contract Scope - - State Variables - - Functions - - Events - - See the dedicated section below - -A contract is a collection of persistent state variables and 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 the 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/docs/getting_started/project_breakdown/#mainnr) function within a Noir `contract` scope. More than one function can be an entrypoint. -::: - -## Directory structure - -Here's a common layout for a basic Aztec.nr Contract project: - -```title="layout of an aztec contract project" -─── my_aztec_contract_project - ├── src - │ ├── main.nr <-- your contract - └── Nargo.toml <-- package and dependency management -``` - -- See the vanilla Noir docs for [more info on packages](https://noir-lang.org/docs/noir/modules_packages_crates/crates_and_packages). \ No newline at end of file diff --git a/docs/pages/aztec-nr/language-description/cosnarks.md b/docs/pages/aztec-nr/language-description/cosnarks.md deleted file mode 100644 index 7e1786c..0000000 --- a/docs/pages/aztec-nr/language-description/cosnarks.md +++ /dev/null @@ -1,4 +0,0 @@ -# CoSnarks - -- Explain when cosnarks are useful. -- Links to hackmd & example repo. \ No newline at end of file diff --git a/docs/pages/aztec-nr/language-description/cross-chain-interactions.md b/docs/pages/aztec-nr/language-description/cross-chain-interactions.md deleted file mode 100644 index 4dec7db..0000000 --- a/docs/pages/aztec-nr/language-description/cross-chain-interactions.md +++ /dev/null @@ -1,199 +0,0 @@ -# Cross-chain Interactions - -12. Interacting with other Chains - - Portal Contracts - - Bridging (also explained in Common Use Cases, below) - - Point to L1-L2 messaging - - Rahul has thought a lot about this. - - This guide shows you how to implement cross-chain communication between Ethereum (L1) and Aztec (L2) contracts using portal contracts. - -## Prerequisites - -- An Aztec contract project set up with `aztec-nr` dependency -- Understanding of Aztec L1/L2 architecture -- Access to Ethereum development environment for L1 contracts -- Deployed portal contract on L1 (see token bridge tutorial) - -## Send messages from L1 to L2 - -### Send a message from your L1 portal contract - -Use the `Inbox` contract to send messages from L1 to L2. Call `sendL2Message` with these parameters: - -| Parameter | Type | Description | -|-------------|----------|-------------| -| `actor` | `L2Actor` | Your L2 contract address and rollup version | -| `contentHash` | `bytes32` | Hash of your message content (use `Hash.sha256ToField`) | -| `secretHash` | `bytes32` | Hash of a secret for message consumption | - -In your Solidity contract: - -```solidity -import {IInbox} from "@aztec/core/interfaces/messagebridge/IInbox.sol"; -import {DataStructures} from "@aztec/core/libraries/DataStructures.sol"; -import {Hash} from "@aztec/core/libraries/crypto/Hash.sol"; - -// ... initialize inbox, get rollupVersion from rollup contract ... - -DataStructures.L2Actor memory actor = DataStructures.L2Actor(l2ContractAddress, rollupVersion); - -// Hash your message content with a unique function signature -bytes32 contentHash = Hash.sha256ToField( - abi.encodeWithSignature("your_action_name(uint256,address)", param1, param2) -); - -// Send the message -(bytes32 key, uint256 index) = inbox.sendL2Message(actor, contentHash, secretHash); -``` - -### Consume the message in your L2 contract - -To consume a message coming from L1, use the `consume_l1_to_l2_message` function within the context: - -- The `content_hash` must match the hash that was sent from L1 -- The `secret` is the pre-image of the `secretHash` sent from L1 -- The `sender` is the L1 portal contract address -- The `message_leaf_index` helps the RPC find the correct message -- If the content or secret doesn't match, the transaction will revert -- "Consuming" a message pushes a nullifier to prevent double-spending - -```rust -#[public] -fn consume_message_from_l1( - secret: Field, - message_leaf_index: Field, - // your function parameters -) { - // Recreate the same content hash as on L1 - let content_hash = /* compute your content hash */; - - // Consume the L1 message - context.consume_l1_to_l2_message( - content_hash, - secret, - portal_address, // Your L1 portal contract address - message_leaf_index - ); - - // Execute your contract logic here -} -``` - -## Send messages from L2 to L1 - -### Send a message from your L2 contract - -Use `message_portal` in your `context` to send messages from L2 to L1: - -```rust -#[public] -fn send_message_to_l1( - // your function parameters -) { - // Note: This can be called from both public and private functions - // Create your message content (must fit in a single Field) - let content = /* compute your content hash */; - - // Send message to L1 portal - context.message_portal(portal_address, content); -} -``` - -### Consume the message in your L1 portal - -Use the `Outbox` to consume L2 messages on L1: - -```solidity -import {IOutbox} from "@aztec/core/interfaces/messagebridge/IOutbox.sol"; -import {DataStructures} from "@aztec/core/libraries/DataStructures.sol"; -import {Hash} from "@aztec/core/libraries/crypto/Hash.sol"; - -function consumeMessageFromL2( - // your parameters - uint256 _l2BlockNumber, - uint256 _leafIndex, - bytes32[] calldata _path -) external { - // Recreate the message structure - DataStructures.L2ToL1Msg memory message = DataStructures.L2ToL1Msg({ - sender: DataStructures.L2Actor(l2ContractAddress, rollupVersion), - recipient: DataStructures.L1Actor(address(this), block.chainid), - content: Hash.sha256ToField( - abi.encodeWithSignature( - "your_action_name(address,uint256,address)", - param1, param2, param3 - ) - ) - }); - - // Consume the message - outbox.consume(message, _l2BlockNumber, _leafIndex, _path); - - // Execute your L1 logic here -} -``` - -:::info - -The `_leafIndex` and `_path` parameters are merkle tree proofs needed to verify the message exists. Get them using JavaScript: - -```ts -import { computeL2ToL1MessageHash } from '@aztec/stdlib/hash'; - -// Compute the message hash -const l2ToL1Message = computeL2ToL1MessageHash({ - l2Sender: l2ContractAddress, - l1Recipient: EthAddress.fromString(portalAddress), - content: messageContent, - rollupVersion: new Fr(version), - chainId: new Fr(chainId), -}); - -// Get the merkle proof -const [leafIndex, siblingPath] = await pxe.getL2ToL1MembershipWitness( - await pxe.getBlockNumber(), - l2ToL1Message -); -``` - -::: - -## Best practices - -### Structure messages properly - -Use function signatures to prevent message misinterpretation: - -```solidity -// ❌ Ambiguous format -bytes memory message = abi.encode(_value, _contract, _recipient); - -// ✅ Clear function signature -bytes memory message = abi.encodeWithSignature( - "execute_action(uint256,address,address)", - _value, _contract, _recipient -); -``` - -### Use designated callers - -Control message execution order with designated callers: - -```solidity -bytes memory message = abi.encodeWithSignature( - "execute_action(uint256,address,address)", - _value, _recipient, - _withCaller ? msg.sender : address(0) -); -``` - -## Example implementations - -- [Token Portal (L1)](https://github.com/AztecProtocol/aztec-packages/blob/#include_aztec_version/l1-contracts/test/portals/TokenPortal.sol) -- [Token Bridge (L2)](https://github.com/AztecProtocol/aztec-packages/blob/#include_aztec_version/noir-projects/noir-contracts/contracts/app/token_bridge_contract/src/main.nr) - -## Next steps - -Follow the cross-chain messaging tutorial for a complete implementation example. - diff --git a/docs/pages/aztec-nr/language-description/declaring-a-contract.md b/docs/pages/aztec-nr/language-description/declaring-a-contract.md deleted file mode 100644 index a496a6d..0000000 --- a/docs/pages/aztec-nr/language-description/declaring-a-contract.md +++ /dev/null @@ -1 +0,0 @@ -# Declaring a Contract \ No newline at end of file diff --git a/docs/pages/aztec-nr/language-description/events-and-logs.md b/docs/pages/aztec-nr/language-description/events-and-logs.md deleted file mode 100644 index facd8df..0000000 --- a/docs/pages/aztec-nr/language-description/events-and-logs.md +++ /dev/null @@ -1,106 +0,0 @@ -# Events and Logs - -8. Events - - Event logs vs note logs - - Public Events - - Private Events -9. Logs -5. Tradeoffs -- Cost vs Recoverability vs Interactivity etc. - 1. Types of logs - 4. Public Logs - 5. Private Logs - 5. Contract Class Logs - 2. Where do the logs end up? - 1. Emit to L1 - 2. Offchain Logs - 3. Shadow Logs - 1. (Future) - -This guide shows you how to emit events and logs from your Aztec contracts to communicate with offchain applications. - -## Prerequisites - -- An Aztec contract project set up with `aztec-nr` dependency -- Understanding of private vs public functions in Aztec -- Basic knowledge of event handling in blockchain applications - -## Emit private events - -### Emit encrypted events - -Use encrypted events to send private data to specific recipients: - -```rust -// Import from aztec.nr -use aztec::event::event_emission::emit_event_in_private; - -emit_event_in_private( - MyEvent { param1, param2, param3 }, - &mut context, - recipient, - MessageDelivery.UNCONSTRAINED_ONCHAIN, -); -``` - -:::note -Developer can choose whether to emit encrypted events or not. Emitting the events means that they will be posted to Ethereum, in blobs, and will inherit the availability guarantees of Ethereum. Developers may choose not to emit events and to share information with recipients offchain, or through alternative mechanisms that are to be developed (e.g. alternative, cheaper data availability solutions). -::: - -The `MessageDelivery` enum provides three modes: - -- `MessageDelivery.CONSTRAINED_ONCHAIN` (value: 1): Constrained encryption, guarantees correct recipient -- `MessageDelivery.UNCONSTRAINED_ONCHAIN` (value: 2): Faster but trusts sender, may lose events if tagged incorrectly -- `MessageDelivery.UNCONSTRAINED_OFFCHAIN` (value: 3): Lowest cost, requires custom offchain infrastructure - -### Event processing - -Events are automatically discovered and decrypted by the PXE when contract functions are invoked. - -## Emit public events - -Emit structured public events using the `emit` function: - -```rust -// Import from aztec.nr -use aztec::event::event_emission::emit_event_in_public; - -emit_event_in_public( - MyPublicEvent { field1: values[0], field2: values[1] }, - &mut context, -); -``` - -## Emit public logs - -### Emit unstructured data - -Emit unstructured public logs using `emit_public_log`: - -```rust -context.emit_public_log(my_value); -context.emit_public_log([1, 2, 3]); -context.emit_public_log("My message"); -``` - -### Query public events - -Query public events from offchain applications: - -```typescript -const fromBlock = await pxe.getBlockNumber(); -const logFilter = { - fromBlock, - toBlock: fromBlock + 1, -}; -const publicLogs = (await pxe.getPublicLogs(logFilter)).logs; -``` - -## Consider costs - -Event data is published to Ethereum as blobs, which incurs costs. Consider: - -- Encrypted events are optional - use alternative communication methods if needed -- Future alternatives for data availability may become available -- Balance event utility with cost implications - diff --git a/docs/pages/aztec-nr/language-description/global-variables.md b/docs/pages/aztec-nr/language-description/global-variables.md deleted file mode 100644 index f46dfe2..0000000 --- a/docs/pages/aztec-nr/language-description/global-variables.md +++ /dev/null @@ -1,65 +0,0 @@ -# Global Variables - -- See all of the things that can be accessed in the Context, and explain them. -- Public Functions -- Private Functions - -# Global Variables - -For developers coming from solidity, this concept will be similar to how the global `block` variable exposes a series of block values. The idea is the same in Aztec. Developers can access a namespace of values made available in each function. - -`Aztec` has two execution environments, Private and Public. Each execution environment contains a different global variables object. - -## Private Global Variables - -#include_code tx-context /noir-projects/noir-protocol-circuits/crates/types/src/abis/transaction/tx_context.nr rust - -The private global variables are equal to the transaction context and contain: - -### Chain Id - -The chain id differs depending on which Aztec instance you are on ( NOT the Ethereum hardfork that the rollup is settling to ). On original deployment of the network, this value will be 1. - -```rust -context.chain_id(); -``` - -### Version - -The version number indicates which Aztec hardfork you are on. The Genesis block of the network will have the version number 1. - -```rust -context.version(); -``` - -### Gas Settings - -The gas limits set by the user for the transaction, the max fee per gas, and the inclusion fee. - -## Public Global Variables - -#include_code global-variables /noir-projects/noir-protocol-circuits/crates/types/src/abis/global_variables.nr rust - -The public global variables contain the values present in the `private global variables` described above, with the addition of: - -### Timestamp - -The timestamp is the unix timestamp in which the block has been executed. The value is provided by the block's proposer (therefore can have variance). This value will always increase. - -```rust -context.timestamp(); -``` - -### Block Number - -The block number is a sequential identifier that labels each individual block of the network. This value will be the block number of the block the accessing transaction is included in. -The block number of the genesis block will be 1, with the number increasing by 1 for every block after. - -```rust -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. -::: \ No newline at end of file diff --git a/docs/pages/aztec-nr/language-description/index.md b/docs/pages/aztec-nr/language-description/index.md deleted file mode 100644 index facd721..0000000 --- a/docs/pages/aztec-nr/language-description/index.md +++ /dev/null @@ -1,41 +0,0 @@ -# Language Description - -Comprehensive guide to the Aztec.nr language and its features. - -## Overview - -Aztec.nr is a domain-specific language for writing smart contracts on Aztec. It's based on Noir, a language for writing zero-knowledge circuits, with additional features specific to Aztec smart contracts. - -## Topics - -This section covers all aspects of the Aztec.nr language: - -### Structure -- **Project Structure** - How to organize your Aztec.nr projects -- **Contract Structure** - Anatomy of an Aztec smart contract -- **Declaring a Contract** - Contract declaration syntax - -### Functions and Execution -- **Functions** - Writing public and private functions -- **Calling Other Contracts** - Cross-contract calls -- **Aztec Attributes** - Special attributes for Aztec contracts - -### State and Data -- **Global Variables** - Available global context -- **State Variables** - Managing contract state -- **Events and Logs** - Emitting events from contracts - -### Communication -- **Private Messaging** - Encrypted messaging between accounts -- **L1↔L2 Messaging** - Cross-layer communication -- **Cross-chain Interactions** - Interacting with other chains - -### Advanced Topics -- **Macros** - Code generation and macros -- **Protocol Oracles** - Accessing protocol-level data -- **Libraries** - Using and creating libraries -- **Upgradeable Contracts** - Contract upgrade patterns -- **Error Handling** - Managing errors in contracts -- **CoSnarks** - Collaborative SNARKs - -Navigate through the sidebar to explore each topic in detail. diff --git a/docs/pages/aztec-nr/language-description/l1-l2-messaging.md b/docs/pages/aztec-nr/language-description/l1-l2-messaging.md deleted file mode 100644 index 6bdcb61..0000000 --- a/docs/pages/aztec-nr/language-description/l1-l2-messaging.md +++ /dev/null @@ -1 +0,0 @@ -# L1-L2 Messaging diff --git a/docs/pages/aztec-nr/language-description/libraries.md b/docs/pages/aztec-nr/language-description/libraries.md deleted file mode 100644 index 5b0d9c6..0000000 --- a/docs/pages/aztec-nr/language-description/libraries.md +++ /dev/null @@ -1,52 +0,0 @@ -# Libraries - -- Common Aztecnr libraries - - UintNote - - See "References->Smart Contract Reference->Importing Aztecnr" in today's docs - - BigNum - - BigCurve - - Link to Awesome Noir - - Cryptographic functions - - Poseidon2 - - Pedersen (more expensive, but homomorphic) - - Some are not available to Public functions. - -On this page you will find information about Aztec.nr libraries and up-to-date paths for use in your `Nargo.toml`. - -## Aztec - -```toml -aztec = { git="https://github.com/AztecProtocol/aztec-packages/", tag="#include_aztec_version", directory="noir-projects/aztec-nr/aztec" } -``` - -This is the core Aztec library that is required for every Aztec.nr smart contract. - -## Address note - -```toml -address_note = { git="https://github.com/AztecProtocol/aztec-packages/", tag="#include_aztec_version", directory="noir-projects/aztec-nr/address-note" } -``` - -This is a library for utilizing notes that hold addresses. Find it on [GitHub](https://github.com/AztecProtocol/aztec-packages/tree/master/noir-projects/aztec-nr/address-note/src). - -## Easy private state - -```toml -easy_private_state = { git="https://github.com/AztecProtocol/aztec-packages/", tag="#include_aztec_version", directory="noir-projects/aztec-nr/easy-private-state" } -``` - -This is an abstraction library for using private variables like [`EasyPrivateUint` (GitHub link)](https://github.com/AztecProtocol/aztec-packages/blob/6c20b45993ee9cbd319ab8351e2722e0c912f427/noir-projects/aztec-nr/easy-private-state/src/easy_private_state.nr#L17). - -## Protocol Types - -```toml -protocol_types = { git="https://github.com/AztecProtocol/aztec-packages/", tag="#include_aztec_version", directory="noir-projects/noir-protocol-circuits/crates/types"} -``` - -This library contains types that are used in the Aztec protocol. Find it on [GitHub](https://github.com/AztecProtocol/aztec-packages/tree/master/noir-projects/noir-protocol-circuits/crates/types/src). - -## Value note - -```toml -value_note = { git="https://github.com/AztecProtocol/aztec-packages/", tag="#include_aztec_version", directory="noir-projects/aztec-nr/value-note" } -``` \ No newline at end of file diff --git a/docs/pages/aztec-nr/language-description/macros.md b/docs/pages/aztec-nr/language-description/macros.md deleted file mode 100644 index 9e157eb..0000000 --- a/docs/pages/aztec-nr/language-description/macros.md +++ /dev/null @@ -1,28 +0,0 @@ -# Macros - -21. - - AztecNr Macros - - nargo expand - - What they do - - Unravelling them - - Custom Macros - - Tooling and Debugging. - - See Noir Docs. - - -## All Aztec macros - -In addition to the function macros in Noir, Aztec also has its own macros for specific functions. An Aztec contract function can be annotated with more than 1 macro. -It is also worth mentioning Noir's `unconstrained` function type [here (Noir docs page)](https://noir-lang.org/docs/noir/concepts/unconstrained/). - -- `#[aztec]` - Defines a contract, placed above `contract ContractName{}` -- `#[public]`, `#[private]` or `#[utility]` - Whether the function is to be executed from a public, private or utility context (see Further Reading) -- `#[initializer]` - If one or more functions are marked as an initializer, then one of them must be called before any non-initializer functions -- `#[noinitcheck]` - The function is able to be called before an initializer (if one exists) -- `#[view]` - Makes calls to the function static -- `#[internal]` - Function can only be called from within the contract -- `#[note]` - Creates a custom note -- `#[storage]` - Defines contract storage - -## Further reading -How do Aztec macros work? - See concepts section diff --git a/docs/pages/aztec-nr/language-description/project-structure.md b/docs/pages/aztec-nr/language-description/project-structure.md deleted file mode 100644 index 622ce46..0000000 --- a/docs/pages/aztec-nr/language-description/project-structure.md +++ /dev/null @@ -1 +0,0 @@ -# Project Structure \ No newline at end of file diff --git a/docs/pages/aztec-nr/language-description/protocol-oracles.md b/docs/pages/aztec-nr/language-description/protocol-oracles.md deleted file mode 100644 index 5362945..0000000 --- a/docs/pages/aztec-nr/language-description/protocol-oracles.md +++ /dev/null @@ -1,4 +0,0 @@ -# Protocol Oracles - -- List them? -- Maybe that can just be part of the protocol spec? \ No newline at end of file diff --git a/docs/pages/aztec-nr/language-description/upgradeable-contracts.md b/docs/pages/aztec-nr/language-description/upgradeable-contracts.md deleted file mode 100644 index 7c8463c..0000000 --- a/docs/pages/aztec-nr/language-description/upgradeable-contracts.md +++ /dev/null @@ -1,191 +0,0 @@ -# Upgrabeable Contracts - -1. Immutability vs Upgradeability - tradeoffs -2. Risks of Upgrading - - Storage slot contamination - - Bricking of state / notes - - Upgades are not immediate - - They're necessarily delayed. - - Don't rely on them to beat an attacker in a race. -3. Upgrade - - Give details of the `upgrade` function in the ContractInstancePublisher - -For familiarity we've used terminology like "deploying a contract instance of a contract class". When considering how it works with contract upgrades it helps to be more specific. - -Each contract instance refers to a class id for its code. Upgrading a contract's implementation is achieved by updating its current class id to a new class id, whilst retaining the "original class id" for reasons explained below. - -## Original class id - -A contract keeps track of the original contract class that it was instantiated with, which is the "original" class id. It is this original class that is used when calculating and verifying the contract's [address](contract_creation#instance-address). -This variable remains unchanged even if a contract is upgraded. - -## Current class id - -When a contract is first deployed, its current class ID is set equal to its original class ID. The current class ID determines which code implementation the contract actually executes. - -During an upgrade: - -- The original class ID remains unchanged -- The current class ID is updated to refer to the new implementation -- All contract state/data is preserved - -## How to upgrade - -Contract upgrades in Aztec have to be initiated by the contract that wishes to be upgraded calling the `ContractInstanceRegistry`: - -```rust -use dep::aztec::protocol_types::contract_class_id::ContractClassId; -use contract_instance_registry::ContractInstanceRegistry; - -#[private] -fn update_to(new_class_id: ContractClassId) { - ContractInstanceRegistry::at(CONTRACT_INSTANCE_REGISTRY_CONTRACT_ADDRESS) - .update(new_class_id) - .enqueue(&mut context); -} -``` - -The `update` function in the registry is a public function, so you can enqueue it from a private function like the example or call it from a public function directly. - -:::note -Recall that `#[private]` means calling this function preserves privacy, and it still CAN be called externally by anyone. -So the `update_to` function above allows anyone to update the contract that implements it. A more complete implementation should have a proper authorization systems to secure contracts from malicious upgrades. -::: - -Contract upgrades are implemented using a DelayedPublicMutable storage variable in the `ContractInstanceRegistry`, since the upgrade applies to both public and private functions. -This means that they have a delay before entering into effect. The default delay is `86400` seconds (one day) but can be configured by the contract: - -```rust -use dep::aztec::protocol_types::contract_class_id::ContractClassId; -use contract_instance_registry::ContractInstanceRegistry; - -#[private] -fn set_update_delay(new_delay: u64) { - ContractInstanceRegistry::at(CONTRACT_INSTANCE_REGISTRY_CONTRACT_ADDRESS) - .set_update_delay(new_delay) - .enqueue(&mut context); -} -``` - -Where `new_delay` is denominated in seconds. However, take into account that changing the update delay also has as its delay that is the previous delay. So the first delay change will take `86400` seconds to take into effect. - -:::info -The update delay cannot be set lower than `600` seconds -::: - -When sending a transaction, the expiration timestamp of your tx will be the timestamp of the current block number you're simulating with + the minimum of the update delays that you're interacting with. -If your tx interacts with a contract that can be upgraded in 1000 seconds and another one that can be upgraded in 10000 seconds, the expiration timestamp (include_by_timestamp property on the tx) will be current block timestamp + 1000. -Note that this can be even lower if there is an upgrade pending in one of the contracts you're interacting with. -If the contract you interacted with will upgrade in 100 seconds, the expiration timestamp of your tx will be current block timestamp + 99 seconds. -Other DelayedPublicMutable storage variables read in your tx might reduce this expiration timestamp further. - -:::note -Only deployed contract instances can upgrade or change its upgrade delay currently. This restriction might be lifted in the future. -::: - -### Upgrade Process - -1. **Register New Implementation** - - - First, register the new contract class if it contains public functions - - The new implementation must maintain state variable compatibility with the original contract - -2. **Perform Upgrade** - - - Call the update function with the new contract class ID - - The contract's original class ID remains unchanged - - The current class ID is updated to the new implementation - - All contract state and data are preserved - -3. **Verify Upgrade** - - After upgrade, the contract will execute functions from the new implementation - - The contract's address remains the same since it's based on the original class ID - - Existing state variables and their values are preserved - -### How to interact with an upgraded contract - -The PXE stores the contract instances and classes in a local database. When a contract is updated, in order to interact with it we need to pass the new artifact to the PXE, since the protocol doesn't publish artifacts. -Consider this contract as an example: - -```rust -#[aztec] -contract Updatable { -... - - #[private] - fn update_to(new_class_id: ContractClassId) { - ContractInstanceRegistry::at(CONTRACT_INSTANCE_REGISTRY_CONTRACT_ADDRESS).update(new_class_id).enqueue( - &mut context, - ); - } -... -``` - -You'd upgrade it in aztec.js doing something similar to this: - -```typescript -const contract = await UpdatableContract.deploy(wallet, ...args) - .send() - .deployed(); -const updatedContractClassId = ( - await getContractClassFromArtifact(UpdatedContractArtifact) -).id; -await contract.methods.update_to(updatedContractClassId).send().wait(); -``` - -Now, when the update has happened, calling `at` with the new contract artifact will automatically update the contract instance in the PXE if it's outdated: - -```typescript -// 'at' will call PXE updateContract if outdated -const updatedContract = await UpdatedContract.at(address, wallet); -``` - -If you try to call `at` with a different contract that is not the current version, it'll fail - -```typescript -// throws when trying to update the PXE instance to RandomContract -// since the current one is UpdatedContract -await RandomContract.at(address, wallet); -``` - -### Security Considerations - -1. **Access Control** - - - Implement proper access controls for upgrade functions - - Consider customizing the upgrades delay for your needs using `set_update_delay` - -2. **State Compatibility** - - - Ensure new implementation is compatible with existing state - - Maintain the same storage layout to prevent data corruption - -3. **Testing** - - - Test upgrades thoroughly in a development environment - - Verify all existing functionality works with the new implementation - -### Example - -```rust -contract Updatable { - #[private] - fn update_to(new_class_id: ContractClassId) { - // TODO: Add access control - assert(context.msg_sender() == owner, "Unauthorized"); - - // Perform the upgrade - ContractInstanceRegistry::at(CONTRACT_INSTANCE_REGISTRY_CONTRACT_ADDRESS) - .update(new_class_id) - .enqueue(&mut context); - } - - #[private] - fn set_update_delay(new_delay: u64) { - // TODO: Add access control - ContractInstanceRegistry::at(CONTRACT_INSTANCE_REGISTRY_CONTRACT_ADDRESS) - .set_update_delay(new_delay) - .enqueue(&mut context); - } -} -``` \ No newline at end of file diff --git a/docs/pages/aztec-nr/optimizations-and-gas/gas.md b/docs/pages/aztec-nr/optimizations-and-gas/gas.md index 4d40343..a185e24 100644 --- a/docs/pages/aztec-nr/optimizations-and-gas/gas.md +++ b/docs/pages/aztec-nr/optimizations-and-gas/gas.md @@ -1,5 +1,7 @@ # Gas +## Mike's notes + - L2 vs DA gas. - Gas tables - How much gas do different operations consume, including common crypto & bignum functions. diff --git a/docs/pages/aztec-nr/optimizations-and-gas/index.md b/docs/pages/aztec-nr/optimizations-and-gas/index.md index 6b80ce2..b1ddde6 100644 --- a/docs/pages/aztec-nr/optimizations-and-gas/index.md +++ b/docs/pages/aztec-nr/optimizations-and-gas/index.md @@ -1,28 +1 @@ # Optimizations, Gas and Profiling - -Learn how to optimize your Aztec smart contracts and understand gas costs. - -## Overview - -Performance and cost optimization are critical for building efficient Aztec applications. This section covers strategies for reducing gas costs and improving contract performance. - -## Topics - -### Understanding Costs -- **[Gas](./gas)** - How gas works in Aztec and fee mechanisms - -### Optimization Techniques -- **[Profiling](./profiling)** - Tools and techniques for profiling contract performance -- **[Optimizing Functions](./optimizing-functions)** - Best practices for writing efficient functions -- **[Choosing Numeric Types](./numeric-types)** - Selecting the right numeric types for gas efficiency - -## Key Considerations - -When optimizing Aztec contracts, consider: - -- **Proof Generation Costs** - Private functions require proof generation -- **Public vs Private** - Different cost models for public and private execution -- **Storage Access** - Reading and writing state has different costs -- **Circuit Size** - Larger circuits are more expensive to prove - -Navigate to the sections above to learn specific optimization strategies. diff --git a/docs/pages/aztec-nr/optimizations-and-gas/numeric-types.md b/docs/pages/aztec-nr/optimizations-and-gas/numeric-types.md index 2437ebf..4cddacb 100644 --- a/docs/pages/aztec-nr/optimizations-and-gas/numeric-types.md +++ b/docs/pages/aztec-nr/optimizations-and-gas/numeric-types.md @@ -1,4 +1,10 @@ # Chosing Numberic Types +## Mike's notes + - Field vs u128 vs U256 vs other BigNums -- Table of costs (Defi Wonderland might have data on this) \ No newline at end of file +- Table of costs (Defi Wonderland might have data on this) + +## Ciara's notes + +Basically when you'd want to use the different types and in different contexts (I also feel iffy about this tbh) \ No newline at end of file diff --git a/docs/pages/aztec-nr/optimizations-and-gas/optimizing-functions.md b/docs/pages/aztec-nr/optimizations-and-gas/optimizing-functions.md index 20ca9f7..2317717 100644 --- a/docs/pages/aztec-nr/optimizations-and-gas/optimizing-functions.md +++ b/docs/pages/aztec-nr/optimizations-and-gas/optimizing-functions.md @@ -1,5 +1,7 @@ # Optimizing Functions +## Mike's notes + ## Private vs Public vs Utility vs Another Language (vs Unconstrained!!!) - The approaches to optimising are different. - Utility vs Unconstrained vs Another Language diff --git a/docs/pages/aztec-nr/optimizations-and-gas/profiling.md b/docs/pages/aztec-nr/optimizations-and-gas/profiling.md index cd27247..d3d1e70 100644 --- a/docs/pages/aztec-nr/optimizations-and-gas/profiling.md +++ b/docs/pages/aztec-nr/optimizations-and-gas/profiling.md @@ -1,4 +1,6 @@ # Profiling +## Mike's super helpful notes + - Profiling Functions - Profiling Transactions \ No newline at end of file diff --git a/docs/pages/aztec-nr/overview.md b/docs/pages/aztec-nr/overview.md index d440598..6366dc0 100644 --- a/docs/pages/aztec-nr/overview.md +++ b/docs/pages/aztec-nr/overview.md @@ -1,11 +1,12 @@ # Aztec.nr Overview -## Aztec.nr vs Noir +## Mike's notes -## Why do we need Aztec.nr? - -## Design principles - - (Example): - - Make it hard to shoot yourself in the foot. - - Make it clear when something is unsafe. - - Rails that intentionally trigger a developer's "WTF?" response, to ensure they understand what they're doing. \ No newline at end of file +1. Intro + 1. Noir vs Aztecnr ←- move it somewhere + 2. Justify [Aztec.nr](http://Aztec.nr) - why do we need it? + 3. Design principles + - (Example): + - Make it hard to shoot yourself in the foot. + - Make it clear when something is unsafe. + - Rails that intentionally trigger a developer's "WTF?" response, to ensure they understand what they're doing. \ No newline at end of file diff --git a/docs/pages/aztec-nr/testing.md b/docs/pages/aztec-nr/testing.md index f5bdde6..c6794f1 100644 --- a/docs/pages/aztec-nr/testing.md +++ b/docs/pages/aztec-nr/testing.md @@ -1,335 +1,9 @@ # Testing +## Ciara's notes + 1. Testing smart contracts in Noir. - TXE docs needed. 2. Testing end-to-end apps. - See Aztec.js? - -This guide shows you how to test your Aztec smart contracts using Noir's `TestEnvironment` for fast, lightweight testing. - -## Prerequisites - -- An Aztec contract project with functions to test -- Aztec sandbox running (required for `aztec test` command) -- Basic understanding of Noir syntax - -:::tip -For complex cross-chain or integration testing, see the TypeScript testing guide in the Aztec.js section. -::: - -## Write Aztec contract tests - -Use `TestEnvironment` from `aztec-nr` for contract unit testing: - -- **Fast**: Lightweight environment with mocked components -- **Convenient**: Similar to Foundry for simple contract tests -- **Limited**: No rollup circuits or cross-chain messaging - -For complex end-to-end tests, use TypeScript testing with `aztec.js`. - -## Run your tests - -Execute Aztec Noir tests using: - -```bash -aztec test -``` - -### Test execution process - -1. Compile contracts -2. Start the sandbox -3. Run `aztec test` - -:::warning -Always use `aztec test` instead of `nargo test`. The `TestEnvironment` requires the TXE (Test eXecution Environment) oracle resolver. -::: - -## Basic test structure - -```rust -use crate::MyContract; -use aztec::{ - protocol_types::address::AztecAddress, - test::helpers::test_environment::TestEnvironment, -}; - -#[test] -unconstrained fn test_basic_flow() { - // 1. Create test environment - let mut env = TestEnvironment::new(); - - // 2. Create accounts - let owner = env.create_light_account(); -} -``` - -:::info Test execution notes -- Tests run in parallel by default -- Use `unconstrained` functions for faster execution -- See all `TestEnvironment` methods [here](https://github.com/AztecProtocol/aztec-packages/blob/#include_aztec_version/noir-projects/smart-contracts/aztec/src/test/helpers/test_environment.nr) -::: - -:::tip Organizing test files -You can organize tests in separate files: - -- Create `src/test.nr` with `mod utils;` to import helper functions -- Split tests into modules like `src/test/transfer_tests.nr`, `src/test/auth_tests.nr` -- Import the test module in `src/main.nr` with `mod test;` -- Share setup functions in `src/test/utils.nr` -::: - -## Deploying contracts - -In order to test you'll most likely want to deploy a contract in your testing environment. First, instantiate a deployer: - -```rust -let deployer = env.deploy("ContractName"); - -// If on a different crate: -let deployer = env.deploy("../other_contract"); -``` - -:::warning -It is always necessary to deploy a contract in order to test it. **It is important to compile before testing**, as `aztec test` does not recompile them on changes. Think of it as regenerating the bytecode and ABI so it becomes accessible externally. -::: - -You can then choose whatever you need to initialize by interfacing with your initializer and calling it: - -```rust -let initializer = MyContract::interface().constructor(param1, param2); - -let contract_address = deployer.with_private_initializer(owner, initializer); -let contract_address = deployer.with_public_initializer(owner, initializer); -let contract_address = deployer.without_initializer(); -``` - -:::tip Reusable setup functions -Create a setup function to avoid repeating initialization code: - -```rust -pub unconstrained fn setup(initial_value: Field) -> (TestEnvironment, AztecAddress, AztecAddress) { - let mut env = TestEnvironment::new(); - let owner = env.create_light_account(); - let initializer = MyContract::interface().constructor(initial_value, owner); - let contract_address = env.deploy("MyContract").with_private_initializer(owner, initializer); - (env, contract_address, owner) -} - -#[test] -unconstrained fn test_something() { - let (env, contract_address, owner) = setup(42); - // Your test logic here -} -``` - -::: - -## Calling contract functions - -`TestEnvironment` provides methods for different function types: - -### Private functions - -```rust -// Call private function -env.call_private(caller, Token::at(token_address).transfer(recipient, 100)); - -// Returns the result -let result = env.call_private(owner, Contract::at(address).get_private_data()); -``` - -### Public functions - -```rust -// Call public function -env.call_public(caller, Token::at(token_address).mint_to_public(recipient, 100)); - -// View public state (read-only) -let balance = env.view_public(Token::at(token_address).balance_of_public(owner)); -``` - -### Utility/Unconstrained functions - -```rust -// Simulate utility/view functions (unconstrained) -let total = env.simulate_utility(Token::at(token_address).balance_of_private(owner)); -``` - -:::tip Helper function pattern -Create helper functions for common assertions: -```rust -pub unconstrained fn check_balance( - env: TestEnvironment, - token_address: AztecAddress, - owner: AztecAddress, - expected: u128, -) { - assert_eq( - env.simulate_utility(Token::at(token_address).balance_of_private(owner)), - expected - ); -} -``` -::: - -## Creating accounts - -Two types of accounts are available: - -```rust -// Light account - fast, limited features -let owner = env.create_light_account(); - -// Contract account - full features, slower -let owner = env.create_contract_account(); -``` - -:::info Account type comparison -**Light accounts:** -- Fast to create -- Work for simple transfers and tests -- Cannot process authwits -- No account contract deployed - -**Contract accounts:** -- Required for authwit testing -- Support account abstraction features -- Slower to create (deploys account contract) -- Needed for cross-contract authorization -::: - -:::tip Choosing account types -```rust -pub unconstrained fn setup(with_authwits: bool) -> (TestEnvironment, AztecAddress, AztecAddress) { - let mut env = TestEnvironment::new(); - let (owner, recipient) = if with_authwits { - (env.create_contract_account(), env.create_contract_account()) - } else { - (env.create_light_account(), env.create_light_account()) - }; - // ... deploy contracts ... - (env, owner, recipient) -} -``` -::: - -## Testing with authwits - -[Authwits](how_to_use_authwit.md) allow one account to authorize another to act on its behalf. - -:::warning -Authwits require **contract accounts**, not light accounts. -::: - -### Import authwit helpers - -```rust -use aztec::test::helpers::authwit::{ - add_private_authwit_from_call_interface, - add_public_authwit_from_call_interface, -}; -``` - -### Private authwits - -```rust -#[test] -unconstrained fn test_private_authwit() { - // Setup with contract accounts (required for authwits) - let (env, token_address, owner, spender) = setup(true); - - // Create the call that needs authorization - let amount = 100; - let nonce = 7; // Non-zero nonce for authwit - let burn_call = Token::at(token_address).burn_private(owner, amount, nonce); - - // Grant authorization from owner to spender - add_private_authwit_from_call_interface(owner, spender, burn_call); - - // Spender can now execute the authorized action - env.call_private(spender, burn_call); -} -``` - -### Public authwits - -```rust -#[test] -unconstrained fn test_public_authwit() { - let (env, token_address, owner, spender) = setup(true); - - // Create public action that needs authorization - let transfer_call = Token::at(token_address).transfer_public(owner, recipient, 100, nonce); - - // Grant public authorization - add_public_authwit_from_call_interface(owner, spender, transfer_call); - - // Execute with authorization - env.call_public(spender, transfer_call); -} - -## Time traveling - -Contract calls do not advance the timestamp by default, despite each of them resulting in a block with a single transaction. Block timestamp can instead by manually manipulated by any of the following methods: - -```rust -// Sets the timestamp of the next block to be mined, i.e. of the next public execution. Does not affect private execution. -env.set_next_block_timestamp(block_timestamp); - -// Same as `set_next_block_timestamp`, but moving time forward by `duration` instead of advancing to a target timestamp. -env.advance_next_block_timestamp_by(duration); - -// Mines an empty block at a given timestamp, causing the next public execution to occur at this time (like `set_next_block_timestamp`), but also allowing for private execution to happen using this empty block as the anchor block. -env.mine_block_at(block_timestamp); -``` - -## Testing failure cases - -Test functions that should fail using annotations: - -### Generic failure - -```rust -#[test(should_fail)] -unconstrained fn test_unauthorized_access() { - let (env, contract, owner) = setup(false); - let attacker = env.create_light_account(); - - // This should fail because attacker is not authorized - env.call_private(attacker, Contract::at(contract).owner_only_function()); -} -``` - -### Specific error message - -```rust -#[test(should_fail_with = "Balance too low")] -unconstrained fn test_insufficient_balance() { - let (env, token, owner, recipient) = setup(false); - - // Try to transfer more than available - let balance = 100; - let transfer_amount = 101; - - env.call_private(owner, Token::at(token).transfer(recipient, transfer_amount)); -} -``` - -### Testing authwit failures - -```rust -#[test(should_fail_with = "Unknown auth witness for message hash")] -unconstrained fn test_missing_authwit() { - let (env, token, owner, spender) = setup(true); - - // Try to burn without authorization - let burn_call = Token::at(token).burn_private(owner, 100, 1); - - // No authwit granted - this should fail - env.call_private(spender, burn_call); -} - -``` - diff --git a/docs/pages/aztec-vs-ethereum.md b/docs/pages/aztec-vs-ethereum.md new file mode 100644 index 0000000..891951c --- /dev/null +++ b/docs/pages/aztec-vs-ethereum.md @@ -0,0 +1,2 @@ +# Aztec vs Ethereum + diff --git a/docs/pages/concepts.md b/docs/pages/concepts.md deleted file mode 100644 index d659242..0000000 --- a/docs/pages/concepts.md +++ /dev/null @@ -1,13 +0,0 @@ -# Concepts - -## Private vs Public State - -## Private State: Notes - -## Public State: Account-based - -## Commitments - -## Nullifiers - -## The Five Merkle Trees diff --git a/docs/pages/foundational-topics/addresses-accounts-and-keys.md b/docs/pages/foundational-topics/addresses-accounts-and-keys.md new file mode 100644 index 0000000..9131c4b --- /dev/null +++ b/docs/pages/foundational-topics/addresses-accounts-and-keys.md @@ -0,0 +1 @@ +# Addresses, Accounts and Keys \ No newline at end of file diff --git a/docs/pages/foundational-topics/addresses-and-keys.md b/docs/pages/foundational-topics/addresses-and-keys.md deleted file mode 100644 index 1894257..0000000 --- a/docs/pages/foundational-topics/addresses-and-keys.md +++ /dev/null @@ -1,312 +0,0 @@ -# Addresses and Keys - -Aztec has native account abstraction. Every account in Aztec is a smart contract. - -In this section, you'll learn about Aztec's account abstraction, Aztec accounts and address derivation, how wallets relate to accounts, and how the entrypoints are defined. - -## Account Abstraction (AA) - -With account abstraction, the identity of a user is usually represented by a smart contract. That makes user's onchain identity more flexible than simply using private/public keys. For example, Bitcoin has rigid accounts that must be a private key, whereas a user might want their onchain identity to be controlled by a physical passport. - -Among the account parts to be abstracted are authentication (“Who I am”), authorization (“What I am allowed to do”), replay protection, fee payment, and execution. - -Some account features unlocked by account abstraction are account recovery, gas sponsorship, and support of signatures other than ECDSA, such as more efficient signatures (e.g. Schnorr, BLS), or more user-friendly ones (e.g. smartphone secure enclave). - -### Protocol vs application level - -AA can be implemented at the protocol level is called native Account Abstraction. In this case, all the accounts on the network are smart contracts. AA can also be implemented at the smart-contract level, then we call it non-native Account Abstraction. In this case, there might be both EOAs and accounts controlled by smart contracts. - -In the case of Aztec, we have native Account Abstraction. - -## Aztec Account Abstraction - -### Authorization abstraction and DoS attacks - -While we talk about “arbitrary verification logic” describing the intuition behind AA, the logic is usually not really arbitrary. The verification logic (i.e. what is checked as an authorization) is limited to make the verification time fast and bounded. If it is not bounded, an attacker can flood the mempool with expensive invalid transactions, clogging the network. That is the case for all chains where transaction validity is checked by the sequencer. - -On Aztec, there is no limitation on the complexity of verification logic (what does it mean for the transaction to be valid). Whatever conditions it checks, the proof (that the sequencer needs to verify) is independent of its complexity. - -This unlocks a whole universe of new use cases and optimization of existing ones. Whenever the dapp can benefit from moving expensive computations offchain, Aztec will provide a unique chance for an optimization. That is to say, on traditional chains users pay for each executed opcode, hence more complex operations (e.g. alternative signature verification) are quite expensive. In the case of Aztec, it can be moved offchain so that it becomes almost free. The user pays for the operations in terms of client-side prover time. However, this refers to Aztec's client-side proving feature and not directly AA. - -Couple of examples: - -- Multisig contract with an arbitrary number of parties that can verify any number of signatures for free. -- Oracle contract with an arbitrary number of data providers that can verify any number of data entries for free. - -## Aztec account - -Smart contracts on Aztec are represented by an "address", which is a hexadecimal number that uniquely represents an entity on the Aztec network. An address is derived by hashing information specific to the entity represented by the address. This information includes contract bytecode and the public keys used in private execution for encryption and nullification. This means addresses are deterministic. - -Aztec has no concept of EOAs (Externally Owned Accounts). Every account is implemented as a contract. - -### Entrypoints - -Account contracts usually have a specific function called `entrypoint`. It serves as the interface for interaction with the smart contract and can be called by external users or other smart contracts. - -An `entrypoint` function receives the actions to be carried out and an authentication payload. In pseudocode: - -```text -publicKey: PublicKey; - -def entryPoint(payload): - let { privateCalls, publicCalls, nonce, signature } = payload; - let payloadHash = hash(privateCalls, publicCalls, nonce); - validateSignature(this.publicKey, signature, payloadHash); - - foreach privateCall in privateCalls: - let { to, data, value } = privateCall; - call(to, data, value); - - foreach publicCall in publicCalls: - let { to, data, value, gasLimit } = publicCall; - enqueueCall(to, data, value, gasLimit); -``` - -A request for executing an action requires: - -- The `origin` contract to execute as the first step. -- The initial function to call (usually `entrypoint`). -- The arguments (which encode the private and public calls to run as well as any signatures). - -### Non-standard entrypoints - -Since the `entrypoint` interface is not enshrined, there is nothing that differentiates an account contract from an application contract. This allows implementing functions that can be called by any user and are just intended to advance the state of a contract. - -For example, a lottery contract, where at some point a prize needs to be paid out to its winners. This `pay` action does not require authentication and does not need to be executed by any user in particular, so anyone could submit a transaction that defines the lottery contract itself as `origin` and `pay` as `entrypoint` function. However, it's on the contract to define how fees for the prize claim will be paid as they won't be paid by the account contract. - -For an example of this behavior see our [e2e_crowdfunding_and_claim test](https://github.com/AztecProtocol/aztec-packages/blob/88b5878dd4b95d691b855cd84153ba884adf25f8/yarn-project/end-to-end/src/e2e_crowdfunding_and_claim.test.ts#L322) and the [SignerLess wallet](https://github.com/AztecProtocol/aztec-packages/blob/master/yarn-project/aztec.js/src/wallet/signerless_wallet.ts) implementation. Notice that the Signerless wallet doesn't invoke an `entrypoint` function of an account contract but instead invokes the target contract function directly. - -:::info - -Entrypoints for the following cases: - -- If no contract `entrypoint` is used `msg_sender` is set to `Field.max`. -- In a private to public `entrypoint`, `msg_sender` is the contract making the private to public call. -- When calling the `entrypoint` on an account contract, `msg_sender` is set to the account contract address. - -::: - -### Account contracts and wallets - -Account contracts are tightly coupled to the wallet software that users use to interact with the protocol. Dapps submit to the wallet software one or more function calls to be executed (e.g. "call swap in X contract"), and the wallet encodes and signs the request as a valid payload for the user's account contract. The account contract then validates the request encoded and signed by the wallet, and executes the function calls requested by the dapp. - -### Account Initialization - -When a user wants to interact with the network's **public** state, they need to deploy their account contract. A contract instance is considered to be publicly deployed when it has been broadcasted to the network via the canonical `ContractInstanceRegistry` contract, which also emits a deployment nullifier associated to the deployed instance. - -However, to send fully **private** transactions, it's enough to initialize the account contract (public deployment is not needed). The default state for any given address is to be uninitialized, meaning a function with the initializer annotation has not been called. The contract is initialized when one of the functions marked with the `#[initializer]` annotation has been invoked. Multiple functions in the contract can be marked as initializers. Contracts may have functions that skip the initialization check (marked with `#[noinitcheck]`). - -Account deployment and initialization are not required to receive notes. The user address is deterministically derived from the encryption public key and the account contract they intend to deploy, so that funds can be sent to an account that hasn't been deployed yet. - -Users will need to pay transaction fees in order to deploy their account contract. This can be done by sending fee juice to their account contract address (which can be derived deterministically, as mentioned above), so that the account has funds to pay for its own deployment. Alternatively, the fee can be paid for by another account, using [fee abstraction](#fee-abstraction). - -## What is an account address - -Address is derived from the [address keys](keys.md#address-keys). While the AddressPublicKey is an elliptic curve point of the form (x,y) on the [Grumpkin elliptic curve](https://github.com/AztecProtocol/aztec-connect/blob/9374aae687ec5ea01adeb651e7b9ab0d69a1b33b/markdown/specs/aztec-connect/src/primitives.md), the address is its x coordinate. The corresponding y coordinate can be derived if needed. For x to be a legitimate address, address there should exist a corresponding y that satisfies the curve equation. Any field element cannot work as an address. - -### Complete address - -Because of the contract address derivation scheme, you can check that a given set of public [keys](keys.md) corresponds to a given address by trying to recompute it. - -If Alice wants Bob to send her a note, it's enough to share with him her address (x coordinate of the AddressPublicKey). - -However, if Alice wants to spend her notes (i.e. to prove that the nullifier key inside her address is correct) she needs her complete address. It is represented by: - -- all the user's public keys, -- [partial address](keys.md#address-keys), -- contract address. - -## Authorizing actions - -Account contracts are also expected, though not required by the protocol, to implement a set of methods for authorizing actions on behalf of the user. During a transaction, a contract may call into the account contract and request the user authorization for a given action, identified by a hash. This pattern is used, for instance, for transferring tokens from an account that is not the caller. - -When executing a private function, this authorization is checked by requesting an authentication witness from the execution oracle, which is usually a signed message. Authentication Witness is a scheme for authenticating actions on Aztec, so users can allow third-parties (e.g. contracts) to execute an action on their behalf. - -The user's Private eXecution Environment (PXE) is responsible for storing these auth witnesses and returning them to the requesting account contract. Auth witnesses can belong to the current user executing the local transaction, or to another user who shared it offchain. - -However, during a public function execution, it is not possible to retrieve a value from the local oracle. To support authorizations in public functions, account contracts should save in a public authwit registry what actions have been pre-authorized by their owner. - -These two patterns combined allow an account contract to answer whether an action `is_valid_impl` for a given user both in private and public contexts. - -You can read more about authorizing actions with authorization witnesses in the concepts section. - -:::info - -Transaction simulations in the PXE are not currently simulated, this is future work described [here](https://github.com/AztecProtocol/aztec-packages/issues/9133). This means that any transaction simulations that call into a function requiring an authwit will require the user to provide an authwit. Without simulating simulations, the PXE can't anticipate what authwits a transaction may need, so developers will need to manually request these authwits from users. In the future, transactions requiring authwits will be smart enough to ask the user for the correct authwits automatically. - -::: - -## Nonce and fee abstraction - -Beyond the authentication logic abstraction, there are nonce abstraction and fee abstraction. - -### Nonce abstraction - -Nonce is a unique number and it is utilized for replay protection (i.e. preventing users from executing a transaction more than once and unauthorized reordering). - -In particular, nonce management defines what it means for a transaction to be canceled, the rules of transaction ordering, and replay protection. In Ethereum, nonce is enshrined into the protocol. On the Aztec network, nonce is abstracted i.e. if a developer wants to customize it, they get to decide how they handle replay protection, transaction cancellation, as well as ordering. - -Take as an example the transaction cancellation logic. It can be done through managing nullifiers. Even though we usually refer to a nullifier as a creature utilized to consume a note, in essence, a nullifier is an emitted value whose uniqueness is guaranteed by the protocol. If we want to cancel a transaction before it was mined, we can send another transaction with higher gas price that emits the same nullifier (i.e. nullifier with the same value, for example, 5). The second transaction will invalidate the original one, since nullifiers cannot be repeated. - -Nonce abstraction is mostly relevant to those building wallets. For example, a developer can design a wallet that allows sending big transactions with very low priority fees because the transactions are not time sensitive (i.e. the preference is that a transaction is cheap and doesn't matter if it is slow). If one tries to apply this logic today on Ethereum (under sequential nonces), when they send a large, slow transaction they can't send any other transactions until that first large, slow transaction is processed. - -### Fee abstraction - -It doesn't have to be the transaction sender who pays the transaction fees. Wallets or dapp developers can choose any payment logic they want using a paymaster. To learn more about fees on Aztec – check the fees section. - -Paymaster is a contract that can pay for transactions on behalf of users. It is invoked during the private execution stage and set as the fee payer. - -- It can be managed by a dapp itself (e.g. a DEX can have its own paymaster) or operate as a third party service available for everyone. -- Fees can be paid publicly or privately. -- Fees can be paid in any token that a paymaster accepts. - -Fee abstraction unlocks use cases like: - -- Sponsored transactions (e.g. the dapp's business model might assume revenue from other streams besides transaction fees or the dapp might utilize sponsored transaction mechanics for marketing purposes). For example, sponsoring the first ten transactions for every user. -- Flexibility in the currency used in transaction payments (e.g. users can pay for transactions in ERC-20 token). - -## Types of keys - -Each Aztec account is backed by four key pairs: - -- Nullifier keys – used to spend notes. -- Address keys – this is an auxiliary key used for the address derivation; it’s internally utilized by the protocol and does not require any action from developers. -- Incoming viewing keys – used to encrypt a note for the recipient. -- Signing keys – an optional key pair used for account authorization. - -The first three pairs are embedded into the protocol while the signing key is abstracted up to the account contract developer. - -### Nullifier keys - -Nullifier keys are presented as a pair of the master nullifier public key (`Npk_m`) and the master nullifier secret key (`nsk_m`). - -To spend a note, the user computes a nullifier corresponding to this note. A nullifier is a hash of the note hash and app-siloed nullifier secret key, the latter is derived using the nullifier master secret key. To compute the nullifier, the protocol checks that the app-siloed key is derived from the master key for this contract and that master nullifier public key is linked to the note owner's address. - -### Address keys - -Address keys are used for account address derivation. - -![address-derivation](/address_derivation.png) - -Address keys are a pair of keys `AddressPublicKey` and `address_sk` where `address_sk` is a scalar defined as `address_sk = pre_address + ivsk` and `AddressPublicKey` is an elliptic curve point defined as `AddressPublicKey = address_sk * G`. This is useful for encrypting notes for the recipient with only their address. - -`pre_address` can be thought of as a hash of all account’s key pairs and functions in the account contract. - -In particular, - -``` -pre_address := poseidon2(public_keys_hash, partial_address) -public_keys_hash := poseidon2(Npk_m, Ivpk_m, Ovpk_m, Tpk_m) -partial_address := poseidon2(contract_class_id, salted_initialization_hash) -contract_class_id := poseidon2(artifact_hash, fn_tree_root, public bytecode commitment) -salted_initialization_hash := poseidon2(deployer_address, salt, constructor_hash) -``` - -where - -- `artifact_hash` – hashes data from the Contract Artifact file that contains the data needed to interact with a specific contract, including its name, functions that can be executed, and the interface and code of those functions. -- `fn_tree_root` – hashes pairs of verification keys and function selector (`fn_selector`) of each private function in the contract. -- `fn_selector` – the first four bytes of the hashed `function signature` where the last one is a string consisting of the function's name followed by the data types of its parameters. -- `public bytecode commitment` – takes contract's code as an input and returns short commitment. -- `deployer_address` – account address of the contract deploying the contract. -- `salt` – a user-specified 32-byte value that adds uniqueness to the deployment. -- `constructor_hash` – hashes `constructor_fn_selector` and `constructor_args` where the last one means public inputs of the contract. - -:::note -Under the current design Aztec protocol does not use `Ovpk` (outgoing viewing key) and `Tpk` (tagging key). However, formally they still exist and can be used by developers for some non-trivial design choices if needed. -::: - -### Incoming viewing keys - -The incoming viewing public key (`Ivpk`) is used by the sender to encrypt a note for the recipient. The corresponding incoming viewing secret key (`ivsk`) is used by the recipient to decrypt the note. - -When it comes to notes encryption and decryption: - -- For each note, there is a randomly generated ephemeral key pair (`esk`, `Epk`) where `Epk = esk * G`. -- The `AddressPublicKey` (derived from the `ivsk`) together with `esk` are encrypted as a secret `S`, `S = esk * AddressPublicKey`. -- `symmetric_encryption_key = hash(S)` -- `Ciphertext = aes_encrypt(note, symmetric_encryption_key)` -- The recipient gets a pair (`Epk`, `Ciphertext`) -- The recipient uses the `address_sk` to decrypt the secret: `S = Epk * address_sk`. -- The recipient uses the decrypted secret to decrypt the ciphertext. - -### Signing keys - -Thanks to the native account abstraction, authorization logic can be implemented in an alternative way that is up to the developer (e.g. using Google authorization credentials, vanilla password logic or Face ID mechanism). In these cases, signing keys may not be relevant. - -However if one wants to implement authorization logic containing signatures (e.g. ECDSA or Shnorr) they will need signing keys. Usually, an account contract will validate a signature of the incoming payload against a known signing public key. - -This is a snippet of our Schnorr Account contract implementation, which uses Schnorr signatures for authentication: - -#include_code is_valid_impl noir-projects/noir-contracts/contracts/account/schnorr_account_contract/src/main.nr rust - -### Storing signing keys - -Since signatures are fully abstracted, how the public key is stored in the contract is abstracted as well and left to the developer of the account contract. Among a few common approaches are storing the key in a private note, in an immutable private note, using delayed public mutable state, reusing other in-protocol keys, or a separate keystore. Below, we elaborate on these approaches. - -#### Using a private note​ - -Storing the signing public key in a private note makes it accessible from the `entrypoint` function, which is required to be a private function, and allows for rotating the key when needed. However, keep in mind that reading a private note requires nullifying it to ensure it is up-to-date, so each transaction you send will destroy and recreate the public key so the protocol circuits can be sure that the notes are not stale. This incurs cost for every transaction. - -#### Using an immutable private note​ - -Using an immutable private note removes the need to nullify the note on every read. This generates no nullifiers or new commitments per transaction. However, it does not allow the user to rotate their key. - -#include_code public_key noir-projects/noir-contracts/contracts/account/schnorr_account_contract/src/main.nr rust - -:::note -When it comes to storing the signing key in a private note, there are several details that rely on the wallets: - -- A note with a key is managed similar to any other private note. Wallets are expected to backup all the notes so that they can be restored on another device (e.g. if the user wants to move to another device). -- The note with the key might exist locally only (in PXE) or it can be broadcasted as an encrypted note by the wallet to itself. In the second case, this note will also exist on Aztec. - ::: - -#### Using Delayed Public Mutable state - -By Delayed Public Mutable we mean privately readable publicly mutable state. - -To make public state accessible privately, there is a delay window in public state updates. One needs this window to be able to generate proofs client-side. This approach would not generate additional nullifiers and commitments for each transaction while allowing the user to rotate their key. However, this causes every transaction to now have a time-to-live determined by the frequency of the delayed mutable state, as well as imposing restrictions on how fast keys can be rotated due to minimum delays. - -#### Reusing some of the in-protocol keys - -It is possible to use some of the key pairs defined in protocol (e.g. incoming viewing keys) as the signing key. Since this key is part of the address preimage, it can be validated against the account contract address rather than having to store it. However, this approach is not recommended since it reduces the security of the user's account. - -#### Using a separate keystore - -Since there are no restrictions on the actions that an account contract may execute for authenticating a transaction (as long as these are all private function executions), the signing public keys can be stored in a separate keystore contract that is checked on every call. In this case, each user could keep a single contract that acts as a keystore, and have multiple account contracts that check against that keystore for authorization. This will incur a higher proving time for each transaction, but has no additional cost in terms of fees. - -### Keys generation - -All key pairs (except for the signing keys) are generated in the Private Execution Environment (PXE) when a user creates an account. PXE is also responsible for the further key management (oracle access to keys, app siloed keys derivation, etc.) - -### Keys derivation - -All key pairs are derived using elliptic curve public-key cryptography on the [Grumpkin curve](https://github.com/AztecProtocol/aztec-connect/blob/9374aae687ec5ea01adeb651e7b9ab0d69a1b33b/markdown/specs/aztec-connect/src/primitives.md), where the secret key is represented as a scalar and the public key is represented as an elliptic curve point multiplied by that scalar. - -The address private key is an exception and derived in a way described above in the [Address keys](#address-keys) section. - -### The special case of escrow contracts - -Typically, for account contracts the public keys will be non-zero and for non-account contracts zero. - -An exception (a non-account contract which would have some of the keys non-zero) is an escrow contract. Escrow contract is a type of contract which on its own is an "owner" of a note meaning that it has a `Npk_m` registered and the notes contain this `Npk_m`. - -Participants in this escrow contract would then somehow get a hold of the escrow's `nsk_m` and nullify the notes based on the logic of the escrow. An example of an escrow contract is a betting contract. In this scenario, both parties involved in the bet would be aware of the escrow's `nsk_m`. The escrow would then release the reward only to the party that provides a "proof of winning". - -### App-siloed keys - -Nullifier keys and Incoming view keys are app-siloed meaning they are scoped to the contract that requests them. This means that the keys used for the same user in two different application contracts will be different. - -App-siloed keys allow to minimize damage of potential key leaks as a leak of the scoped keys would only affect one application. - -App-siloed keys are derived from the corresponding master keys and the contract address. For example, for the app-siloed nullifier secret key: `nsk_app = hash(nsk_m, app_contract_address)`. - -App-siloed keys are derived in PXE every time the user interacts with the application. - -App-siloed incoming viewing key also allows per-application auditability. A user may choose to disclose this key for a given application to an auditor or regulator (or for 3rd party interfaces, e.g. giving access to a block explorer to display my activity), as a means to reveal all their activity within that context, while retaining privacy across all other applications in the network. - -### Key rotation - -Key rotation is the process of creating new signing keys to replace existing keys. By rotating encryption keys on a regular schedule or after specific events, you can reduce the potential consequences of the key being compromised. - -On Aztec, key rotation is impossible for nullifier keys, incoming viewing keys and address keys as all of them are embedded into the address and address is unchangeable. In the meanwhile, signing keys can be rotated. diff --git a/docs/pages/foundational-topics/call-types.md b/docs/pages/foundational-topics/call-types.md deleted file mode 100644 index 86b5c95..0000000 --- a/docs/pages/foundational-topics/call-types.md +++ /dev/null @@ -1,193 +0,0 @@ -# Call Types - -## What is a Call - -We say that a smart contract is called when one of its functions is invoked and its code is run. This means there'll be: - -- a caller -- arguments -- return values -- a call status (successful or failed) - -There are multiple types of calls, and some of the naming can make things **very** confusing. This page lists the different call types and execution modes, pointing out key differences between them. - -## Ethereum Call Types - -Even though we're discussing Aztec, its design is heavily influenced by Ethereum and many of the APIs and concepts are quite similar. It is therefore worthwhile to briefly review how things work there and what naming conventions are used to provide context to the Aztec-specific concepts. - -Broadly speaking, Ethereum contracts can be thought of as executing as a result of three different things: running certain EVM opcodes, running Solidity code (which compiles to EVM opcodes), or via the node JSON-RPC interface (e.g. when executing transactions). - -### EVM - -Certain opcodes allow contracts to make calls to other contracts, each with different semantics. We're particularly interested in `CALL` and `STATICCALL`, and how those relate to contract programming languages and client APIs. - -#### `CALL` - -This is the most common and basic type of call. It grants execution control to the caller until it eventually returns. No special semantics are in play here. Most Ethereum transactions spend the majority of their time in `CALL` contexts. - -#### `STATICCALL` - -This behaves almost exactly the same as `CALL`, with one key difference: any state-changing operations are forbidden and will immediately cause the call to fail. This includes writing to storage, emitting logs, or deploying new contracts. This call is used to query state on an external contract, e.g. to get data from a price oracle, check for access control permissions, etc. - -#### Others - -The `CREATE` and `CREATE2` opcodes (for contract deployment) also result in something similar to a `CALL` context, but all that's special about them has to do with how deployments work. `DELEGATECALL` (and `CALLCODE`) are somewhat complicated to understand but don't have any Aztec equivalents, so they are not worth covering. - -### Solidity - -Solidity (and other contract programming languages such as Vyper) compile down to EVM opcodes, but it is useful to understand how they map language concepts to the different call types. - -#### Mutating External Functions - -These are functions marked `payable` (which can receive ETH, which is a state change) or with no mutability declaration (sometimes called `nonpayable`). When one of these functions is called on a contract, the `CALL` opcode is emitted, meaning the callee can perform state changes, make further `CALL`s, etc. - -It is also possible to call such a function with `STATICCALL` manually (e.g. using assembly), but the execution will fail as soon as a state-changing opcode is executed. - -#### `view` - -An external function marked `view` will not be able to mutate state (write to storage, etc.), it can only _view_ the state. Solidity will emit the `STATICCALL` opcode when calling these functions, since its restrictions provide added safety to the caller (e.g. no risk of reentrancy). - -Note that it is entirely possible to use `CALL` to call a `view` function, and the result will be the exact same as if `STATICCALL` had been used. The reason why `STATICCALL` exists is so that _untrusted or unknown_ contracts can be called while still being able to reason about correctness. From the [EIP](https://eips.ethereum.org/EIPS/eip-214): - -> '`STATICCALL` adds a way to call other contracts and restrict what they can do in the simplest way. It can be safely assumed that the state of all accounts is the same before and after a static call.' - -### JSON-RPC - -From outside the EVM, calls to contracts are made via [JSON-RPC](https://ethereum.org/en/developers/docs/apis/json-rpc/) methods, typically from some client library that is aware of contract ABIs, such as [ethers.js](https://docs.ethers.org/v5) or [viem](https://viem.sh/). - -#### `eth_sendTransaction` - -This method is how transactions are sent to a node to get them to be broadcast and eventually included in a block. The specified `to` address will be called in a `CALL` context, with some notable properties: - -- there are no return values, even if the contract function invoked does return some data -- there is no explicit caller: it is instead derived from a provided signature - -Some client libraries choose to automatically issue `eth_sendTransaction` when calling functions from a contract ABI that are not marked as `view` - [ethers is a good example](https://docs.ethers.org/v5/getting-started/#getting-started--writing). Notably, this means that any return value is lost and not available to the calling client - the library typically returns a transaction receipt instead. If the return value is required, then the only option is to simulate the call `eth_call`. - -Note that it is possible to call non state-changing functions (i.e. `view`) with `eth_sendTransaction` - this is always meaningless. What transactions do is change the blockchain state, so all calling such a function achieves is for the caller to lose funds by paying for gas fees. The sole purpose of a `view` function is to return data, and `eth_sendTransaction` does not make the return value available. - -#### `eth_call` - -This method is the largest culprit of confusion around calls, but unfortunately requires understanding of all previous concepts in order to be explained. Its name is also quite unhelpful. - -What `eth_call` does is simulate a transaction (a call to a contract) given the current blockchain state. The behavior will be the exact same as `eth_sendTransaction`, except: - -- no actual transaction will be created -- while gas _will_ be measured, there'll be no transaction fees of any kind -- no signature is required: the `from` address is passed directly, and can be set to any value (even if the private key is unknown, or if they are contract addresses!) -- the return value of the called contract is available - -`eth_call` is typically used for one of the following: - -- query blockchain data, e.g. read token balances -- preview the state changes produced by a transaction, e.g. the transaction cost, token balance changes, etc - -Because some libraries ([such as ethers](https://docs.ethers.org/v5/getting-started/#getting-started--reading)) automatically use `eth_call` for `view` functions (which when called via Solidity result in the `STATICCALL` opcode), these concepts can be hard to tell apart. The following bears repeating: **an `eth_call`'s call context is the same as `eth_sendTransaction`, and it is a `CALL` context, not `STATICCALL`.** - -## Aztec Call Types - -Large parts of the Aztec Network's design are still not finalized, and the nitty-gritty of contract calls is no exception. This section won't therefore contain a thorough review of these, but rather list some of the main ways contracts can currently be interacted with, with analogies to Ethereum call types when applicable. - -While Ethereum contracts are defined by bytecode that runs on the EVM, Aztec contracts have multiple modes of execution depending on the function that is invoked. - -### Private Execution - -Contract functions marked with `#[private]` can only be called privately, and as such 'run' in the user's device. Since they're circuits, their 'execution' is actually the generation of a zk-SNARK proof that'll later be sent to the sequencer for verification. - -#### Private Calls - -Private functions from other contracts can be called either regularly or statically by using the `.call()` and `.static_call` functions. They will also be 'executed' (i.e. proved) in the user's device, and `static_call` will fail if any state changes are attempted (like the EVM's `STATICCALL`). - -#include_code private_call /noir-projects/noir-contracts/contracts/app/lending_contract/src/main.nr rust - -Unlike the EVM however, private execution doesn't revert in the traditional way: in case of error (e.g. a failed assertion, a state changing operation in a static context, etc.) the proof generation simply fails and no transaction request is generated, spending no network gas or user funds. - -#### Public Calls - -Since public execution can only be performed by the sequencer, public functions cannot be executed in a private context. It is possible however to _enqueue_ a public function call during private execution, requesting the sequencer to run it during inclusion of the transaction. It will be [executed in public](#public-execution) normally, including the possibility to enqueue static public calls. - -Since the public call is made asynchronously, any return values or side effects are not available during private execution. If the public function fails once executed, the entire transaction is reverted including state changes caused by the private part, such as new notes or nullifiers. Note that this does result in gas being spent, like in the case of the EVM. - -#include_code enqueue_public /noir-projects/noir-contracts/contracts/app/lending_contract/src/main.nr rust - -It is also possible to create public functions that can _only_ be invoked by privately enqueueing a call from the same contract, which can be very useful to update public state after private execution (e.g. update a token's supply after privately minting). This is achieved by annotating functions with `#[internal]`. - -A common pattern is to enqueue public calls to check some validity condition on public state, e.g. that a deadline has not expired or that some public value is set. - -#include_code enqueueing /noir-projects/noir-contracts/contracts/protocol/router_contract/src/utils.nr rust - -Note that this reveals what public function is being called on what contract, and perhaps more importantly which contract enqueued the call during private execution. -For this reason we've created a canonical router contract which implements some of the checks commonly performed: this conceals the calling contract, as the `context.msg_sender()` in the public function will be the router itself (since it is the router that enqueues the public call). - -An example of how a deadline can be checked using the router contract follows: - -#include_code call-check-deadline /noir-projects/noir-contracts/contracts/app/crowdfunding_contract/src/main.nr rust - -`privately_check_timestamp` and `privately_check_block_number` are helper functions around the call to the router contract: - -#include_code helper_router_functions /noir-projects/noir-contracts/contracts/protocol/router_contract/src/utils.nr rust - -This is what the implementation of the check timestamp functionality looks like: - -#include_code check_timestamp /noir-projects/noir-contracts/contracts/protocol/router_contract/src/main.nr rust - -:::note -Note that the router contract is not currently part of the [aztec-nr repository](https://github.com/AztecProtocol/aztec-nr). -To add it as a dependency point to the aztec-packages repository instead: - -```toml -[dependencies] -aztec = { git = "https://github.com/AztecProtocol/aztec-packages/", tag = "#include_aztec_version", directory = "noir-projects/noir-contracts/contracts/protocol/router_contract/src" } -``` - -::: - -Even with the router contract achieving good privacy is hard. -For example, if the value being checked against is unique and stored in the contract's public storage, it's then simple to find private transactions that are using that value in the enqueued public reads, and therefore link them to this contract. -For this reason it is encouraged to try to avoid public function calls and instead privately read Shared State when possible. - -### Public Execution - -Contract functions marked with `#[public]` can only be called publicly, and are executed by the sequencer. The computation model is very similar to the EVM: all state, parameters, etc. are known to the entire network, and no data is private. Static execution like the EVM's `STATICCALL` is possible too, with similar semantics (state can be accessed but not modified, etc.). - -Since private calls are always run in a user's device, it is not possible to perform any private execution from a public context. A reasonably good mental model for public execution is that of an EVM in which some work has already been done privately, and all that is know about it is its correctness and side-effects (new notes and nullifiers, enqueued public calls, etc.). A reverted public execution will also revert the private side-effects. - -Public functions in other contracts can be called both regularly and statically, just like on the EVM. - -#include_code public_call /noir-projects/noir-contracts/contracts/fees/fpc_contract/src/main.nr rust - -:::note -This is the same function that was called by privately enqueuing a call to it! Public functions can be called either directly in a public context, or asynchronously by enqueuing in a private context. -::: - -### Utility - -Contract functions marked with `#[utility]` cannot be called as part of a transaction, and are only invoked by applications that interact with contracts to perform state queries from an offchain client (from both private and public state!) or to modify local contract-related PXE state (e.g. when processing logs in Aztec.nr). No guarantees are made on the correctness of the result since the entire execution is unconstrained and heavily reliant on oracle calls. It is possible however to verify that the bytecode being executed is the correct one, since a contract's address includes a commitment to all of its utility functions. - -### aztec.js - -There are three different ways to execute an Aztec contract function using the `aztec.js` library, with close similarities to their [JSON-RPC counterparts](#json-rpc). - -#### `simulate` - -This is used to get a result out of an execution, either private or public. It creates no transaction and spends no gas. The mental model is fairly close to that of [`eth_call`](#eth_call), in that it can be used to call any type of function, simulate its execution and get a result out of it. `simulate` is also the only way to run [utility functions](#utility). - -#include_code public_getter /noir-projects/noir-contracts/contracts/app/auth_contract/src/main.nr rust - -#include_code simulate_function yarn-project/end-to-end/src/composed/docs_examples.test.ts typescript - -:::warning -No correctness is guaranteed on the result of `simulate`! Correct execution is entirely optional and left up to the client that handles this request. -::: - -#### `prove` - -This creates and returns a transaction request, which includes proof of correct private execution and side-effects. The request is not broadcast however, and no gas is spent. It is typically used in testing contexts to inspect transaction parameters or to check for execution failure. - -#include_code local-tx-fails /yarn-project/end-to-end/src/guides/dapp_testing.test.ts typescript - -#### `send` - -This is the same as [`prove`](#prove) except it also broadcasts the transaction and returns a receipt. This is how transactions are sent, getting them to be included in blocks and spending gas. It is similar to [`eth_sendTransaction`](#eth_sendtransaction), except it also performs some work on the user's device, namely the production of the proof for the private part of the transaction. - -#include_code send_tx yarn-project/end-to-end/src/e2e_card_game.test.ts typescript diff --git a/docs/pages/foundational-topics/circuits.md b/docs/pages/foundational-topics/circuits.md deleted file mode 100644 index 22d290c..0000000 --- a/docs/pages/foundational-topics/circuits.md +++ /dev/null @@ -1,95 +0,0 @@ -# Circuits - -Central to Aztec's operations are 'circuits' derived both from the core protocol and the developer-written Aztec.nr contracts. - -The core circuits enhance privacy by adding additional security checks and preserving transaction details - a characteristic Ethereum lacks. - -On this page, you’ll learn a bit more about these circuits and their integral role in promoting secure and efficient transactions within Aztec's privacy-centric framework. - -## Motivation - -In Aztec, circuits come from two sources: - -1. Core protocol circuits -2. User-written circuits (written as Aztec.nr Contracts and deployed to the network) - -This page focuses on the core protocol circuits. These circuits check that the rules of the protocol are being adhered to. - -When a function in an Ethereum smart contract is executed, the EVM performs checks to ensure that Ethereum's transaction rules are being adhered-to correctly. Stuff like: - -- "Does this tx have a valid signature?" -- "Does this contract address contain deployed code?" -- "Does this function exist in the requested contract?" -- "Is this function allowed to call this function?" -- "How much gas has been paid, and how much is left?" -- "Is this contract allowed to read/update this state variable?" -- "Perform the state read / state write" -- "Execute these opcodes" - -All of these checks have a computational cost, for which users are charged gas. - -Many existing L2s move this logic offchain, as a way of saving their users gas costs, and as a way of increasing tx throughput. - -zk-Rollups, in particular, move these checks offchain by encoding them in zk-S(N/T)ARK circuits. Rather than paying a committee of Ethereum validators to perform the above kinds of checks, L2 users instead pay a sequencer to execute these checks via the circuit(s) which encode them. The sequencer can then generate a zero-knowledge proof of having executed the circuit(s) correctly, which they can send to a rollup contract on Ethereum. The Ethereum validators then verify this zk-S(N/T)ARK. It often turns out to be much cheaper for users to pay the sequencer to do this, than to execute a smart contract on Ethereum directly. - -But there's a problem. - -Ethereum (and the EVM) doesn't have a notion of privacy. - -- There is no notion of a private state variable in the EVM. -- There is no notion of a private function in the EVM. - -So users cannot keep private state variables' values private from Ethereum validators, nor from existing (non-private) L2 sequencers. Nor can users keep the details of which function they've executed private from validators or sequencers. - -How does Aztec add privacy? - -Well, we just encode _extra_ checks in our zk-Rollup's zk-SNARK circuits! These extra checks introduce the notions of private state and private functions, and enforce privacy-preserving constraints on every transaction being sent to the network. - -In other words, since neither the EVM nor other rollups have rules for how to preserve privacy, we've written a new rollup which introduces such rules, and we've written circuits to enforce those rules! - -What kind of extra rules / checks does a rollup need, to enforce notions of private states and private functions? Stuff like: - -- "Perform state reads and writes using new tree structures which prevent tx linkability" (see indexed merkle tree section). -- "Hide which function was just executed, by wrapping it in a zk-snark" -- "Hide all functions which were executed as part of this tx's stack trace, by wrapping the whole tx in a zk-snark" - -## Aztec core protocol circuits - -So what kinds of core protocol circuits does Aztec have? - -### Kernel, Rollup, and Squisher Circuits - -The specs of these have recently been updated. Eg for squisher circuits since Honk and Goblin Plonk schemes are still being improved! But we'll need some extra circuit(s) to squish a Honk proof (as produced by the Root Rollup Circuit) into a Standard Plonk or Fflonk proof, for cheap verification on Ethereum. - -## Kernel Circuits - -### Private Kernel Circuit - -This circuit is executed by the user, on their own device. This is to ensure private inputs to the circuit remain private! - -:::note - -**This is the only core protocol circuit which actually needs to be "zk" (zero-knowledge)!!!** That's because this is the only core protocol circuit which handles private data, and hence the only circuit for which proofs must not leak any information about witnesses! (The private data being handled includes: details of the Aztec.nr Contract function which has been executed; the address of the user who executed the function; the intelligible inputs and outputs of that function). - -Most so-called "zk-Rollups" do not make use of this "zero-knowledge" property. Their snarks are "snarks"; with no need for zero-knowledge, because they don't seek privacy; they only seek the 'succinct' computation-compression properties of snarks. Aztec's "zk-Rollup" actually makes use of "zero-knowledge" snarks. That's why we sometimes call it a "zk-zk-Rollup", or "_actual_ zk-Rollup". - -::: - -### Public Kernel Circuit - -This circuit is executed by a Sequencer, since only a Sequencer knows the current state of the public data tree at any time. A Sequencer might choose to delegate proof generation to the Prover pool. - -## Rollup Circuits - -The primary purpose of the Rollup Circuits is to 'squish' all of the many thousands of transactions in a rollup into a single SNARK, which can then be efficiently and verified on Ethereum. - -These circuits are executed by a Sequencer, since their primary role is to order transactions. A Sequencer might choose to delegate proof generation to the Prover pool. - -The way we 'squish' all this data is in a 'binary tree of proofs' topology. - -> Example: If there were 16 txs in a rollup, we'd arrange the 16 kernel proofs into 8 pairs and merge each pair into a single proof (using zk-snark recursion techniques), resulting in 8 output proofs. We'd then arrange those 8 proofs into pairs and again merge each pair into a single proof, resulting in 4 output proofs. And so on until we'd be left with a single proof, which represents the correctness of the original 16 txs. -> This 'binary tree of proofs' topology allows proof generation to be greatly parallelized across prover instances. Each layer of the tree can be computed in parallel. Or alternatively, subtrees can be coordinated to be computed in parallel. - -> Note: 'binary tree of proofs' is actually an oversimplification. The Rollup Circuits are designed so that a Sequencer can actually deviate from a neat, symmetrical tree, for the purposes of efficiency, and instead sometimes create wonky trees. - -Some of the Rollup Circuits also do some protocol checks and computations, for efficiency reasons. We might rearrange which circuit does what computation, as we discover opportunities for efficiency. diff --git a/docs/pages/foundational-topics/fees.md b/docs/pages/foundational-topics/fees.md index fe59782..318a070 100644 --- a/docs/pages/foundational-topics/fees.md +++ b/docs/pages/foundational-topics/fees.md @@ -1,70 +1 @@ -# Fees - -In a nutshell, the pricing of transactions transparently accounts for: - -- L1 costs, including L1 execution of a block, and data availability via blobs, -- L2 node operating costs, including proving - -This is done via multiple variables and calculations explained in detail in the protocol specifications. - -## Terminology and factors - -Familiar terms from Ethereum mainnet as referred to on the Aztec network: - -| Ethereum Mainnet | Aztec | Description | -| ---------------- | ------------------ | ---------------------------------------------- | -| gas | mana | indication of effort in transaction operations | -| fee per gas | fee-juice per mana | cost per unit of effort | -| fee (wei) | fee-juice | amount to be paid | - -An oracle informs the price of fee-juice per wei, which can be used to calculate a transaction's fee-juice in the units of wei. - -Also Aztec borrows ideas from EIP-1559 including: congestion multipliers, and the ability to specify base and priority fee per mana. - -### Aztec-specific fields - -There are many other fields used in mana and fee calculations, and below shows the ways these fields are determined: - -- hard-coded constants (eg congestion update fraction) -- values assumed constant (eg L1 gas cost of publishing a block, blobs per block) -- informed from previous block header and/or L1 rollup contract (eg base_fee_juice_per_mana) -- informed via an oracle (eg wei per mana) - -Most of the constants are defined by the protocol, several others are part of the rollup contract on L1. - -More information about the design/choices can be found in the fees section of the protocol specification. - -### User selected factors - -As part of a transaction the follow gas settings are available to be defined by the user. - -These are: - -#include_code gas_settings_vars yarn-project/stdlib/src/gas/gas_settings.ts javascript - -## Fee payment - -A fee payer will have bridged fee-juice from L1. On Aztec this fee asset is non-transferrable, and only deducted by the protocol to pay for fees. A user can claim bridged fee juice and use it to pay for transaction fees in the same transaction. - -The mechanisms for bridging is the same as any other token. For more on this concept see the Token Bridge Tutorial where it describes the components and how bridging works (under the hood this makes use of portals). - -### Payment methods - -An account with fee-juice can pay for its transactions, including deployment of a new account, if fee juice has been bridged the that address at which the account will be deployed. - -An account making a transaction can also refer to "fee-paying contracts" (FPCs) to pay for its transactions. FPCs are contracts that accept a token and pay for transactions in fee juice. This means a user doesn't need to hold fee juice, they only need the token that the FPC accepts. FPCs can contain arbitrary logic to determine how they want to authorize transaction fee payments. They can be used for paying transaction fees privately or publicly. - -### Teardown phase - -### Operator rewards - -The calculated fee-juice of a transaction is deducted from the fee payer (nominated account or fee-paying contract), then pooled together each transaction, block, and epoch. -Once the epoch is proven, the total fee-juice (minus any burnt congestion amount), is distributed to provers and block sequencers that contributed to the epoch. - -The fees section of the protocol specification explains this distribution of fee-juice between proposers and provers. - -## Next steps - -More comprehensive technical details for implementers will be available from the updated protocol specifications soon. - -For a guide showing ways to pay fees programmatically, see the Aztec.js guides section. +# Fees \ No newline at end of file diff --git a/docs/pages/foundational-topics/index.md b/docs/pages/foundational-topics/index.md index 9e08807..6542fe5 100644 --- a/docs/pages/foundational-topics/index.md +++ b/docs/pages/foundational-topics/index.md @@ -1,18 +1 @@ # Protocol Architecture - -This section covers the core architectural components of the Aztec protocol. - -## Topics Covered - -- **Addresses & Keys** - How addresses and cryptographic keys work in Aztec -- **Wallets** - Wallet architecture and account abstraction -- **Call Types** - Different types of function calls (private, public, etc.) -- **PXE & ACIR Simulator** - Private execution environment and circuit simulation -- **Transactions** - Transaction structure and lifecycle -- **Fees** - Fee mechanism and payment -- **Nullifier Merkle Tree** - Nullifier tree structure and operations -- **Note Discovery** - How users discover their private notes -- **Circuits** - Overview of the circuit architecture -- **Storage Slots** - How storage is organized in contracts - -Navigate through the sections in the sidebar to learn more about each component. diff --git a/docs/pages/foundational-topics/note-discovery.md b/docs/pages/foundational-topics/note-discovery.md deleted file mode 100644 index 15f973f..0000000 --- a/docs/pages/foundational-topics/note-discovery.md +++ /dev/null @@ -1,42 +0,0 @@ -# Note Discovery - - -Note discovery refers to the process of a user identifying and decrypting the encrypted notes that belong to them. - -## Existing solutions - -### Brute force / trial-decrypt - -In some existing protocols, the user downloads all possible notes and tries to decrypt each one. If the decryption succeeds, the user knows they own that note. However, this approach becomes exponentially more expensive as the network grows and more notes are created. It also introduces a third-party server to gather and trial-decrypt notes, which is an additional point of failure. Note that this note discovery technique is not currently implemented for Aztec. - -### Offchain communication - -Another proposed solution is having the sender give the note content to the recipient via some offchain communication. While it solves the brute force issue, it introduces reliance on side channels which we don't want in a self-sufficient network. This option incurs lower transaction costs because fewer logs needs to be posted onchain. Aztec apps will be able to choose this method if they wish. - -## Aztec's solution: Note tagging - -Aztec introduces an approach that allows users to identify which notes are relevant to them by having the sender _tag_ the log in which the note is created. This is known as note tagging. The tag is generated in such a way that only the sender and recipient can identify it. - -### How it works - -#### Every log has a tag - -In Aztec, each emitted log is an array of fields, eg `[x, y, z]`. The first field (`x`) is a _tag_ field used to index and identify logs. The Aztec node exposes an API `getLogsByTags()` that can retrieve logs matching specific tags. - -#### Tag generation - -The sender and recipient share a predictable scheme for generating tags. The tag is derived from a shared secret and an index (a shared counter that increments each time the sender creates a note for the recipient). - -#### Discovering notes in Aztec contracts - -This note discovery scheme will be implemented by Aztec contracts rather than by the PXE. This means that users can update or use other types of note discovery to suit their needs. - -### Limitations - -- **You cannot receive notes from an unknown sender**. If you do not know the sender’s address, you cannot create the shared secret, and therefore cannot create the note tag. There are potential ways around this, such as senders adding themselves to a contract and then recipients searching for note tags from all the senders in the contract. However this is out of scope at this point in time. - -- **Index synchronization can be complicated**. If transactions are reverted or mined out of order, the recipient may stop searching after receiving a tag with the latest index they expect. This means they can miss notes that belong to them. We cannot redo a reverted a transaction with the same index, because then the tag will be the same and the notes will be linked, leaking privacy. We can solve this by widening the search window (not too much, or it becomes brute force) and implementing a few restrictions on sending notes. A user will not be able to send a large amount of notes from the same contract to the same recipient within a short time frame. - -## Further reading - -- How partial notes are discovered diff --git a/docs/pages/foundational-topics/nullifier-merkle-tree.md b/docs/pages/foundational-topics/nullifier-merkle-tree.md index e69de29..6976847 100644 --- a/docs/pages/foundational-topics/nullifier-merkle-tree.md +++ b/docs/pages/foundational-topics/nullifier-merkle-tree.md @@ -0,0 +1,3 @@ +## Nullifier Merkle Tree + +We will use the guide that's already there and put it in an advanced section of the resources because its not super necessary to know unless you are an auditor for Aztec lol \ No newline at end of file diff --git a/docs/pages/foundational-topics/overview.md b/docs/pages/foundational-topics/overview.md new file mode 100644 index 0000000..476060d --- /dev/null +++ b/docs/pages/foundational-topics/overview.md @@ -0,0 +1,2 @@ +# Overview + diff --git a/docs/pages/foundational-topics/pxe-and-acir-simulator.md b/docs/pages/foundational-topics/pxe-and-acir-simulator.md deleted file mode 100644 index e69de29..0000000 diff --git a/docs/pages/foundational-topics/state-management.md b/docs/pages/foundational-topics/state-management.md new file mode 100644 index 0000000..a0388b2 --- /dev/null +++ b/docs/pages/foundational-topics/state-management.md @@ -0,0 +1,5 @@ +# State Management + +## Ciara's notes + +- Execution envs, state trees, UTXO notes, nullifiers, etc. PLEASE SHOW ME WHAT A NOTE STRUCT LOOKS LIKE COS THAT WAS HELLA CONFUSING WITHOUT SEEING IT \ No newline at end of file diff --git a/docs/pages/foundational-topics/storage-slots.md b/docs/pages/foundational-topics/storage-slots.md deleted file mode 100644 index 665f026..0000000 --- a/docs/pages/foundational-topics/storage-slots.md +++ /dev/null @@ -1,90 +0,0 @@ -# Storage Slots - -## Public State Slots - -As mentioned in the State Model section, Aztec public state behaves similarly to public state on Ethereum from the point of view of the developer. Behind the scenes however, the storage is managed differently. As mentioned, public state has just one large sparse tree in Aztec - so we silo slots of public data by hashing it together with its contract address. - -The mental model is that we have a key-value store, where the siloed slot is the key, and the value is the data stored in that slot. You can think of the `real_storage_slot` identifying its position in the tree, and the `logical_storage_slot` identifying the position in the contract storage. - -```rust -real_storage_slot = H(contract_address, logical_storage_slot) -``` - -The siloing is performed by the Kernel circuits. - -For structs and arrays, we are logically using a similar storage slot computation to ethereum, e.g., as a struct with 3 fields would be stored in 3 consecutive slots. However, because the "actual" storage slot is computed as a hash of the contract address and the logical storage slot, the actual storage slot is not consecutive. - -## Private State Slots - -Private storage is a different beast. As you might remember from the Hybrid State Model, private state is stored in encrypted logs and the corresponding private state commitments in append-only tree, called the note hash tree where each leaf is a commitment. Append-only means that leaves are never updated or deleted; instead a nullifier is emitted to signify that some note is no longer valid. A major reason we used this tree, is that updates at a specific storage slot would leak information in the context of private state, even if the value is encrypted. That is not good privacy. - -Following this, the storage slot as we know it doesn't really exist. The leaves of the note hashes tree are just commitments to content (think of it as a hash of its content). - -Nevertheless, the concept of a storage slot is very useful when writing applications, since it allows us to reason about distinct and disjoint pieces of data. For example we can say that the balance of an account is stored in a specific slot and that the balance of another account is stored in another slot with the total supply stored in some third slot. By making sure that these slots are disjoint, we can be sure that the balances are not mixed up and that someone cannot use the total supply as their balance. - -### Implementation - -If we include the storage slot, as part of the note whose commitment is stored in the note hashes tree, we can _logically link_ all the notes that make up the storage slot. For the case of a balance, we can say that the balance is the sum of all the notes that have the same storage slot - in the same way that your physical wallet balance is the sum of all the physical notes in your wallet. - -Similarly to how we siloed the public storage slots, we can silo our private storage by hashing the packed note together with the logical storage slot. - -```rust -note_hash = H([...packed_note, logical_storage_slot]); -``` - -Note hash siloing is done in the application circuit, since it is not necessary for security of the network (but only the application). -:::info -The private variable wrappers `PrivateSet` and `PrivateMutable` in Aztec.nr include the `logical_storage_slot` in the commitments they compute, to make it easier for developers to write contracts without having to think about how to correctly handle storage slots. -::: - -When reading the values for these notes, the application circuit can then constrain the values to only read notes with a specific logical storage slot. - -To ensure that contracts can only modify their own logical storage, we do a second siloing by hashing the `commitment` with the contract address. - -```rust -siloed_note_hash = H(contract_address, note_hash); -``` - -By doing this address-siloing at the kernel circuit we _force_ the inserted commitments to include and not lie about the `contract_address`. - -:::info -To ensure that nullifiers don't collide across contracts we also force this contract siloing at the kernel level. -::: - -## Example - -In this section we will go into more detail and walk through an entire example of how storage slots are computed for private state to improve our storage slot intuition. Recall, that storage slots in the private domain is just a logical construct, and are not "actually" used for lookups, but rather just as a value to constrain against. - -For the case of the example, we will look at what is inserted into the note hashes tree when adding a note in the Token contract. Specifically, we are looking at the last part of the `transfer` function: - -#include_code increase_private_balance noir-projects/noir-contracts/contracts/app/token_contract/src/main.nr rust - -This function is creating a new note and inserting it into the balance set of the recipient `to`. Recall that to ensure privacy, only the note hash is really inserted into the note hashes tree. To share the contents of the note with `to` the contract can emit an encrypted log (which this one does), or it can require an offchain data transfer sharing the information. Below, we will walk through the steps of how the note hash is computed and inserted into the tree. For this, we don't care about the encrypted log, so we are going to ignore that part of the function call for now. - -Outlining it in more detail below as a sequence diagram, we can see how the calls make their way down the stack. -In the end a siloed note hash is computed in the kernel. - -:::info -Some of the syntax below is a little butchered to make it easier to follow variables without the full code. -::: - -Notice the `siloed_note_hash` at the very end. It's a hash that will be inserted into the note hashes tree. To clarify what this really is, we "unroll" the values to their simplest components. This gives us a better idea around what is actually inserted into the tree. - -```rust -siloed_note_hash = H(contract_address, unique_note_hash) -siloed_note_hash = H(contract_address, H(nonce, note_hash)) -siloed_note_hash = H(contract_address, H(H(tx_hash, note_index_in_tx), note_hash)) -siloed_note_hash = H(contract_address, H(H(tx_hash, note_index_in_tx), MSM([G_amt, G_to, G_rand, G_slot], [amount, to, randomness, derived_slot]).x)) -``` - -MSM is a multi scalar multiplication on a grumpkin curve and G\_\* values are generators. - -And `to` is the actor who receives the note, `amount` of the note and `randomness` is the randomness used to make the note hiding. Without the `randomness` the note could just as well be plaintext (computational cost of a preimage attack would be trivial in such a case). - -:::info -Beware that this hash computation is what the aztec.nr library is doing, and not strictly required by the network (only the kernel computation is). -::: - -With this note structure, the contract can require that only notes sitting at specific storage slots can be used by specific operations, e.g., if transferring funds from `from` to `to`, the notes to destroy should be linked to `H(map_slot, from)` and the new notes (except the change-note) should be linked to `H(map_slot, to)`. - -That way, we can have logical storage slots, without them really existing. This means that knowing the storage slot for a note is not enough to actually figure out what is in there (whereas it would be for looking up public state). diff --git a/docs/pages/foundational-topics/transactions.md b/docs/pages/foundational-topics/transactions.md index 53d4ec7..c7020de 100644 --- a/docs/pages/foundational-topics/transactions.md +++ b/docs/pages/foundational-topics/transactions.md @@ -1,110 +1,5 @@ -On this page you'll learn: +# Transactions -- The step-by-step process of sending a transaction on Aztec -- The role of components like PXE, Aztec Node, ACIR simulator, and the sequencer -- The Aztec Kernel and its two circuits: private and public, and how they execute function calls -- The call stacks for private & public functions and how they determine a transaction's completion +## Ciara's notes -## Simple Example of the (Private) Transaction Lifecycle - -The transaction lifecycle for an Aztec transaction is fundamentally different from the lifecycle of an Ethereum transaction. - -The introduction of the Private eXecution Environment (PXE) provides a safe environment for the execution of sensitive operations, ensuring that decrypted data are not accessible to unauthorized applications. However, the PXE exists client-side on user devices, which creates a different model for imagining what the lifecycle of a typical transaction might look like. The existence of a sequencing network also introduces some key differences between the Aztec transaction model and the transaction model used for other networks. - -The accompanying diagram illustrates the flow of interactions between a user, their wallet, the PXE, the node operators (sequencers / provers), and the L1 chain. - -![transaction-lifecycle](/transaction-lifecycle.png) - -1. **The user initiates a transaction** – In this example, the user decides to privately send 10 DAI to gudcause.eth. After inputting the amount and the receiving address, the user clicks the confirmation button on their wallet. - -_The transaction has not been broadcasted to the sequencer network yet. For now, the transaction exists solely within the context of the PXE._ - -2. **The PXE executes transfer locally** – The PXE, running locally on the user's device, executes the transfer method on the DAI token contract on Aztec and computes the state difference based on the user’s intention. - -_The transaction has still not been broadcasted to the sequencer network yet and continues to live solely within the context of the PXE._ - -3. **The PXE proves correct execution** – At this point, the PXE proves correct execution (via zero-knowledge proofs) of the authorization and of the private transfer method. Once the proofs have been generated, the PXE sends the proofs and required inputs (inputs are new note commitments, stored in the note hash tree and nullifiers stored in the nullifiers tree) to the sequencer. Nullifiers are data that invalidate used note commitments, ensuring that they can only be used once. - -_The sequencer has received the transaction proof and can begin to process the transaction - verifying proofs and applying updates to the relevant data trees - alongside other public and private transactions._ - -4. **The sequencer has the necessary information to act** – the randomly-selected sequencer (based on the Fernet sequencer selection protocol) validates the transaction proofs along with required inputs (e.g. the note commitments and nullifiers) for this private transfer. The sequencer also executes public functions and requests proofs of public execution from a prover network. The sequencer updates the corresponding data trees and does the same for other private transactions. When the sequencer receives proofs from the prover network, the proofs will be bundled into a final rollup proof. - -_The sequencer has passed the transaction information – proofs of correct execution and authorization, or public function execution information – to the prover, who will submit the new state root to Ethereum._ - -5. **The transaction settles to L1** – the verifier contract on Ethereum can now validate the rollup proof and record a new state root. The state root is submitted to the rollup smart contract. Once the state root is verified in an Ethereum transaction, the private transfer has settled and the transaction is considered final. - -### Detailed Diagram - -Transactions on Aztec start with a call from Aztec.js, which creates a request containing transaction details. This request moves to the Private Execution Environment (PXE) which simulates and processes it. Then the PXE interacts with the Aztec Node which uses the sequencer to ensure that all the transaction details are enqueued properly. The sequencer then submits the block to the rollup contract, and the transaction is successfully mined. - -![sandbox-sending-a-tx](/sandbox_sending_a_tx.png) - -See [this diagram](https://raw.githubusercontent.com/AztecProtocol/aztec-packages/2fa143e4d88b3089ebbe2a9e53645edf66157dc8/docs/static/img/sandbox_sending_a_tx.svg) for a more detailed overview of the transaction execution process. It highlights 3 different types of transaction execution: contract deployments, private transactions and public transactions. - -See the page on contract communication for more context on transaction execution. - -### Transaction Requests - -Transaction requests are how transactions are constructed and sent to the network. - -In Aztec.js: - -#include_code constructor yarn-project/stdlib/src/tx/tx_request.ts javascript - -Where: - -- `origin` is the account contract where the transaction is initiated from. -- `functionData` contains the function selector and indicates whether the function is private or public. -- `argsHash` is the hash of the arguments of all of the calls to be executed. The complete set of arguments is passed to the PXE as part of the [TxExecutionRequest](https://github.com/AztecProtocol/aztec-packages/blob/#include_aztec_version/yarn-project/stdlib/src/tx/tx_execution_request.ts) and checked against this hash. -- `txContext` contains the chain id, version, and gas settings. - -The `functionData` includes an `AppPayload`, which includes information about the application functions and arguments, and a `FeePayload`, which includes info about how to pay for the transaction. - -An account contract validates that the transaction request has been authorized via its specified authorization mechanism, via the `is_valid_impl` function (e.g. [an ECDSA signature](https://github.com/AztecProtocol/aztec-packages/blob/#include_aztec_version/noir-projects/noir-contracts/contracts/account/ecdsa_k_account_contract/src/main.nr#L56-L57), generated [in JS](https://github.com/AztecProtocol/aztec-packages/blob/#include_aztec_version/yarn-project/accounts/src/ecdsa/ecdsa_k/account_contract.ts#L30)). - -Transaction requests are simulated in the PXE in order to generate the necessary inputs for generating proofs. Once transactions are proven, a [transaction object](https://github.com/AztecProtocol/aztec-packages/blob/#include_aztec_version/yarn-project/stdlib/src/tx/tx.ts#L26) is created and can be sent to the network to be included in a block. - -#### Contract Interaction Methods - -Most transaction requests are created as interactions with specific contracts. The exception is transactions that deploy contracts. Here are the main methods for interacting with contracts related to transactions. - -1. [`simulate`](#simulate) -2. [`prove`](#prove) -3. [`send`](#send) - -##### `simulate` - -#include_code simulate yarn-project/aztec.js/src/contract/contract_function_interaction.ts javascript - -##### `prove` - -#include_code prove yarn-project/aztec.js/src/contract/base_contract_interaction.ts javascript - -##### `send` - -#include_code send yarn-project/aztec.js/src/contract/base_contract_interaction.ts javascript - -### Batch Transactions - -Batched transactions are a way to send multiple transactions in a single call. They are created by the [`BatchCall` class in Aztec.js](https://github.com/AztecProtocol/aztec-packages/blob/#include_aztec_version/yarn-project/aztec.js/src/contract/batch_call.ts). This allows a batch of function calls from a single wallet to be sent as a single transaction through a wallet. - -### Enabling Transaction Semantics - -There are two kernel circuits in Aztec, the private kernel and the public kernel. Each circuit validates the correct execution of a particular function call. - -A transaction is built up by generating proofs for multiple recursive iterations of kernel circuits. Each call in the call stack is modeled as new iteration of the kernel circuit and are managed by a [FIFO](https://en.wikipedia.org/wiki/FIFO_(computing_and_electronics)) queue containing pending function calls. There are two call stacks, one for private calls and one for public calls. - -One iteration of a kernel circuit will pop a call off of the stack and execute the call. If the call triggers subsequent contract calls, these are pushed onto the stack. - -Private kernel proofs are generated first. The transaction is ready to move to the next phase when the private call stack is empty. - -The public kernel circuit takes in proof of a public/private kernel circuit with an empty private call stack, and operates recursively until the public call stack is also empty. - -A transaction is considered complete when both call stacks are empty. - -The only information leaked about the transaction is: - -1. The number of private state updates triggered -2. The set of public calls generated - -The addresses of all private calls are hidden from observers. +- Tx lifsecycle including execution envs, the different circuits and where they fit in the puzzle \ No newline at end of file diff --git a/docs/pages/foundational-topics/wallets.md b/docs/pages/foundational-topics/wallets.md index 9943d4a..966e9d6 100644 --- a/docs/pages/foundational-topics/wallets.md +++ b/docs/pages/foundational-topics/wallets.md @@ -1,89 +1 @@ -# Wallets - -On this page we will cover the main responsibilities of a wallet in the Aztec network. - -See the wallet architecture section for an overview of its architecture and a reference on the interface a wallet must implement. - -Wallets are the applications through which users manage their accounts. Users rely on wallets to browse through their accounts, monitor their balances, and create new accounts. Wallets also store seed phrases and private keys, or interact with external keystores such as hardware wallets. - -Wallets also provide an interface for dapps. Dapps may request access to see the user accounts, in order to show the state of those accounts in the context of the application, and request to send transactions from those accounts as the user interacts with the dapp. - -In addition to these usual responsibilities, wallets in Aztec also need to track private state. This implies keeping a local database of all private notes encrypted for any of the user's accounts, so dapps and contracts can query the user's private state. Aztec wallets are also responsible for producing local proofs of execution for private functions. - -## Account setup - -The first step for any wallet is to let the user set up their accounts. An account in Aztec is represented onchain by its corresponding account contract that the user must deploy to begin interacting with the network. This account contract dictates how transactions are authenticated and executed. - -A wallet must support at least one specific account contract implementation, which means being able to deploy such a contract, as well as interacting with it when sending transactions. Code-wise, this requires [implementing the `AccountContract` interface](https://github.com/AztecProtocol/aztec-packages/blob/#include_aztec_version/yarn-project/aztec.js/src/account/interface.ts). - -Note that users must be able to receive funds in Aztec before deploying their account. A wallet should let a user generate a deterministic complete address without having to interact with the network, so they can share it with others to receive funds. This requires that the wallet pins a specific contract implementation, its initialization arguments, a deployment salt, and a privacy key. These values yield a deterministic address, so when the account contract is actually deployed, it is available at the precalculated address. Once the account contract is deployed, the user can start sending transactions using it as the transaction origin. - -## Transaction lifecycle - -Every transaction in Aztec is broadcast to the network as a zero-knowledge proof of correct execution, in order to preserve privacy. This means that transaction proofs are generated on the wallet and not on a remote node. This is one of the biggest differences with regard to EVM chain wallets. - -A wallet is responsible for **creating** an _execution request_ out of one or more _function calls_ requested by a dapp. For example, a dapp may request a wallet to "invoke the `transfer` function on the contract at `0x1234` with the following arguments", in response to a user action. The wallet turns that into an execution request with the signed instructions to execute that function call from the user's account contract. In an [ECDSA-based account](https://github.com/AztecProtocol/aztec-packages/blob/master/noir-projects/noir-contracts/contracts/account/ecdsa_k_account_contract/src/main.nr), for instance, this is an execution request that encodes the function call in the _entrypoint payload_, and includes its ECDSA signature with the account's signing private key. - -Once the _execution request_ is created, the wallet is responsible for **simulating** and **proving** the execution of its private functions. The simulation yields an execution trace, which can be used to provide the user with a list of side effects of the private execution of the transaction. During this simulation, the wallet is responsible of providing data to the virtual machine, such as private notes, encryption keys, or nullifier secrets. This execution trace is fed into the prover, which returns a zero-knowledge proof that guarantees correct execution and hides all private information. The output of this process is a _transaction object_. - -:::info -Since private functions rely on a UTXO model, the private execution trace of a transaction is determined exclusively by the notes used as inputs. Since these notes are immutable, the trace of a transaction is always the same, so any effects observed during simulation will be exactly the same when the transaction is mined. However, the transaction may be dropped if it attempts to consume a private note that another transaction nullified before it gets mined. Note that this applies only to private function execution. Public functions rely on an account model, similar to Ethereum, so their execution trace depends on the chain's public state at the point they are included in a block, which may have changed since the transaction was simulated locally. -::: - -Finally, the wallet **sends** the resulting _transaction_ object, which includes the proof of execution, to an Aztec Node. The transaction is then broadcasted through the peer-to-peer network, to be eventually picked up by a sequencer and included in a block. - -:::warning -There are no proofs generated as of the Sandbox release. This will be included in a future release before testnet. -::: - -## Authorizing actions - -Account contracts in Aztec expose an interface for other contracts to validate whether an action is authorized by the account or not. For example, an application contract may want to transfer tokens on behalf of a user, in which case the token contract will check with the account contract whether the application is authorized to do so. These actions may be carried out in private or in public functions, and in transactions originated by the user or by someone else. - -Wallets should manage these authorizations, prompting the user when they are requested by an application. Authorizations in private executions come in the form of _auth witnesses_, which are usually signatures over an identifier for an action. Applications can request the wallet to produce an auth witness via the `createAuthWit` call. In public functions, authorizations are pre-stored in the account contract storage, which is handled by a call to an internal function in the account contract implementation. - -## Key management - -As in EVM-based chains, wallets are expected to manage user keys, or provide an interface to hardware wallets or alternative key stores. Keep in mind that in Aztec each account requires two sets of keys: privacy keys and authentication keys. Privacy keys are mandated by the protocol and used for encryption and nullification, whereas authentication keys are dependent on the account contract implementation rolled out by the wallet. Should the account contract support it, wallets must provide the user with the means to rotate or recover their authentication keys. - -:::info -Due to limitations in the current architecture, privacy keys need to be available in the wallet software itself and cannot be punted to an external keystore. This restriction may be lifted in a future release. -::: - -## Recipient encryption keys - -Wallets are also expected to manage the public encryption keys of any recipients of local transactions. When creating an encrypted note for a recipient given their address, the wallet needs to provide their complete address. Recipients broadcast their complete addresses when deploying their account contracts, and wallets collect this information and save it in a local registry for easy access when needed. - -Note that, in order to interact with a recipient who has not yet deployed their account contract (and thus not broadcasted their complete address), it must also be possible to manually add an entry to a wallet's local registry of complete addresses. - -## Private state - -Last but not least, wallets also store the user's private state. Wallets currently rely on brute force decryption, where every new block is downloaded and its encrypted data blobs are attempted to be decrypted with the user decryption keys. Whenever a blob is decrypted properly, it is added to the corresponding account's private state. Note that wallets must also scan for private state in blocks prior to the deployment of a user's account contract, since users may have received private state before deployment. - -:::info -At the time of this writing, all private state is encrypted and broadcasted through the network, and eventually committed to L1. This means that a wallet can reconstruct its entire private state out of its encryption keys in the event of local data loss. -::: - -Encrypted data blobs do not carry any public information as to whom their recipient is. Therefore, it is not possible for a remote node to identify the notes that belong to a user, and it is not possible for a wallet to query a remote node for its private state. As such, wallets need to keep a local database of their accounts private state, in order to be able to answer any queries on their private state. - -Dapps may require access to the user's private state, in order to show information relevant to the current application. For instance, a dapp for a token may require access to the user's private notes in the token contract in order to display the user's balance. It is responsibility of the wallet to require authorization from the user before disclosing private state to a dapp. - -## Wallet Architecture - -This page talks about the architecture of a wallet in Aztec. Wallets expose to dapps an interface that allows them to act on behalf of the user, such as querying private state or sending transactions. Bear in mind that, as in Ethereum, wallets should require user confirmation whenever carrying out a potentially sensitive action requested by a dapp. - -## Overview - -Architecture-wise, a wallet is an instance of an **Private Execution Environment (PXE)** which manages user keys and private state. -The PXE also communicates with an **Aztec Node** for retrieving public information or broadcasting transactions. -Note that the PXE requires a local database for keeping private state, and is also expected to be continuously syncing new blocks for trial-decryption of user notes. - -Additionally, a wallet must be able to handle one or more account contract implementation. When a user creates a new account, the account is represented onchain by an account contract. The wallet is responsible for deploying and interacting with this contract. A wallet may support multiple flavours of accounts, such as an account that uses ECDSA signatures, or one that relies on WebAuthn, or one that requires multi-factor authentication. For a user, the choice of what account implementation to use is then determined by the wallet they interact with. - -In code, this translates to a wallet implementing an **AccountInterface** interface that defines how to create an _execution request_ out of an array of _function calls_ for the specific implementation of an account contract and how to generate an _auth witness_ for authorizing actions on behalf of the user. Think of this interface as the Javascript counterpart of an account contract, or the piece of code that knows how to format a transaction and authenticate an action based on the rules defined by the user's account contract implementation. - -## Account interface - -The account interface is used for creating an _execution request_ out of one or more _function calls_ requested by a dapp, as well as creating an _auth witness_ for a given message hash. Account contracts are expected to handle multiple function calls per transaction, since dapps may choose to batch multiple actions into a single request to the wallet. - -#include_code account-interface yarn-project/aztec.js/src/account/interface.ts typescript +# Wallets \ No newline at end of file diff --git a/docs/pages/getting-started.mdx b/docs/pages/getting-started.mdx index ca1a48a..35059bd 100644 --- a/docs/pages/getting-started.mdx +++ b/docs/pages/getting-started.mdx @@ -182,7 +182,7 @@ This should print `Simulation result: 25n` Want to build something cool on Aztec? -- Check out the [Token Contract Tutorial](./guides/smart-contracts/token-contract) for a beginner tutorial +- Check out the [Token Contract Tutorial](./tutorials/private-token-smart-contract) for a beginner tutorial - Start on your own thing and check out the How To Guides to help you! @@ -395,7 +395,7 @@ const nodeUrl = process.env.NODE_URL || "http://localhost:8080"; - **New to Aztec?** Start with the sandbox tab for faster development - **Ready for production testing?** Continue using testnet -- **Learn more:** Check out our [tutorials](./guides/smart-contracts/counter-contract) +- **Learn more:** Check out our [tutorials](./tutorials/counter-smart-contract) - **Explore:** Visit [Aztec Playground](https://play.aztec.network/) ## Additional Resources diff --git a/docs/pages/glossary.md b/docs/pages/glossary.md index e69de29..ad418c9 100644 --- a/docs/pages/glossary.md +++ b/docs/pages/glossary.md @@ -0,0 +1,3 @@ +# Glossary + +Port existing one \ No newline at end of file diff --git a/docs/pages/guides/smart-contracts/counter-contract.md b/docs/pages/guides/smart-contracts/counter-contract.md deleted file mode 100644 index be86709..0000000 --- a/docs/pages/guides/smart-contracts/counter-contract.md +++ /dev/null @@ -1,2 +0,0 @@ -# Write a Counter Smart Contract - diff --git a/docs/pages/guides/smart-contracts/private-payments.md b/docs/pages/guides/smart-contracts/private-payments.md deleted file mode 100644 index e69de29..0000000 diff --git a/docs/pages/guides/smart-contracts/token-contract.md b/docs/pages/guides/smart-contracts/token-contract.md deleted file mode 100644 index e69de29..0000000 diff --git a/docs/pages/index.md b/docs/pages/index.md new file mode 100644 index 0000000..a460fde --- /dev/null +++ b/docs/pages/index.md @@ -0,0 +1,3 @@ +# Aztec Overview + +Overview of aztec obvs but also like where to go in the docs - are you a FE dev? a smart contract dev? Node op? \ No newline at end of file diff --git a/docs/pages/index.mdx b/docs/pages/index.mdx deleted file mode 100644 index d36b4f5..0000000 --- a/docs/pages/index.mdx +++ /dev/null @@ -1,146 +0,0 @@ -import { CardContainer, Card, CardHeader, CardBody } from '../../components/Card' - -# Aztec Overview - -Aztec is a privacy-first L2 rollup scaling solution on Ethereum. - -![Aztec Overview](../public/Aztec_overview.png) - -## High level view - -![High level](../public/aztec-high-level.png) - -1. A user interacts with Aztec through Aztec.js (like web3js or ethersjs) -2. Private functions are executed in the PXE, which is client-side -3. Proofs and tree updates are sent to the Public VM (running on an Aztec node) -4. Public functions are executed in the Public VM -5. The Public VM rolls up the transactions that include private and public state updates into blocks -6. The block data and proof of a correct state transition are submitted to Ethereum for verification - -## Private and public execution - -Private functions are executed client side, on user devices to maintain maximum privacy. Public functions are executed by a remote network of nodes, similar to other blockchains. These distinct execution environments create a directional execution flow for a single transaction--a transaction begins in the private context on the user's device then moves to the public network. This means that private functions executed by a transaction can enqueue public functions to be executed later in the transaction life cycle, but public functions cannot call private functions. - -### Private Execution Environment (PXE) - -Private functions are executed on the user's device in the Private Execution Environment (PXE, pronounced 'pixie'), then it generates proofs for onchain verification. It is a client-side library for execution and proof-generation of private operations. It holds keys, notes, and generates proofs. It is included in aztec.js, a TypeScript library, and can be run within Node or the browser. - -Note: It is easy for private functions to be written in a detrimentally unoptimized way, because many intuitions of regular program execution do not apply to proving. For more about writing performant private functions in Noir, see [this page](https://noir-lang.org/docs/explainers/explainer-writing-noir) of the Noir documentation. - -### Aztec Virtual Machine (AVM) - -Public functions are executed by the Aztec Virtual Machine (AVM), which is conceptually similar to the Ethereum Virtual Machine (EVM). As such, writing efficient public functions follow the same intuition as gas-efficient solidity contracts. - -The PXE is unaware of the Public VM. And the Public VM is unaware of the PXE. They are completely separate execution environments. This means: - -- The PXE and the Public VM cannot directly communicate with each other -- Private transactions in the PXE are executed first, followed by public transactions - -## Private and public state - -Private state works with UTXOs, which are chunks of data that we call notes. To keep things private, notes are stored in an append-only UTXO tree, and a nullifier is created when notes are invalidated (aka deleted). Nullifiers are stored in their own nullifier tree. - -Public state works similarly to other chains like Ethereum, behaving like a public ledger. Public data is stored in a public data tree. - -![Public vs private state](/public-and-private-state-diagram.png) - -Aztec smart contract developers should keep in mind that different data types are used when manipulating private or public state. Working with private state is creating commitments and nullifiers to state, whereas working with public state is directly updating state. - -## Accounts and keys - -### Account abstraction - -Every account in Aztec is a smart contract (account abstraction). This allows implementing different schemes for authorizing transactions, nonce management, and fee payments. - -Developers can write their own account contract to define the rules by which user transactions are authorized and paid for, as well as how user keys are managed. - -Learn more about account contracts in the documentation. - -### Key pairs - -Each account in Aztec is backed by 3 key pairs: - -- A **nullifier key pair** used for note nullifier computation -- A **incoming viewing key pair** used to encrypt a note for the recipient -- A **outgoing viewing key pair** used to encrypt a note for the sender - -As Aztec has native account abstraction, accounts do not automatically have a signing key pair to authenticate transactions. This is up to the account contract developer to implement. - -## Noir - -Noir is a zero-knowledge domain specific language used for writing smart contracts for the Aztec network. It is also possible to write circuits with Noir that can be verified on or offchain. For more in-depth docs into the features of Noir, go to the [Noir documentation](https://noir-lang.org/). - -## What's next? - -### Start coding - -
- - -

Developer Getting Started Guide

-
- - Follow the getting started guide to start developing with the Aztec Sandbox - -
-
- -### Dive deeper into how Aztec works - -Explore the Concepts for a deeper understanding into the components that make up Aztec: - - - - -

Accounts

-
- - Learn about Aztec's native account abstraction - every account in Aztec is a smart contract which defines the rules for whether a transaction is or is not valid - -
- - - -

PXE (pronounced 'pixie')

-
- - The Private Execution Environment (or PXE) is a client-side library for the execution of private operations - -
- - - -

State model

-
- - Aztec has a hybrid public/private state model - -
- - - -

Storage

-
- - In Aztec, private data and public data are stored in two trees: a public data tree and a note hashes tree - -
- - - -

Wallets

-
- - Wallets expose to dapps an interface that allows them to act on behalf of the user, such as querying private state or sending transactions - -
- - - -

Protocol Circuits

-
- - Central to Aztec's operations are circuits in the core protocol and the developer-written Aztec.nr contracts - -
-
\ No newline at end of file diff --git a/docs/pages/navigation.md b/docs/pages/navigation.md deleted file mode 100644 index a116925..0000000 --- a/docs/pages/navigation.md +++ /dev/null @@ -1,38 +0,0 @@ -# Navigating the Documentation - -These docs are targeted at **Solidity developers** who want to build privacy applications. Since Aztec smart contracts are written in Noir, those familiar with Rust will find the developer experience to be similar since Noir is Rust-based. - -The differences between Ethereum development and Aztec development are detailed in these docs but the high-level differences that will need to be understood are: -1. How public and private state is managed. -2. How to write Aztec smart contracts with Noir and Aztec.nr. - -### Getting started on Sandbox or Testnet - -Get started developing on Aztec by running the sandbox or interacting with testnet. - -The differences between sandbox and testnet: - -**Sandbox (Local Development)** -- Runs locally on your machine -- No proving by default (faster development) -- No fees -- Instant block times -- Test accounts automatically deployed -- Ideal for rapid development and testing - -**Testnet (Remote Network)** -- Remote environment with network of sequencers -- Always has proving enabled (longer transaction times) -- Always has fees enabled (need to pay or sponsor fees) -- ~36 second block times, longer L1 settlement -- No automatic test accounts -- Ideal for production-like testing - -### Concepts - -### Guides - -- Smart contracts - - Write a counter smart contract - - Write a token smart contract - - Write a contract with partial notes \ No newline at end of file diff --git a/docs/pages/tutorials/counter-smart-contract.md b/docs/pages/tutorials/counter-smart-contract.md new file mode 100644 index 0000000..56636fe --- /dev/null +++ b/docs/pages/tutorials/counter-smart-contract.md @@ -0,0 +1,2 @@ +# Counter Smart Contract + diff --git a/docs/pages/tutorials/private-token-smart-contract.md b/docs/pages/tutorials/private-token-smart-contract.md new file mode 100644 index 0000000..5c7789d --- /dev/null +++ b/docs/pages/tutorials/private-token-smart-contract.md @@ -0,0 +1,2 @@ +# Private Token Smart Contract + diff --git a/docs/pages/tutorials/token-bridge.md b/docs/pages/tutorials/token-bridge.md new file mode 100644 index 0000000..58556a9 --- /dev/null +++ b/docs/pages/tutorials/token-bridge.md @@ -0,0 +1,2 @@ +# Token Bridge + diff --git a/docs/pages/wallet-cli/overview.md b/docs/pages/wallet-cli/overview.md index 71f5ef2..7ab2c12 100644 --- a/docs/pages/wallet-cli/overview.md +++ b/docs/pages/wallet-cli/overview.md @@ -1,11 +1,5 @@ # Wallet CLI Overview -For development, it may be useful to deploy, transact, or create notes in a non-programmatic way. The wallet CLI (`aztec-wallet`) can be used for the following: +## Ciara's notes -- Deploying contracts -- Sending transactions -- Bridging L1 "Fee Juice" into Aztec -- Pushing arbitrary notes to your PXE -- Creating authwits -- Aliasing info and secrets for further usage -- Proving your transactions and profile gate counts \ No newline at end of file +wtf is the cli wallet, why do i need it, when will i use it, what can i do? \ No newline at end of file diff --git a/docs/pages/wallet-cli/reference.md b/docs/pages/wallet-cli/reference.md index c2e9761..0ab9e9b 100644 --- a/docs/pages/wallet-cli/reference.md +++ b/docs/pages/wallet-cli/reference.md @@ -1 +1,5 @@ -# Wallet CLI Reference \ No newline at end of file +# Wallet CLI Reference + +## Ciara's notes + +ALL THE COMMANDS \ No newline at end of file diff --git a/vocs.config.ts b/vocs.config.ts index 09ad047..a8b9f22 100644 --- a/vocs.config.ts +++ b/vocs.config.ts @@ -34,7 +34,7 @@ export default defineConfig({ }, { text: '3. Aztec vs Ethereum', - link: '/aztec-nr/aztec-vs-ethereum', + link: '/aztec-vs-ethereum', }, { text: '4. Foundational Topics', @@ -126,8 +126,8 @@ export default defineConfig({ link: '/aztec-nr/framework-description/contract-structure' }, { - text: '6.4.3 Declaring a Contract', - link: '/aztec-nr/framework-description/declaring-a-contract' + text: '6.4.3 Contract Scope', + link: '/aztec-nr/framework-description/contract-scope' }, { text: '6.4.4 Functions', @@ -158,35 +158,43 @@ export default defineConfig({ link: '/aztec-nr/framework-description/private-messaging' }, { - text: '6.4.15 Libraries', + text: '6.4.11 Libraries', link: '/aztec-nr/framework-description/libraries' }, { - text: '6.4.11 Ethereum<>Aztec Messaging', + text: '6.4.12 Ethereum<>Aztec Messaging', link: '/aztec-nr/framework-description/ethereum-aztec-messaging' }, { - text: '6.4.12 Cross-chain Interactions', + text: '6.4.13 Cross-chain Interactions', link: '/aztec-nr/framework-description/cross-chain-interactions' }, { - text: '6.4.13 Authentication Witness', + text: '6.4.14 Authentication Witness', link: '/aztec-nr/framework-description/authentication-witness' }, + { + text: '6.4.15 Reading Historic State', + link: '/aztec-nr/framework-description/reading-historic-state' + }, { text: '6.4.16 Upgradeable Contracts', link: '/aztec-nr/framework-description/upgradeable-contracts' }, { - text: '6.4.14 Protocol Oracles', + text: '6.4.17 Capsules', + link: '/aztec-nr/framework-description/capsules' + }, + { + text: '6.4.18 Protocol Oracles', link: '/aztec-nr/framework-description/protocol-oracles' }, { - text: '6.4.18 CoSnarks: Shared Private State', + text: '6.4.19 CoSnarks: Shared Private State', link: '/aztec-nr/framework-description/cosnarks' }, { - text: '6.4.17 Error Handling', + text: '6.4.20 Error Handling', link: '/aztec-nr/framework-description/error-handling' } ] @@ -254,35 +262,35 @@ export default defineConfig({ }, { text: '7.2 Installation', - link: '/aztec-js/getting-started/installation', + link: '/aztec-js/installation', }, { text: '7.3 Creating Accounts', - link: '/aztec-js/getting-started/creating-accounts', + link: '/aztec-js/creating-accounts', }, { text: '7.4 Deploying Contracts', - link: '/aztec-js/getting-started/deploying-contracts', + link: '/aztec-js/deploying-contracts', }, { text: '7.5 Sending Transactions', - link: '/aztec-js/getting-started/sending-transactions', + link: '/aztec-js/sending-transactions', }, { text: '7.6 Simulating Transactions', - link: '/aztec-js/getting-started/simulating-transactions', + link: '/aztec-js/simulating-transactions', }, { text: '7.7 Using Authorizing Other Accounts', - link: '/aztec-js/getting-started/using-authorizing-other-accounts', + link: '/aztec-js/using-authorizing-other-accounts', }, { text: '7.8 Paying Fees', - link: '/aztec-js/getting-started/paying-fees', + link: '/aztec-js/paying-fees', }, { text: '7.9 Testing Contracts', - link: '/aztec-js/getting-started/testing-contracts', + link: '/aztec-js/testing-contracts', }, { text: '7.10 Reference',