Skip to content

Commit

Permalink
docs(yellow-paper): Contract deployment
Browse files Browse the repository at this point in the history
Adds section on contract deployment, as well as contract trees to state
and delegate calls.
  • Loading branch information
spalladino committed Dec 7, 2023
1 parent 48e2e3d commit 2a98685
Show file tree
Hide file tree
Showing 6 changed files with 144 additions and 1 deletion.
11 changes: 11 additions & 0 deletions yellow-paper/docs/calls/delegate-calls.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
---
sidebar_position: 3.2
---

# Delegate calls

Delegate calls are function calls against a [contract class identifier](../contract-deployment/classes.md) instead of an instance. Any call, synchronous or asynchronous, can be made as a delegate call. The behavior of a delegate call is to execute the function code in the specified class identifier but on the context of the current instance. This opens the door to script-like executions and upgradeable contracts. Delegate calls are based on [EIP7](https://eips.ethereum.org/EIPS/eip-7).

At the protocol level, a delegate call is identified by a `is_delegate_call` flag in the `CircuitPublicInputs` of the `CallStackItem`. The `contract_address` field is reinterpreted as a contract class instead. When executing a delegate call, the kernel preserves the values of the `CallContext` `msgSender` and `storageContractAddress`.

At the contract level, a caller can initiate a delegate call via a `delegateCallPrivateFunction` or `delegateCallPublicFunction` oracle call. The caller is responsible for asserting that the returned `CallStackItem` has the `is_delegate_call` flag correctly set.
72 changes: 72 additions & 0 deletions yellow-paper/docs/contract-deployment/classes.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
# Contract classes

A contract class is a collection of related unconstrained, private, and public functions. Contract classes don't have state, they just define code.

## Rationale

Contract classes simplify the process of reusing code by enshrining implementations as a first-class citizen at the protocol. Given multiple contract instances that rely on the same class, the class needs to be declared only once, reducing the deployment cost for all contract instances. Classes also simplify the process of upgradeability. Classes decouple state from code, making it easier for an instance to switch to different code while retaining its state.

:::info
Read the following discussions for additional context:
- [Abstracting contract deployment](https://forum.aztec.network/t/proposal-abstracting-contract-deployment/2576)
- [Implementing contract upgrades](https://forum.aztec.network/t/implementing-contract-upgrades/2570)
- [Contract classes, upgrades, and default accounts](https://forum.aztec.network/t/contract-classes-upgrades-and-default-accounts/433)
:::

## Structure

The structure of a contract class is defined as:

| Field | Type | Description |
|----------|----------|----------|
| version | u8 | Version identifier. Initially one, bumped for any changes to the contract class struct. |
| registerer_address | AztecAddress | Address of the canonical contract used for registering this class. |
| artifact_hash | Field | Hash of the entire contract artifact, including compiler information, proving backend, and all generated ACIR and Brillig. The specification of this hash is left to the compiler and not enforced by the protocol. |
| constructor_function | PrivateFunction | Constructor for instances of this class. |
| private_functions | PrivateFunction[] | List of private functions. |
| public_functions | PublicFunction[] | List of public functions. |
| unconstrained_functions | UnconstrainedFunction[] | List of unconstrained functions. |

<!-- TODO: Do we need the artifact hash, if we're including the artifact hash of each individual function? -->
<!-- NOTE: I'm deliberately omitting the portal bytecode hash here -->

### Private Function

| Field | Type | Description |
|----------|----------|----------|
| function_selector | u32 | Selector of the function. Calculated as the hash of the method name and arguments. |
| vk_hash | Field | Hash of the verification key associated to this private function. |
| salt | Field | Optional value for salting the bytecode of a function. |
| bytecode_hash | Field | Hash of the compiled function artifact, including all generated ACIR and Brillig. |
| optional_bytecode | Buffer | Optional bytecode for the function. Private function bytecode can be kept private if desired and only broadcasted to participants of the contract. |

### Public and Unconstrained Function

| Field | Type | Description |
|----------|----------|----------|
| function_selector | u32 | Selector of the function. Calculated as the hash of the method name and arguments. |
| bytecode_hash | Field | Hash of the compiled function artifact, including all generated Brillig code. |
| bytecode | Buffer | Full bytecode for the function. Must hash to the `artifact_hash`. |

<!-- TODO: Expand on the bytecode commitment scheme and bytecode_hash, both here and for private fns. -->

## Registration

A contract class is registered by calling a `register` function in a canonical `ClassRegisterer` contract. The `register` function receives a `ContractClass` struct as defined above, and performs the following checks:

<!-- TODO: Should register be private or public? Or both? -->

- `version` is 1 for the initial release
- `registerer_address` equals to self
- `bytecode` for each function hashes to the `bytecode_hash`

The `register` function then:

- Emits the `ContractClass` struct as unencrypted events.
- Computes the class identifier as the hash of the `ContractClass` object.
- Returns the computed class identifier as a `new_contract_classes` public input.

<!-- TODO: Define the format of the unencrypted event -->
<!-- TODO: Define how to compute the hash -->

The kernel circuit then validates that the contract emitting the new contract classes is the canonical `ClassRegisterer`, and emits the `new_contract_classes` so they are added to the contract class tree in global state. Upon seeing a new contract class registration in a mined transaction, nodes are expected to store the contract class, so they can retrieve it when executing a public function for that class.
11 changes: 11 additions & 0 deletions yellow-paper/docs/contract-deployment/index.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
---
title: Contract Deployment
---

# Contract Deployment

Contracts in Aztec are deployed as _instances_ of a contract _class_. Deploying a new contract then requires first registering the _class_, if it has not been registered before, and then actually creating an _instance_ that references the class. Both classes and instances are committed to in dedicated indexed trees in the global state, and are created via a call to a canonical class registry or instance deployer contract respectively.

import DocCardList from '@theme/DocCardList';

<DocCardList />
37 changes: 37 additions & 0 deletions yellow-paper/docs/contract-deployment/instances.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
# Contract instances

A contract instance is a concrete deployment of a [contract class](./classes.md). A contract instance has state, both private and public, as well as an address that acts as identifier, and can be called into. A contract instance always references a contract class, that dictates what code it executes when called.

## Structure

The structure of a contract instance is defined as:

| Field | Type | Description |
|----------|----------|----------|
| version | u8 | Version identifier. Initially one, bumped for any changes to the contract instance struct. |
| deployer_address | AztecAddress | Address of the canonical contract used for deploying this instance. |
| salt | Field | User-generated pseudorandom value for uniqueness. |
| contract_class_id | Field | Identifier of the contract class for this instance. |
| contract_args_hash | Field | Hash of the arguments to the constructor. |
| portal_contract_address | EthereumAddress | Optional address of the L1 portal contract. |
| public_keys | PublicKeys | Optional struct of public keys used for encryption and nullifying by this contract. |

<!-- TODO: Define the structure of public_keys -->

## Deployment

A new contract instance can be created by calling a private `deploy` function in a canonical `Deployer` contract. This function receives a `ContractInstance` struct as described above, and executes the following actions:

<!-- TODO: Should deploy be private or public? Or both? -->

- Validates the referenced `contract_class_id` exists.
- Computes the resulting contract address from the hash of the contract instance.
- Emits the address in the `new_contract_instances` public inputs.
- Calls the constructor with the preimage of the supplied `contract_args_hash`.
- Emits an unencrypted event with the address and its preimage.

The kernel circuit validates that the contract emitting the `new_contract_instances` is the canonical `Deployer`, and emits those values so they can be added to the global contract instances tree. Upon seeing a new contract deployed event, nodes are expected to store the address and preimage.

<!-- TODO: Define how address is computed -->
<!-- TODO: Define what info is emitted -->
<!-- TODO: Define the format of the unencrypted event -->
12 changes: 12 additions & 0 deletions yellow-paper/docs/state/contracts_trees.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
# Contracts Trees

Global state includes two trees for contracts: a [_contract classes_](../contract-deployment/classes.md) tree and a [_contract instances_](../contract-deployment/instances.md) tree. These trees store contract class identifiers and contract instance addresses respectively. Both trees are implemented as [indexed Merkle trees](./tree_impls.md#indexed-merkle-trees) in order to support non-membership proofs:

- A public call to an undeployed contract instance needs to provably fail.
- A re-deployment of the same instance must provably fail.
- A public deployment of an instance with an unregistered contract class need to provably fail.
- A delegate call to an unregistered class must provably fail.

<!-- NOTE: We should be able to turn the contract class into an append-only merkle tree if we don't include delegate calls nor public contract deployments. -->

The identifiers for new classes and instances are emitted by canonical application contracts, which are validated and re-emitted by the kernel circuit. Uniqueness in their respective trees is checked on insertion.
2 changes: 1 addition & 1 deletion yellow-paper/docs/state/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ title: State

# State

Global state in the Aztec Network is represented by a set of Merkle trees: the [Note Hash tree](./note_hash_tree.md), [Nullifier tree](./nullifier_tree.md), and [Public Data tree](./public_data_tree.md) reflect the latest state of the chain.
Global state in the Aztec Network is represented by a set of Merkle trees: the [Note Hash tree](./note_hash_tree.md), [Nullifier tree](./nullifier_tree.md), [Public Data tree](./public_data_tree.md), and [Contracts trees](./contracts_trees.md) reflect the latest state of the chain.

Merkle trees are either [append-only](./tree_impls.md#append-only-merkle-trees), for storing immutable data, or [indexed](./tree_impls.md#indexed-merkle-trees), for storing data that requires proofs of non-membership.

Expand Down

0 comments on commit 2a98685

Please sign in to comment.