-
Notifications
You must be signed in to change notification settings - Fork 266
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
docs(yellow-paper): Sync, enqueued, and static calls
- Loading branch information
1 parent
32881a4
commit 6d1e931
Showing
5 changed files
with
78 additions
and
8 deletions.
There are no files selected for viewing
This file was deleted.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
--- | ||
sidebar_position: 2 | ||
--- | ||
# Enqueued calls | ||
|
||
Calls from private functions to public functions are asynchronous. Since private and public functions are executed in different domains at different times and in different contexts, as the former are run by the user on a PXE and the latter by the sequencer, it is not possible for a private function to call a public one and await its result. Instead, private functions can _enqueue_ public function calls. | ||
|
||
The process is analogous to [synchronous calls](./sync-calls.md), but rely on an `enqueuePublicFunctionCall` oracle call that accepts the same arguments. The returned object by the enqueue call is a `PublicCallStackItem` with a flag `is_execution_request` set and empty side effects, to reflect that the stack item has not been executed yet. As with synchronous calls, the caller is responsible for validating the function and arguments in the call stack item, and to push its hash to its public call stack, which represents the list of enqueued public function calls. | ||
|
||
As the transaction is received by the sequencer, the public kernel circuit begins processing the enqueued public function calls from the transaction public call stack, pushing new recursive calls as needed, until the public call stack is empty, as described in the [synchronous calls](./sync-calls.md) section. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
--- | ||
title: Calls | ||
--- | ||
|
||
# Calls | ||
|
||
Functions in the Aztec Network can call other functions. These calls are [synchronous](./sync-calls.md) when they they occur within private functions or within public functions, but are [enqueued](./enqueued-calls.md) when done from a private to a public function. The protocol also supports alternate call methods, such as static calls. | ||
|
||
In addition to function calls, the protocol allows for communication via message-passing back-and-forth between L1 and L2, as well as from public to private functions. | ||
|
||
import DocCardList from '@theme/DocCardList'; | ||
|
||
<DocCardList /> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,20 @@ | ||
--- | ||
sidebar_position: 3 | ||
--- | ||
# Static calls | ||
|
||
[Synchronous calls](./sync-calls.md), both private and public, can be executed as _static_ calls. This means that the called function, and all nested calls within, cannot emit any modifying side effects, such as creating or consuming notes, writing to storage, or emitting events. The purpose of a static call is to query another contract while ensuring that the call will not modify state. Static calls are based on [EIP214](https://eips.ethereum.org/EIPS/eip-214). | ||
|
||
In particular, the following fields of the returned `CallStackItem` must be zero or empty in a static call: | ||
- `new_commitments` | ||
- `new_nullifiers` | ||
- `nullified_commitments` | ||
- `new_l2_to_l1_msgs` | ||
- `encrypted_logs_hash` | ||
- `unencrypted_logs_hash` | ||
- `encrypted_log_preimages_length` | ||
- `unencrypted_log_preimages_length` | ||
|
||
At the protocol level, a static call is identified by a `is_static_call` flag in the `CircuitPublicInputs` of the `CallStackItem`. The kernel is responsible for asserting that the call and all nested calls do not emit any forbidden side effects. | ||
|
||
At the contract level, a caller can initiate a static call via a `staticCallPrivateFunction` or `staticCallPublicFunction` oracle call. The caller is responsible for asserting that the returned `CallStackItem` has the `is_static_call` flag correctly set. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,35 @@ | ||
--- | ||
sidebar_position: 1 | ||
--- | ||
# Synchronous calls | ||
|
||
Calls from a private function to another private function, as well as calls from a public function to another public function, are *synchronous*. When a synchronous function call is found during execution, execution jumps to the target of the call, and returns to the caller with a return value from the function called. This allows easy composability across contracts. | ||
|
||
At the protocol level, each call is represented as a `CallStackItem`, which includes the contract address and function being called, as well as the public inputs `PrivateCircuitPublicInputs` or `PublicCircuitPublicInputs` that are outputted by the execution of the called function. These public inputs include information on the call context, the side effects of the execution, and the block header. | ||
|
||
At the contract level, a call is executed via an oracle call `callPrivateFunction` or `callPublicFunction`, both of which accept the contract address to call, the function selector, and a hash of the arguments. The oracle call prompts the executor to pause the current frame, jump to the target of the call, and return its result. The result is a `CallStackItem` that represents the nested execution. | ||
|
||
The caller is responsible for asserting that the function and arguments in the returned `CallStackItem` match the requested ones, otherwise a malicious oracle could return a `CallStackItem` for a different execution. The caller must also push the hash of the returned `CallStackItem` into the private or public call stack of the current execution context, which is returned as part of the `CircuitPublicInputs` output. The end result is a top-level entrypoint `CallStackItem`, with a stack of nested call stack items to process. | ||
|
||
The kernel circuit is then responsible for iteratively processing each `CallStackItem`, pushing new items into the stack as it encounters nested calls, until the stack is empty. The private kernel circuit processes private function calls locally in the PXE, whereas the public kernel circuit processes public function calls on the sequencer. | ||
|
||
The private kernel circuit iterations begin with the entrypoint execution, empty output and proof. The public kernel circuit starts with the public call stack in the transaction object, and builds on top of the output and proof of the private kernel circuit. | ||
|
||
``` | ||
let call_stack, kernel_public_inputs, proof | ||
if is_private(): | ||
call_stack = [top_level_execution] | ||
kernel_public_inputs = empty_inputs | ||
proof = empty_proof | ||
else: | ||
call_stack = tx.public_call_stack | ||
kernel_public_inputs = tx.kernel_public_inputs | ||
proof = tx.proof | ||
while call_stack is not empty: | ||
let call_stack_item = call_stack.pop() | ||
call_stack.push(...call_stack_item.call_stack) | ||
kernel_public_inputs, proof = kernel_circuit(call_stack_item, kernel_public_inputs, proof) | ||
``` | ||
|
||
The kernel circuit asserts that nested functions and their side effects are processed in order, and that the hash of each nested execution matches the corresponding hash outputted in the call stack by each `CircuitPublicInputs`. |