Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

docs: events #1768

Merged
merged 7 commits into from
Aug 24, 2023
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
95 changes: 91 additions & 4 deletions docs/docs/dev_docs/contracts/events.md
Original file line number Diff line number Diff line change
@@ -1,17 +1,104 @@
## Events
Events in Aztec works similarly to Ethereum events in a sense that they are a way for contracts to communicate with the outside world.
benesjan marked this conversation as resolved.
Show resolved Hide resolved
They are emitted by contracts and stored inside each instance ofAztecNode.
benesjan marked this conversation as resolved.
Show resolved Hide resolved
Aztec events are currently represented as raw data and are not ABI encoded.
ABI encoded events are a feature that will be added in the future.
benesjan marked this conversation as resolved.
Show resolved Hide resolved

Unlike on Ethereum, there are 2 types of events supported by Aztec: encrypted and unencrypted.

### Encrypted Events
Encrypted events can only be emitted by private functions and are encrypted using a public key of a recipient.
For this reason it is necessary to register a recipient in the AztecRPC server before encrypting the events for them.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This reminded me that we need to broadcast account data, when an account contract is deployed, so that the public key and partial address can be grabbed automatically by the simulator (from the public node). #1778

Recipients can be registered using the Aztec CLI or Aztec.js:

import Tabs from '@theme/Tabs';
import TabItem from '@theme/TabItem';

<Tabs groupId="events">
<TabItem value="cli" label="Aztec CLI">

```bash
aztec-cli register-recipient --address 0x147392a39e593189902458f4303bc6e0a39128c5a1c1612f76527a162d36d529 --public-key 0x26e193aef4f83c70651485b5526c6d01a36d763223ab24efd1f9ff91b394ac0c20ad99d0ef669dc0dde8d5f5996c63105de8e15c2c87d8260b9e6f02f72af622 --partial-address 0x200e9a6c2d2e8352012e51c6637659713d336405c29386c7c4ac56779ab54fa7
```

</TabItem>
<TabItem value="js" label="Aztec.js">

```ts
const aztecAddress = AztecAddress.fromString("0x147392a39e593189902458f4303bc6e0a39128c5a1c1612f76527a162d36d529");
const publicKey = Point.fromString("0x26e193aef4f83c70651485b5526c6d01a36d763223ab24efd1f9ff91b394ac0c20ad99d0ef669dc0dde8d5f5996c63105de8e15c2c87d8260b9e6f02f72af622");
const partialAddress = Fr.fromString("0x200e9a6c2d2e8352012e51c6637659713d336405c29386c7c4ac56779ab54fa7");

const completeAddress = CompleteAddress.create(aztecAddress, publicKey, partialKey);
await aztecRpc.registerRecipient(completeAddress);
```

</TabItem>
</Tabs>

> **NOTE**: If a note recipient is one of the accounts inside the Aztec RPC, we don't need to register it as a recipient because we already have the public key available.
benesjan marked this conversation as resolved.
Show resolved Hide resolved

At this point we only allow emitting note spending info through encrypted events.
In the future we will allow emitting arbitrary information.
(If you currently emit arbitrary information, AztecRPC server will fail to process it and it will not be queryable.)
benesjan marked this conversation as resolved.
Show resolved Hide resolved

To emit encrypted logs first import the `emit_encrypted_log` utility function inside your contract:

#include_code encrypted_import /yarn-project/noir-libs/value-note/src/utils.nr rust

Then you can call the function:

#include_code encrypted /yarn-project/noir-libs/value-note/src/utils.nr rust

### Constraining events

### Unencrypted Events
Unencrypted events are events which can be read by anyone.
They can be emitted by both public and private functions.
It's important for a developer to consider whether it's acceptable for the unencrypted event to be emitted from private functions as it might pose a significant privacy leak.
benesjan marked this conversation as resolved.
Show resolved Hide resolved

### Encrypted Events
Once emitted, unencrypted events are stored in AztecNode and can be queried by anyone:
<Tabs groupId="events">
<TabItem value="cli" label="Aztec CLI">

```bash
aztec-cli get-logs --from 5 --limit 1
```

</TabItem>
<TabItem value="js" label="Aztec.js">

#include_code logs /yarn-project/end-to-end/src/e2e_public_token_contract.test.ts typescript

</TabItem>
</Tabs>

To emit unencrypted logs first import the `emit_unencrypted_log` utility function inside your contract:

#include_code unencrypted_import /yarn-project/noir-contracts/src/contracts/private_token_contract/src/main.nr rust

Then you can call the function:

#include_code unencrypted /yarn-project/noir-contracts/src/contracts/private_token_contract/src/main.nr rust

### Costs

Explain L1 cost to emit an event.
All the event data is pushed on Ethereum by the sequencer and for this reason the cost of emitting an event is non-trivial.
benesjan marked this conversation as resolved.
Show resolved Hide resolved

