From the early days of blockchain, great minds and stakeholders have been concerned about on-chain privacy. The reason is simple: no matter how excited we are about DeFi, on-chain identity, or any other area, it just doesn’t make sense to store all personal data on-chain. Doing so would make it accessible to anyone, anywhere, at any time.
However, to convert a system that is designed to be transparent into a private one is a tricky task. It’s even harder if the goal is to provide dApps with “privacy as a feature” (i.e. privacy should be optional and flexible) not something that changes a transparent system into a fully private one.
In this article, we’ll talk about one of the core components unlocking privacy on Ethereum: state management. We’ll shed some light on programmable and composable privacy which allows dApp developers to choose what they want to make private and what to leave public. That allows for an ecosystem of privacy-preserving dApps that can communicate with each other as well as Ethereum.
Building programmable and composable privacy on Ethereum is Aztec’s core mission. There are two critical pieces needed to make this happen:
- Smart Contracts: the anatomy of an Aztec smart contract needs to accommodate both private and public functions and variables.
- Network: the Aztec network needs to have both private and public states.
In this piece, we will explore how different state modes enable this functionality, and explain how state management works on the Aztec Network.
State Management on Ethereum: Account-Based Model
For state management, Ethereum utilizes an account-based model. Whatever data needs to be stored, it takes the form of “key: value,” where “key” is derived from the account address, and “value” can be, for example, the account’s balance.
Here is a database showing some sample keys and associated values:
When a new transaction is executed, account balances are adjusted. For example, if Alice sends Bob $10, the new state will be the following:
This is the core property of the account-based model: account states are adjusted with each transaction.
Can the Account-Based Model Suggest Privacy?
But, what if we want a transaction—for example, a transfer—to be private? Can we simply encrypt all entries in the database to create privacy?
The answer is no, we can’t.
It leaks privacy.
If we execute a transaction on encrypted entries, people can still see which entries were modified: that constitutes a leak in privacy. If two transactions engage the same account one by one, it’s clear that this particular account state was modified.
By retrospectively analyzing account activity and the connections between them, one can retrieve tons of information. This means the account-based model doesn’t qualify for privacy needs.
UTXO Model Meets Privacy Needs
UTXO stands for unspent transaction output. In the UTXO model, the account state is represented as a record of unspent assets.
Unlike in the account-based model, in the UTXO model, account states cannot be modified. Instead, the model operates in an append-only manner. Whenever someone needs to update an account state—for example, the balance—they have to destroy some existing notes and create some new notes.
Going back to our previous example, Alice transfers $10 to Bob.
The updated database is:
*In the example above, “crossed-out” indicates destroyed entries.
So using UTXO, if we want a transaction to remain private, we can encrypt all the entries without causing a privacy leak:
When one entry is destroyed and a new entry is created, observers can’t determine if these entries refer to the same account or to different accounts. No additional information can be gleaned by analyzing accounts’ activity.
And that’s exactly what we need.
Aztec Meets the Needs of Ready-to-Use Privacy
Blended State Model on Aztec
Aztec utilizes the UTXO model for private state management and the account-based model for public state management. Public state is universal (i.e. one for the whole network and available for anyone) while private state is individual to every user.
A smart contract on Aztec consists of both private and public functions. When a smart contract is executed, the first thing that happens is all private functions are executed client-side and the user's private state is updated. After that, all public functions are executed and the transaction execution is reflected in the public state update.
Specifics of Aztec’s Private State
As we discussed earlier, for the sake of privacy, private state operates in the append-only mode. At the same time, modifying an entry in the UTXO model means “destroying” the existing entry (or multiple entries) and creating new entries reflecting the transaction. (In our example, a note with a transfer to the receiver and Alice’s updated balance).
How can the note be destroyed in an append-only mode? Using nullifiers.
A nullifier is a commitment corresponding to a private entry that was destroyed. Only the note owner is aware of the correspondence between notes and nullifiers. For everyone else, it’s impossible to define which nullifier was created for which note.
Every note can be consumed only once. When a new transaction emits a nullifier the sequencer has to check that this nullifier hasn’t existed before (to prevent double-spend).
Let’s circle back to our example: Alice has $100 and she is sending Bob $10.
- For simplicity’s sake, let’s first assume that Alice’s $100 is a single note.
- When Alice makes a transfer of $10, she destroys (or “consumes”) her $100 note. Meaning that she creates a nullifier corresponding to this particular note, and only she will know that this nullifier corresponds to the $100 note. She also creates a $10 note for Bob and a note for herself reflecting her updated balance of $90.
- Bob then can decrypt the note and get access to the $10 transferred to him by Alice. No other action is required because in Alice's transaction, she has already added the commitment to Bob's note ($10) to the database.
Let’s think of another example. Alice has two notes, $50 and $60, and she wants to transfer $100 to Bob. For this transaction, she has to destroy both notes, emit a note for Bob with a value of $100, and emit a note for herself updating her remaining balance of $10. That is to say that an arbitrary amount of notes can be consumed per transaction.
More generally, any state (i.e. not just balances) can be represented with notes. For example, if someone keeps a counter that they increase every time they send a transaction, they can store the value of the counter in a note. Every time they send a new transaction, they nullify the existing counter note and create a new one with the updated value.
Although notes and nullifiers are emitted client-side, it’s the sequencer who grabs the notes’ hashes and nullifiers (as commitments) and adds them to the databases (i.e. updates the state). That is to say, a client is always running its private execution environment in an “old” state.
However, what if someone picks an old state root that satisfies some logic that the current state root would not satisfy?
Circling back to our second example, imagine someone has a note where they keep an increasing counter. The counter owner is allowed to do something only if that counter is less than 10. So they can always pick a state root where the counter was less than 10 (even if it was 10 years ago) and just run their transaction over that state.
To mitigate this issue, the note must be nullified even if it was only read without spending it. This introduces the nullify-on-read concept that whenever someone reads a private note and performs some action on it (e.g. reading a note grants permission to make a call), they must nullify it and recreate it with the same value. Which means the content of each unique note can be read only once.
To Wrap it Up
In this article, we’ve explored the core concepts behind Aztec’s state management mechanism, which allows builders to get programmable and composable privacy. Now, it’s your turn!
Ready to put your hands on on-chain privacy and kick off the next big thing for Ethereum? – this is your next step.
Stay updated on all things Noir and Aztec by following Noir and Aztec on X, and join the Aztec developer community on Discord.