Skip to content

Commit

Permalink
chore(docs): Update token bridge tutorial (AztecProtocol#3773)
Browse files Browse the repository at this point in the history
This PR adds source code references to code that was previously hard
coded. It also adds a couple of functions that are in the implementation
but were not referenced in the tutorial.
- removes references to the `utils.nr` file that is no longer used in
the implementation in noir-contracts.
- Adds the FORK_URL and FORK_BLOCK_NUMBER env vars to the sandbox
reference page

# Checklist:
Remove the checklist to signal you've completed it. Enable auto-merge if
the PR is ready to merge.
- [ ] If the pull request requires a cryptography review (e.g.
cryptographic algorithm implementations) I have added the 'crypto' tag.
- [ ] I have reviewed my diff in github, line by line and removed
unexpected formatting changes, testing logs, or commented-out code.
- [ ] Every change is related to the PR description.
- [ ] I have
[linked](https://docs.github.com/en/issues/tracking-your-work-with-issues/linking-a-pull-request-to-an-issue)
this pull request to relevant issues (if any exist).
  • Loading branch information
critesjosh authored Jan 2, 2024
1 parent 4dd076c commit 764cb46
Show file tree
Hide file tree
Showing 8 changed files with 38 additions and 71 deletions.
5 changes: 3 additions & 2 deletions docs/docs/dev_docs/cli/sandbox-reference.md
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,9 @@ MODE='sandbox' # Option to start the sandbox or a standalone part of the system.
AZTEC_NODE_PORT=8079 # The port that the Aztec node wil be listening to (default: 8079)
PXE_PORT=8080 # The port that the PXE will be listening to (default: 8080)


# Ethereum Forking (Optional: not enabled by default) #
FORK_BLOCK_NUMBER=0 # The block number to fork from
FORK_URL="" # The URL of the Ethereum node to fork from

## Polling intervals ##
ARCHIVER_POLLING_INTERVAL_MS=50
Expand All @@ -78,7 +80,6 @@ Variables like `DEPLOY_AZTEC_CONTRACTS` & `AZTEC_NODE_PORT` are valid here as de
`TEST_ACCOUNTS` cannot be used here because the Aztec node does not control an Aztec account to deploy contracts from.

```sh

# P2P config #
# Configuration variables for connecting a Node to the Aztec Node P2P network. You'll need a running P2P-Bootstrap node to connect to.
P2P_ENABLED='false' # A flag to enable P2P networking for this node. (default: false)
Expand Down
48 changes: 6 additions & 42 deletions docs/docs/dev_docs/tutorials/token_portal/depositing_to_aztec.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,32 +8,7 @@ In this step, we will write our token portal contract on L1.

In `l1-contracts/contracts` in your file called `TokenPortal.sol` paste this:

```solidity
pragma solidity ^0.8.20;
import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import {SafeERC20} from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
// Messaging
import {IRegistry} from "@aztec/l1-contracts/src/core/interfaces/messagebridge/IRegistry.sol";
import {IInbox} from "@aztec/l1-contracts/src/core/interfaces/messagebridge/IInbox.sol";
import {DataStructures} from "@aztec/l1-contracts/src/core/libraries/DataStructures.sol";
import {Hash} from "@aztec/l1-contracts/src/core/libraries/Hash.sol";
contract TokenPortal {
using SafeERC20 for IERC20;
IRegistry public registry;
IERC20 public underlying;
bytes32 public l2TokenAddress;
function initialize(address _registry, address _underlying, bytes32 _l2TokenAddress) external {
registry = IRegistry(_registry);
underlying = IERC20(_underlying);
l2TokenAddress = _l2TokenAddress;
}
}
```
#include_code init /l1-contracts/test/portals/TokenPortal.sol solidity

This imports relevant files including the interfaces used by the Aztec rollup. And initializes the contract with the following parameters:

Expand All @@ -45,20 +20,7 @@ Create a basic ERC20 contract that can mint tokens to anyone. We will use this t

Create a file `PortalERC20.sol` in the same folder and add:

```solidity
// SPDX-License-Identifier: Apache-2.0
pragma solidity ^0.8.0;
import "@openzeppelin/contracts/token/ERC20/ERC20.sol";
contract PortalERC20 is ERC20 {
constructor() ERC20("Portal", "PORTAL") {}
function mint(address to, uint256 amount) external {
_mint(to, amount);
}
}
```
#include_code contract /l1-contracts/test/portals/PortalERC20.sol solidity

## Depositing tokens to Aztec publicly

Expand Down Expand Up @@ -108,8 +70,10 @@ Note that because L1 is public, everyone can inspect and figure out the fee, con

**So how do we privately consume the message on Aztec?**

On Aztec, anytime something is consumed, we emit a nullifier hash and add it to the nullifier tree. This prevents double-spends. The nullifier hash is a hash of the message that is consumed. So without the secret, one could reverse engineer the expected nullifier hash that might be emitted on L2 upon message consumption. Hence, to consume the message on L2, the user provides a secret to the private noir function, which computes the hash and asserts that it matches to what was provided in the L1->L2 message. This secret is then included in the nullifier hash computation and emits this nullifier. This way, anyone inspecting the blockchain, won’t know which nullifier hash corresponds to the L1->L2 message consumption.
On Aztec, anytime something is consumed (i.e. deleted), we emit a nullifier hash and add it to the nullifier tree. This prevents double-spends. The nullifier hash is a hash of the message that is consumed. So without the secret, one could reverse engineer the expected nullifier hash that might be emitted on L2 upon message consumption. To consume the message on L2, the user provides a secret to the private function, which computes the hash and asserts that it matches to what was provided in the L1->L2 message. This secret is included in the nullifier hash computation and the nullifier is added to the nullifier tree. Anyone inspecting the blockchain won’t know which nullifier hash corresponds to the L1->L2 message consumption.

Note: the secret hashes are Pedersen hashes since the hash has to be computed on L2, and sha256 hash is very expensive for zk circuits. The content hash however is a sha256 hash truncated to a field as clearly shown before.
:::note
Secret hashes are Pedersen hashes since the hash has to be computed on L2 and sha256 hash is very expensive for zk circuits. The content hash however is a sha256 hash truncated to a field as shown before.
:::

In the next step we will start writing our L2 smart contract to mint these tokens on L2.
24 changes: 7 additions & 17 deletions docs/docs/dev_docs/tutorials/token_portal/minting_on_aztec.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,23 +6,18 @@ In this step we will start writing our Aztec.nr bridge smart contract and write

## Initial contract setup

In our `token-bridge` Noir project in `aztec-contracts`, under `src` there is an example `main.nr` file. Delete all the code in here and paste this to define imports and initialize the constructor:
In our `token-bridge` Aztec project in `aztec-contracts`, under `src` there is an example `main.nr` file. Paste this to define imports and initialize the constructor:

```rust
mod util;
#include_code token_bridge_imports /yarn-project/noir-contracts/src/contracts/token_bridge_contract/src/main.nr raw
use crate::token_interface::Token;
use crate::util::{get_mint_public_content_hash, get_mint_private_content_hash, get_withdraw_content_hash};
#include_code token_bridge_storage_and_constructor /yarn-project/noir-contracts/src/contracts/token_bridge_contract/src/main.nr raw
```
#include_code token_bridge_imports /yarn-project/noir-contracts/src/contracts/token_bridge_contract/src/main.nr rust

This imports Aztec-related dependencies and our two helper files `token_interface.nr` and `util.nr`.
#include_code token_bridge_storage_and_constructor /yarn-project/noir-contracts/src/contracts/token_bridge_contract/src/main.nr rust

This imports Aztec-related dependencies and our helper file `token_interface.nr`.
(The code above will give errors right now - this is because we haven't implemented util and token_interface yet.)

In `token_interface.nr`, add this:
#include_code token_bridge_token_interface /yarn-project/noir-contracts/src/contracts/token_bridge_contract/src/token_interface.nr rust

We will write `util.nr` as needed.
#include_code token_bridge_token_interface /yarn-project/noir-contracts/src/contracts/token_bridge_contract/src/token_interface.nr rust

## Consume the L1 message

Expand All @@ -31,9 +26,6 @@ In the previous step, we have moved our funds to the portal and created a L1->L2
In `main.nr`, now paste this `claim_public` function:
#include_code claim_public /yarn-project/noir-contracts/src/contracts/token_bridge_contract/src/main.nr rust

In your `util.nr` paste this `mint_public_content_hash` function:
#include_code mint_public_content_hash_nr /yarn-project/noir-contracts/src/contracts/token_portal_content_hash_lib/src/lib.nr rust

The `claim_public` function enables anyone to consume the message on the user's behalf and mint tokens for them on L2. This is fine as the minting of tokens is done publicly anyway.

**What’s happening here?**
Expand All @@ -55,9 +47,7 @@ Now we will create a function to mint the amount privately. Paste this into your

#include_code call_mint_on_token /yarn-project/noir-contracts/src/contracts/token_bridge_contract/src/main.nr rust

Then inside your `util.nr`, paste this:

#include_code get_mint_private_content_hash /yarn-project/noir-contracts/src/contracts/token_portal_content_hash_lib/src/lib.nr rust
The `get_mint_private_content_hash` function is imported from the `token_portal_content_hash_lib`.

If the content hashes were constructed similarly for `mint_private` and `mint_publicly`, then content intended for private execution could have been consumed by calling the `claim_public` method. By making these two content hashes distinct, we prevent this scenario.

Expand Down
10 changes: 4 additions & 6 deletions docs/docs/dev_docs/tutorials/token_portal/setup.md
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ aztec-contracts
└── token_bridge
├── Nargo.toml
├── src
├── main
├── main.nr
```

Inside `Nargo.toml` add the following content:
Expand All @@ -62,8 +62,7 @@ type = "contract"

[dependencies]
aztec = { git="https://github.com/AztecProtocol/aztec-packages/", tag="#include_aztec_version", directory="yarn-project/aztec-nr/aztec" }
value_note = { git="https://github.com/AztecProtocol/aztec-packages/", tag="#include_aztec_version", directory="yarn-project/aztec-nr/value-note"}
safe_math = { git="https://github.com/AztecProtocol/aztec-packages/", tag="#include_aztec_version", directory="yarn-project/aztec-nr/safe-math"}
token_portal_content_hash_lib = { git="https://github.com/AztecProtocol/aztec-packages/", tag="aztec-packages-v0.16.9", directory="yarn-project/noir-contracts/src/contracts/token_portal_content_hash_lib" }
protocol_types = { git="https://github.com/AztecProtocol/aztec-packages/", tag="#include_aztec_version", directory="yarn-project/noir-protocol-circuits/src/crates/types"}
```

Expand All @@ -76,7 +75,6 @@ aztec-contracts
├── src
├── main.nr
├── token_interface.nr
├── util.nr
```

# Create a JS hardhat project
Expand Down Expand Up @@ -110,8 +108,6 @@ This is what your `l1-contracts` should look like:

```tree
├── README.md
├── artifacts
├── cache
├── contracts
├── hardhat.config.js
├── node_modules
Expand All @@ -138,6 +134,8 @@ yarn add @aztec/aztec.js @aztec/noir-contracts @aztec/types @aztec/foundation @a
yarn add -D jest @jest/globals ts-jest
```

If you are going to track this repo using git, consider adding a `.gitignore` file to your `src` directory and adding `node_modules` to it.

In `package.json`, add:

```json
Expand Down
14 changes: 11 additions & 3 deletions docs/docs/dev_docs/tutorials/token_portal/withdrawing_to_l1.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,7 @@ Go back to your `main.nr` and paste this:

#include_code exit_to_l1_public /yarn-project/noir-contracts/src/contracts/token_bridge_contract/src/main.nr rust

For this to work we will need this helper function, in `util.nr`:

#include_code get_withdraw_content_hash /yarn-project/noir-contracts/src/contracts/token_portal_content_hash_lib/src/lib.nr rust
For this to work we import the `get_withdraw_content_hash` helper function from the `token_portal_content_hash_lib`.

**What’s happening here?**

Expand Down Expand Up @@ -53,6 +51,16 @@ We also use a `_withCaller` parameter to determine the appropriate party that ca

We call this pattern _designed caller_ which enables a new paradigm **where we can construct other such portals that talk to the token portal and therefore create more seamless crosschain legos** between L1 and L2.

Before we can compile and use the contract, we need to add two additional functions.

We need a function that let's us read the token value.

#include_code read_token /yarn-project/noir-contracts/src/contracts/token_bridge_contract/src/main.nr rust

And the `compute_note_hash_and_nullifier` required on every contract.

#include_code compute_note_hash_and_nullifier_placeholder /yarn-project/noir-contracts/src/contracts/token_bridge_contract/src/main.nr rust

## Compile code

Congratulations, you have written all the contracts we need for this tutorial! Now let's compile them.
Expand Down
2 changes: 2 additions & 0 deletions l1-contracts/test/portals/PortalERC20.sol
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
// SPDX-License-Identifier: Apache-2.0
// docs:start:contract
pragma solidity ^0.8.0;

import "@oz/token/ERC20/ERC20.sol";
Expand All @@ -10,3 +11,4 @@ contract PortalERC20 is ERC20 {
_mint(to, amount);
}
}
// docs:end:contract
2 changes: 2 additions & 0 deletions l1-contracts/test/portals/TokenPortal.sol
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
// docs:start:init
pragma solidity >=0.8.18;

import {IERC20} from "@oz/token/ERC20/IERC20.sol";
Expand All @@ -23,6 +24,7 @@ contract TokenPortal {
underlying = IERC20(_underlying);
l2TokenAddress = _l2TokenAddress;
}
// docs:end:init

// docs:start:deposit_public
/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,11 +19,11 @@ contract TokenBridge {
types::type_serialization::address_serialization::AddressSerializationMethods,
selector::compute_selector,
};
// docs:end:token_bridge_imports

use dep::token_portal_content_hash_lib::{get_mint_public_content_hash, get_mint_private_content_hash, get_withdraw_content_hash};

use crate::token_interface::Token;
// docs:end:token_bridge_imports

// docs:start:token_bridge_storage_and_constructor
// Storage structure, containing all storage, and specifying what slots they use.
Expand Down Expand Up @@ -148,9 +148,11 @@ contract TokenBridge {

// /// Unconstrained ///

// docs:start:read_token
unconstrained fn token() -> pub AztecAddress {
storage.token.read()
}
// docs:end:read_token

#[aztec(public)]
internal fn _initialize(token: AztecAddress) {
Expand Down

0 comments on commit 764cb46

Please sign in to comment.