## Processing events
Both the encrypted and unencrypted events are stored in AztecNode.
Unencrypted logs can be queried by anyone as we described above in the [Unencrypted Events](#unencrypted-events) section.

Encrypted logs need to first be decrypted:

### Decrypting
One function of AztecRPC server is constantly loading encrypted logs from AztecNode and trying to decrypt them.
When new encrypted logs are obtained, AztecRPC server will try to decrypt them using the private encryption key of all the accounts registered inside AztecRPC server.
If the decryption is successful, AztecRPC server will store the decrypted note inside a database.
benesjan marked this conversation as resolved.
Show resolved Hide resolved
If the decryption fails, the specific log will be discarded.

For the AztecRPC server to successfully process the decrypted note we need to compute note hash and nullifier.
benesjan marked this conversation as resolved.
Show resolved Hide resolved
Because we want to support arbitrary ways of computing these values, Noir developers need to implement a `compute_note_hash_and_nullifier` function inside their contracts.
benesjan marked this conversation as resolved.
Show resolved Hide resolved

This is an example implementation inside the `PrivateTokenContract`:

### Stev
#include_code compute_note_hash_and_nullifier /yarn-project/noir-contracts/src/contracts/private_token_contract/src/main.nr rust
2 changes: 1 addition & 1 deletion docs/docs/dev_docs/getting_started/cli.md
Original file line number Diff line number Diff line change
Expand Up @@ -222,7 +222,7 @@ View result: [
Finally, we can use the CLI's `get-logs` command to retrieve unencrypted logs emitted by the contract:

```
% aztec-cli get-logs 5 1
% aztec-cli get-logs --from 5 --limit 1
Logs found:

Coins transferred
Expand Down
2 changes: 1 addition & 1 deletion docs/docs/dev_docs/getting_started/sandbox.md
Original file line number Diff line number Diff line change
Expand Up @@ -197,7 +197,7 @@ Great!. The Sandbox is running and we are able to interact with it.

The next step is to create some accounts. An in-depth explaining about accounts on aztec can be found [here](../../concepts/foundation/accounts/main.md). But creating an account on the Sandbox does 2 things:

1. Deploys an account contract reprepresenting you allowing you to perform actions on the network (deploy contracts, call functions etc).
1. Deploys an account contract representing you allowing you to perform actions on the network (deploy contracts, call functions etc).
benesjan marked this conversation as resolved.
Show resolved Hide resolved
2. Adds your encryption keys to the RPC Server allowing it to decrypt and manage your private state.

Continue with adding the following to the `index.ts` file in our example:
Expand Down
4 changes: 4 additions & 0 deletions yarn-project/end-to-end/src/e2e_public_token_contract.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,8 +27,12 @@ describe('e2e_public_token_contract', () => {
};

const expectLogsFromLastBlockToBe = async (logMessages: string[]) => {
// docs:start:logs

const l2BlockNum = await aztecRpcServer.getBlockNumber();
const unencryptedLogs = await aztecRpcServer.getUnencryptedLogs(l2BlockNum, 1);

// docs:end:logs
const unrolledLogs = L2BlockL2Logs.unrollLogs(unencryptedLogs);
const asciiLogs = unrolledLogs.map(log => log.toString('ascii'));

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,13 @@ contract PrivateToken {
note_header::NoteHeader,
utils as note_utils,
};

// docs:start:unencrypted_import

use dep::aztec::log::emit_unencrypted_log;

// docs:end:unencrypted_import

// docs:start:storage-import
use crate::storage::Storage;
// docs:end:storage-import
Expand Down Expand Up @@ -62,8 +67,13 @@ contract PrivateToken {
// Insert new note to a set of user notes and emit the newly created encrypted note preimage via oracle call.
let owner_balance = storage.balances.at(owner);
send_note(&mut context, owner_balance, amount, owner);

// docs:start:unencrypted

emit_unencrypted_log(&mut context, "Coins minted");

// docs:end:unencrypted

// Return private circuit public inputs. All private functions need to return this as it is part of the input of the private kernel..
context.finish()
}
Expand Down Expand Up @@ -116,8 +126,10 @@ contract PrivateToken {
// Computes note hash and nullifier.
// Note 1: Needs to be defined by every contract producing logs.
// Note 2: Having it in all the contracts gives us the ability to compute the note hash and nullifier differently for different kind of notes.
// docs:start:compute_note_hash_and_nullifier
unconstrained fn compute_note_hash_and_nullifier(contract_address: Field, nonce: Field, storage_slot: Field, preimage: [Field; VALUE_NOTE_LEN]) -> [Field; 4] {
let note_header = NoteHeader { contract_address, nonce, storage_slot };
note_utils::compute_note_hash_and_nullifier(ValueNoteMethods, note_header, preimage)
}
// docs:end:compute_note_hash_and_nullifier
}
4 changes: 4 additions & 0 deletions yarn-project/noir-libs/value-note/src/utils.nr
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
use dep::aztec::context::PrivateContext;
// docs:start:encrypted_import

use dep::aztec::log::emit_encrypted_log;

// docs:end:encrypted_import
use dep::aztec::note::note_getter_options::NoteGetterOptions;
use dep::aztec::oracle::get_public_key::get_public_key;
use dep::aztec::state_vars::set::Set;
Expand Down