From cb226812626642481423e871e8e2eb84214710ee Mon Sep 17 00:00:00 2001 From: dbanks12 Date: Fri, 5 Jan 2024 18:16:30 -0500 Subject: [PATCH 1/2] docs(yellowpaper): first draft of avm circuit memory --- .../docs/public-vm/InstructionSet.mdx | 4 ++ yellow-paper/docs/public-vm/avm-circuit.md | 40 +++++++++++++++++++ 2 files changed, 44 insertions(+) create mode 100644 yellow-paper/docs/public-vm/avm-circuit.md diff --git a/yellow-paper/docs/public-vm/InstructionSet.mdx b/yellow-paper/docs/public-vm/InstructionSet.mdx index 141e435dd25c..8cc928d178ea 100644 --- a/yellow-paper/docs/public-vm/InstructionSet.mdx +++ b/yellow-paper/docs/public-vm/InstructionSet.mdx @@ -1,3 +1,7 @@ +--- +sidebar_position: 2 +--- + # Instruction Set import GeneratedInstructionSet from './gen/_InstructionSet.mdx'; diff --git a/yellow-paper/docs/public-vm/avm-circuit.md b/yellow-paper/docs/public-vm/avm-circuit.md new file mode 100644 index 000000000000..513593c3cf21 --- /dev/null +++ b/yellow-paper/docs/public-vm/avm-circuit.md @@ -0,0 +1,40 @@ +--- +sidebar_position: 1 +--- + +# AVM Circuit + +## Memory +**VM memory** refers to the VM circuit's top-level memory structure. Since the VM circuit executes an entire public execution request, VM memory contains a dedicated region for each of a request's message calls. + +**Call memory** refers to a message call's dedicated memory region. Each call's memory region is of the same size `CALL_MEMORY_SIZE` and starts at offset `callMemoryOffset = callPointer * CALL_MEMORY_SIZE`. + +### Protected memory and user memory +A call's memory is further divided into protected memory and user memory subregions. + +**Protected memory** refers to the subsection of a call's memory region that is not explicitly addressable by user code. Dedicated instructions (like `ADDRESS`, or `JUMP`) can read or write protected memory, but when user code performs an explicit memory access like `M[offset]`, it will never touch protected memory. + +**User memory** (otherwise known as `MachineState.memory`) is explicitly addressable by user code. Explicit memory accesses like `M[offset]` performed by user code are accesses of user memory. + +#### Protected memory offsets +Protected memory lives at the start of a call's memory region (`callProtectedMemoryOffset = callMemoryOffset + 0`). Protected memory for any call has size `PROTECTED_MEMORY_SIZE`. + +A call's `ExecutionEnvironment` and `MachineState` (except for `MachineState.memory`) reside in protected memory, and so each of their members has a dedicated offset. These offsets are referred to according to the following pattern: +- `ENVIRONMENT_ADDRESS_OFFSET`: offset to `ExecutionEnvironment.address` within a call's protected memory subregion +- `ENVIRONMENT_L1GASPRICE`: offset to `ExecutionEnvironment.l1GasPrice` within a call's protected memory subregion +- `MACHINESTATE_L1GASLEFT`: offset to `MachineState.l1GasLeft` within a call's protected memory subregion +- `MACHINESTATE_PC`: offset to `MachineState.pc` within a call's protected memory subregion +- `MACHINESTATE_INTERNALCALLSTACK`: offset to `MachineState.internalCallStack` within a call's protected memory subregion + +> Note: A call's `ExecutionEnvironment.bytecode` and `ExecutionEnvironment.calldata` are not included in the protected memory region because they are handled in a special manner. This will be expanded on in a later section. +> For complete definitions of `ExecutionEnvironment` and `MachineState` see the [AVM's high level specification](./avm.md). + +#### User memory offsets +User memory comes after protected memory in a call's region (`callUserMemoryOffset = callMemoryOffset + PROTECTED_MEMORY_SIZE`). User memory for any call has size `USER_MEMORY_SIZE`. + +A call's user memory is its `MachineState.memory`. When an instruction makes an explicit memory access like `M[userOffset]` (which can also be expressed as `MachineState.memory[userOffset]`), the user code offset translates to an internal memory offset of `userOffset + callUserMemoryOffset`. + +#### Protected memory and user memory examples +An instruction like `ADDRESS` serves as great example because it performs a read from protected memory and a write to user memory: `M[dstOffset] = ExecutionEnvironment.address` (see [Instruction Set](./InstructionSet) for more details). Below, this operation is deconstructed into its two memory accesses: +1. `ExecutionEnvironment.address`: a read from VM memory offset `ENVIRONMENT_ADDRESS_OFFSET + callProtectedMemoryOffset` +1. `M[dstOffset] =`: a write to `dstOffset + callUserMemoryOffset` From ce5b3ac98e1b9267ae4ac6c159c9548ef83c6885 Mon Sep 17 00:00:00 2001 From: dbanks12 Date: Mon, 8 Jan 2024 18:43:30 +0000 Subject: [PATCH 2/2] rework memory section --- yellow-paper/docs/public-vm/avm-circuit.md | 47 ++++++++++++++-------- 1 file changed, 30 insertions(+), 17 deletions(-) diff --git a/yellow-paper/docs/public-vm/avm-circuit.md b/yellow-paper/docs/public-vm/avm-circuit.md index 513593c3cf21..e3c1130e0f10 100644 --- a/yellow-paper/docs/public-vm/avm-circuit.md +++ b/yellow-paper/docs/public-vm/avm-circuit.md @@ -5,21 +5,33 @@ sidebar_position: 1 # AVM Circuit ## Memory -**VM memory** refers to the VM circuit's top-level memory structure. Since the VM circuit executes an entire public execution request, VM memory contains a dedicated region for each of a request's message calls. +To process a public execution request, the AVM executes the request's initial message call along with any nested calls it encounters. Execution of a message call requires some context including an `ExecutionEnvironment` and `MachineState`. Separate instances of these constructs must exist for each message call. -**Call memory** refers to a message call's dedicated memory region. Each call's memory region is of the same size `CALL_MEMORY_SIZE` and starts at offset `callMemoryOffset = callPointer * CALL_MEMORY_SIZE`. +AVM instructions may read from or write to these constructs (explicitly or indirectly), and therefore it is natural to represent them in the AVM circuit via a memory table. Since each call must have its own `ExecutionEnvironment` and `MachineState`, each entry in the memory table must specify which call it corresponds to. This is accomplished via a `callPointer` column. The memory table is sorted first by `callPointer` and thus all memory accesses for a given message call are grouped. -### Protected memory and user memory -A call's memory is further divided into protected memory and user memory subregions. +User code has explicit access to a construct known as **user memory**, also known as `MachineState.memory`. When an AVM instruction performs an access like `M[offset]`, it is accessing user memory. -**Protected memory** refers to the subsection of a call's memory region that is not explicitly addressable by user code. Dedicated instructions (like `ADDRESS`, or `JUMP`) can read or write protected memory, but when user code performs an explicit memory access like `M[offset]`, it will never touch protected memory. +The remainder of a call's `ExecutionEnvironment` and `MachineState` is not explicitly addressable by user code. This remaining context lives in a construct known as **protected memory** and is accessible only via dedicated instructions (like `ADDRESS`, `JUMP`, `CALL`, etc...). -**User memory** (otherwise known as `MachineState.memory`) is explicitly addressable by user code. Explicit memory accesses like `M[offset]` performed by user code are accesses of user memory. +> Note: the fact that this context is implemented as protected circuit memory is not relevant to user code or even to the high-level AVM specification. -#### Protected memory offsets -Protected memory lives at the start of a call's memory region (`callProtectedMemoryOffset = callMemoryOffset + 0`). Protected memory for any call has size `PROTECTED_MEMORY_SIZE`. +Therefore, for a given call the VM circuit's memory table is subdivided into user and protected memories. This is accomplished via a `userMemory` column which flags each of a call's memory table entries as either a user or protected memory access. -A call's `ExecutionEnvironment` and `MachineState` (except for `MachineState.memory`) reside in protected memory, and so each of their members has a dedicated offset. These offsets are referred to according to the following pattern: +The VM circuit's memory is sorted first by `callPointer` and next by the `userMemory` flag (before standard sorting by memory address, timestamp, etc...). Thus, the memory table is organized as follows: +- VM circuit memory + - call `0` memory + - protected memory + - user memory + - call `1` memory + - protected memory + - user memory + - ... + - call `n-1` memory + - protected memory + - user memory + +### Protected memory offsets +As mentioned above, a call's `ExecutionEnvironment` and `MachineState` (except for `MachineState.memory`) reside in protected memory, and so each of their members has a dedicated offset. These offsets are referred to according to the following pattern: - `ENVIRONMENT_ADDRESS_OFFSET`: offset to `ExecutionEnvironment.address` within a call's protected memory subregion - `ENVIRONMENT_L1GASPRICE`: offset to `ExecutionEnvironment.l1GasPrice` within a call's protected memory subregion - `MACHINESTATE_L1GASLEFT`: offset to `MachineState.l1GasLeft` within a call's protected memory subregion @@ -29,12 +41,13 @@ A call's `ExecutionEnvironment` and `MachineState` (except for `MachineState.mem > Note: A call's `ExecutionEnvironment.bytecode` and `ExecutionEnvironment.calldata` are not included in the protected memory region because they are handled in a special manner. This will be expanded on in a later section. > For complete definitions of `ExecutionEnvironment` and `MachineState` see the [AVM's high level specification](./avm.md). -#### User memory offsets -User memory comes after protected memory in a call's region (`callUserMemoryOffset = callMemoryOffset + PROTECTED_MEMORY_SIZE`). User memory for any call has size `USER_MEMORY_SIZE`. - -A call's user memory is its `MachineState.memory`. When an instruction makes an explicit memory access like `M[userOffset]` (which can also be expressed as `MachineState.memory[userOffset]`), the user code offset translates to an internal memory offset of `userOffset + callUserMemoryOffset`. - -#### Protected memory and user memory examples +### Protected memory and user memory examples An instruction like `ADDRESS` serves as great example because it performs a read from protected memory and a write to user memory: `M[dstOffset] = ExecutionEnvironment.address` (see [Instruction Set](./InstructionSet) for more details). Below, this operation is deconstructed into its two memory accesses: -1. `ExecutionEnvironment.address`: a read from VM memory offset `ENVIRONMENT_ADDRESS_OFFSET + callProtectedMemoryOffset` -1. `M[dstOffset] =`: a write to `dstOffset + callUserMemoryOffset` +1. `ExecutionEnvironment.address` + - memory read + - flags: `callPointer`, `userMemory = 0` (protected memory access) + - offset: `ENVIRONMENT_ADDRESS_OFFSET` +1. `M[dstOffset] =` + - memory write + - flags: `callPointer`, `userMemory = 1` (user memory access) + - offset: `dstOffset`