LogoLogo
  • WELCOME TO ALEPH ZERO
  • EXPLORE
    • About Aleph Zero
    • AlephBFT Consensus
    • The Economy of Aleph Zero
    • Where to Buy AZERO
    • Decentralized Governance on Aleph Zero
    • Ecosystem
    • Aleph Zero Foundation Treasury Management
    • Community
    • Glossary
    • Audit & Research Papers
  • USE
    • Wallets
    • Explorer
    • Ledger
    • Telegram Notifications
    • Aleph Zero Signer
      • General introduction
      • What does Signer do?
      • What are Sub-accounts and Sub-account paths?
      • Why is it critical to store your Secret Phrase in a safe place?
      • How to forget and restore accounts?
      • What are Networks?
      • What are Trusted apps?
    • Dashboard
      • Dashboard basics
      • Overview
    • Stake
      • Staking Basics
      • Staking Menu Overview
      • How to Start Staking with the Aleph Zero Dashboard
      • How to Start Staking With the Developer Wallet
      • How to start staking using Ledger hardware wallet
      • How to Change Nominations
      • How to Stop Staking
      • Staking Rewards
      • Validators
      • Commission and Foundation Nodes
      • Proxy Accounts
    • Validate
      • Validating Overview
      • Hardware requirements
      • Running an Aleph Node on Testnet
        • Downloading and running the node
        • Verifying your setup
        • Customizing your setup
        • Building and running from source [advanced]
          • Building from source
          • Set environment variables
          • Download DB snapshot
          • Running the binary
        • Appendix: Ports, addresses, validators, and archivists
      • Running an Aleph Node on Mainnet
        • Running the node
        • Building and running from source [advanced]
      • Setting your identity
      • Making the node validate
      • Securing your validator
      • Troubleshooting
      • Elections and Rewards Math
      • Testnet Validator Airdrop
      • Foundation Nomination Program
    • Using the EVM-layer
    • Governance
      • Token
      • Multisig Accounts
  • BUILD
    • Aleph Zero smart contracts basics
      • Setting up a Testnet account
      • Installing required tools
      • Creating your first contract
      • Deploying your contract to Aleph Zero Testnet
      • Extending your contract
    • Cross contract calls
      • Using references
      • Using dynamic calls
    • Migrating from Solidity
    • Writing e2e tests with ink-wrapper
    • Aleph Zero Signer integration
    • Front-end app: smart contract interaction
    • Security Course by Kudelski Security
      • ink! Developers Security Guideline
      • Lesson 1 - Getting started with ink!
      • Lesson 2 - Threat Assessment
      • Lesson 3 - Integer Overflow
      • Lesson 4 - Signed-integer
      • Lesson 5 - Role-Based Access Control
      • Lesson 6 - Address Validation
      • Lesson 7 - Smart Contract Control
    • Development on EVM-layer
  • PROTOCOL DETAILS
    • Shielder
      • Overview
      • Design against Bad Actors
      • Preliminaries - ZK-relations
      • Notes and Accounts
      • ZK-ID and Registrars
      • Anonymity Revokers
      • PoW Anonymity Revoking
      • Relayers
      • Deterministic Secret Management
      • SNARK-friendly Symmetric Encryption
      • SNARK-friendly Asymmetric Encryption
      • Cryptography
      • Token shortlist
      • User Wallet
      • Versioning
      • PoC
      • Version 0.1.0
      • Version 0.2.0
    • Common DEX
      • Common Whitepaper - Differences
      • Dutch Auctions
  • FAQ
  • Tutorials
    • Withdrawing coins from exchanges
      • How to withdraw your AZERO coins from KuCoin
      • How to withdraw your AZERO coins from MEXC Global
      • How to withdraw your AZERO coins from HTX
  • Setting up or restoring a wallet
    • How to set up or recover your AZERO account using Aleph Zero Signer
    • How to set up or recover your AZERO account using the official mainnet web wallet
    • How to set up or recover your AZERO account using Nova Wallet
    • How to set up or recover your AZERO account using SubWallet
    • How to set up or recover your AZERO account using Talisman
  • Staking
    • How to stake via a direct nomination using the Aleph Zero Dashboard
    • How to stake via a nomination pool using the Aleph Zero Dashboard
    • How to destroy a nomination pool via the Aleph Zero Dashboard
Powered by GitBook
On this page
  • Alternative designs
  • Note representation
  • Withdrawrelation
  • Implementing constraint group (C1) in a circuit
  • Implementing constraint group (C2) – the range check – in a circuit
  • Increasing balance tuple size

Was this helpful?

  1. PROTOCOL DETAILS
  2. Shielder

Token shortlist

Note: this way of supporting ERC20 tokens has not been implemented for version 0.2.0. Instead a different approach is introduced -- see Version 0.2.0.

On this page, we explain how to update Version 0.1.0 circuits – supporting only the native AZERO token – to handle more tokens. Due to note structure limitations, in this update we choose around 5-30 tokens to support, and we call the list of chosen tokens the token shortlist. Full ERC20 support is expected in future releases.

We only discuss the Withdrawcircuit, as the remaining circuits should be easy to implement if the design of Withdrawis known.

We represent the account balances as a 6-tuple. We assume that there are 2 destination addresses (for the recipient and for the fee), and we do not differentiate between them. The circuit receives 2 indexes of modified balances as public inputs, and performs range checks just on the 2 new balances.

In the future, we will be able to extend the length of the tuple with small changes to the circuits and no note migration. We describe such an extension in the last section.

Alternative designs

We considered storing the shortlisted balances as a tree of height 2, but abandoned this idea because it visibly increases circuit complexity without significant performance gains for the expected number of shortlisted tokens.

Note representation

We represent the account balances of all tokens, including AZERO, as the following tuple:

const NUM_SLOTS: usize = 6;

type Account = [Scalar; NUM_SLOTS];

We do not need to decide in advance, which tokens occupy all NUM_SLOTSslots. We may define the slot mapping in the contract just for a subset of slots, and make the contract reject attempts to use the unused slots. In any case, the NewAccountcircuit should properly initialize all slots.

Our note struct is given by:

struct Note {
    version: Scalar,
    id: Scalar,
    nullifier: Scalar,
    trapdoor: Scalar,
    h_acc: Scalar,          // hash of the `Account` tuple
} 

For an account tuple acc, we define its hash as:

h_acc = poseidon2(acc[0], ..., acc[5], 0).      

Withdrawrelation

We are ready to define the Withdrawrelation:

relation Withdraw

inputs:
    - merkle_root:      Scalar
    - h_nullifier_old:  Scalar
    - h_note_new:       Scalar
    - value_1:          Scalar    // amount that goes to the 1st recipient
    - value_2:          Scalar    // amount that goes to the 2nd recipient
    - idx_1:            Scalar    // 0-based index of token sent to the 1st recipient
    - idx_2:            Scalar    // 0-based index of token sent to the 2nd recipient
    - commitment:       Scalar    // Keccak hash of values that do not need
                                  // to be unpacked inside the circuit
    
witnesses:
    - note_old:            Note
    - note_new:            Note
    - acc_old:             Account
    - acc_new:             Account
    - merkle_path:         [[Scalar; MERKLE_ARITY]; MERKLE_HEIGHT]

constraints:
    // (C1)
    - for j ∈ { 0, ..., NUM_SLOTS - 1 }:
      acc_new[j] = ⎧ acc_old[j] - value_1 - value_2,     if j = idx_1 = idx_2,
                   ⎪ acc_old[j] - value_1,               if j = idx_1 != idx_2,
                   ⎪ acc_old[j] - value_2,               if j = idx_2 != idx_1,
                   ⎩ acc_old[j],                         otherwise
       
    // (C2) Range check, prevents overflow after subtraction.            
    - acc_new[j] <= AMOUNT_BOUND                         for j ∈ { idx_1, idx_2 }
        
    // Updated hash application:
    - note_old.h_acc is a correct hash of acc_old
    - note_new.h_acc is a correct hash of acc_new
    
    // Same as before.
    - note_new.id = note_old.id
    - h_note_new = hash(note_new)
    - h_nullifier_old = hash(note_old.nullifier)
    - hash(note_old) belongs to the first layer of merkle_path
    - merkle_path is a valid path to merkle_root

The definition above does not immediately translate to a circuit description because of the way constraint groups (C1) and (C2) are defined. These constraint groups may be implemented in a circuit as follows:

Implementing constraint group (C1) in a circuit

First, we introduce auxiliary private witnesses idx_i_j ∈ {0, 1}such that:

idx_i_j = 1 iff idx_i = j, for i ∈ {1, 2}, j ∈ {0, ..., NUM_SLOTS - 1}.

Then, for every j ∈ {0, ..., NUM_SLOTS - 1}, we add the following constraint:

acc_new[j] = acc_old[j] - value_1 * idx_1_j - value_2 * idx_2_j.

We create the above constraints using a custom gate.

Implementing constraint group (C2) – the range check – in a circuit

We just add the following constraint, for i ∈ {1, 2}:

∑_{j ∈ {0, ..., NUM_SLOTS - 1}} idx_i_j * acc_new[j] <= AMOUNT_BOUND

Increasing balance tuple size

We may increase NUM_SLOTS. We always require NUM_SLOTSto be a multiple of 6.

For NUM_SLOTS = 6k > 6, we define h_acc = h(acc, 0), where h(acc, i)is recursively defined as:

h(acc, i) =
    ⎧ poseidon2(acc[i], ..., acc[i + 5], 0), 
    |       if i = NUM_SLOTS - 6 or acc[i + 6..NUM_SLOTS] has only zeroes,
    | poseidon2(
    |     acc[i],
    |     ...,
    |     acc[i + 5],
    |     h(acc, i + 6)
    ⎩ ),                                                      otherwise.       

For example:

  • The hash of (1, 2, 3, 4, 5, 6, 0, 0, 0, 0, 0, 0) is poseidon2(1, 2, 3, 4, 5, 6, 0).

  • The hash of (1, 2, 3, 4, 5, 6, 0, 0, 0, 0, 0, 1) is poseidon2(1, 2, 3, 4, 5, 6, poseidon2(0, 0, 0, 0, 0, 1, 0)).

Note that we do not allow (1, 2, 3, 4, 5, 6, 0, 0, 0, 0, 0, 0) to be hashed as poseidon2(1, 2, 3, 4, 5, 6, poseidon2(0, 0, 0, 0, 0, 0, 0)). This way, every balance tuple has a deterministic hash value.

This change requires updating NUM_SLOTSand the hashing logic in the circuits, but no note migration.

PreviousCryptographyNextUser Wallet

Last updated 3 months ago

Was this helpful?