diff --git a/.gitignore b/.gitignore index ddccb49e24c..a1d1f434282 100644 --- a/.gitignore +++ b/.gitignore @@ -31,4 +31,7 @@ tmux-client-*.log .supermavenignore # parallel -joblog.txt \ No newline at end of file +joblog.txt + +docs/.yarn/install-state.gz +docs/docs/protocol-specs/public-vm/gen/ \ No newline at end of file diff --git a/docs/.yarn/install-state.gz b/docs/.yarn/install-state.gz deleted file mode 100644 index decac804d7d..00000000000 Binary files a/docs/.yarn/install-state.gz and /dev/null differ diff --git a/docs/docs/aztec/concepts/pxe/index.md b/docs/docs/aztec/concepts/pxe/index.md index c82f3bcc97f..4b1d0eea33b 100644 --- a/docs/docs/aztec/concepts/pxe/index.md +++ b/docs/docs/aztec/concepts/pxe/index.md @@ -109,4 +109,4 @@ Oracles are pieces of data that are injected into a smart contract function from To learn how to develop on top of the PXE, refer to these guides: - [Run more than one PXE on your local machine](../../../developers/guides/local_env/run_more_than_one_pxe_sandbox.md) -- [Use in-built oracles including oracles for arbitrary data](../../../developers/guides/smart_contracts/writing_contracts/how_to_pop_capsules.md) +- [Use in-built oracles including oracles for arbitrary data](../../../developers/guides/smart_contracts/writing_contracts/how_to_use_capsules.md) diff --git a/docs/docs/aztec/smart_contracts/oracles/index.md b/docs/docs/aztec/smart_contracts/oracles/index.md index c12afb6325c..60ef28b6610 100644 --- a/docs/docs/aztec/smart_contracts/oracles/index.md +++ b/docs/docs/aztec/smart_contracts/oracles/index.md @@ -5,7 +5,7 @@ tags: [functions, oracles] This page goes over what oracles are in Aztec and how they work. -Looking for a hands-on guide? You can learn how to use oracles in a smart contract [here](../../../developers/guides/smart_contracts/writing_contracts/how_to_pop_capsules.md). +Looking for a hands-on guide? You can learn how to use oracles in a smart contract [here](../../../developers/guides/smart_contracts/writing_contracts/how_to_use_capsules.md). An oracle is something that allows us to get data from the outside world into our contracts. The most widely-known types of oracles in blockchain systems are probably Chainlink price feeds, which allow us to get the price of an asset in USD taking non-blockchain data into account. @@ -31,4 +31,4 @@ Oracles introduce **non-determinism** into a circuit, and thus are `unconstraine Find a full list [on GitHub](https://github.com/AztecProtocol/aztec-packages/tree/master/noir-projects/aztec-nr/aztec/src/oracle). -Please note that it is **not** possible to write a custom oracle for your dapp. Oracles are implemented in the PXE, so all users of your dapp would have to use a PXE service with your custom oracle included. If you want to inject some arbitrary data that does not have a dedicated oracle, you can use [popCapsule](../../../developers/guides/smart_contracts/writing_contracts/how_to_pop_capsules.md). +Please note that it is **not** possible to write a custom oracle for your dapp. Oracles are implemented in the PXE, so all users of your dapp would have to use a PXE service with your custom oracle included. If you want to inject some arbitrary data that does not have a dedicated oracle, you can use [capsules](../../../developers/guides/smart_contracts/writing_contracts/how_to_use_capsules.md). diff --git a/docs/docs/developers/guides/smart_contracts/writing_contracts/how_to_pop_capsules.md b/docs/docs/developers/guides/smart_contracts/writing_contracts/how_to_pop_capsules.md deleted file mode 100644 index 6f7f60ae8fc..00000000000 --- a/docs/docs/developers/guides/smart_contracts/writing_contracts/how_to_pop_capsules.md +++ /dev/null @@ -1,19 +0,0 @@ ---- -title: Using the popCapsule Oracle -sidebar_position: 5 -tags: [functions, oracles] ---- - -`popCapsule` is used for passing arbitrary data. We have not yet included this in Aztec.nr, so it is a bit more complex than the other oracles. You can follow this how-to: - -### 1. Import capsules into your smart contract - -If it lies in the same directory as your smart contract, you can import it like this: - -#include_code import_pop_capsule noir-projects/noir-contracts/contracts/contract_class_registerer_contract/src/main.nr rust - -### 2. Use it as any other oracle - -Now it becomes a regular oracle you can call like this: - -#include_code pop_capsule noir-projects/noir-contracts/contracts/contract_class_registerer_contract/src/main.nr rust diff --git a/docs/docs/developers/guides/smart_contracts/writing_contracts/how_to_use_capsules.md b/docs/docs/developers/guides/smart_contracts/writing_contracts/how_to_use_capsules.md new file mode 100644 index 00000000000..f5a56036f75 --- /dev/null +++ b/docs/docs/developers/guides/smart_contracts/writing_contracts/how_to_use_capsules.md @@ -0,0 +1,54 @@ +--- +title: Using Capsules +sidebar_position: 5 +tags: [functions, oracles] +--- + +Capsules are a per-contract non-volatile database. +It can be used for storing arbitrary data that can be retrieved later. +The data is stored locally in PXE and it is scoped per contract address, so external contracts cannot access it. +The capsule (data stored under a storage slot in the capsules database) persists until explicitly deleted with `delete`. + +The capsules module provides these main functions: + +- `store` - Stores arbitrary data at a slot, overwriting any existing data +- `load` - Retrieves previously stored data from a slot +- `delete` - Deletes data at a slot +- `copy` - Efficiently copies contiguous entries between slots + +### 1. Import capsules into your smart contract + +Import the capsules module: + +#include_code import_capsules noir-projects/noir-contracts/contracts/contract_class_registerer_contract/src/main.nr rust + +### 2. Store and load data + +You can store any type that implements `Serialize` and `Deserialize`: + +#include_code load_capsule noir-projects/noir-contracts/contracts/contract_class_registerer_contract/src/main.nr rust + +The data is stored per contract address and slot. When loading, you'll get back an `Option` - `None` if no data exists at that slot. + +### 3. Copying data + +You can use `copy` to move contiguous entries between slots without repeated loads and stores. +This supports overlapping source and destination regions. + +Note that all values are scoped per contract address, so external contracts cannot access them. + +### 4. Using CapsuleArray + +The `CapsuleArray` type provides a dynamically sized array backed by capsules. +It handles the storage layout and management automatically. +The array stores its length at a base slot, with elements stored in consecutive slots after it. + +Key functions: + +- `at(contract_address, base_slot)` - Creates/connects to an array at the given base slot +- `len()` - Returns the number of elements in the array +- `push(value)` - Appends a value to the end of the array +- `get(index)` - Retrieves the value at the given index +- `remove(index)` - Removes an element, shifting subsequent elements to maintain contiguous storage + + \ No newline at end of file diff --git a/docs/docs/protocol-specs/public-vm/gen/_instruction-set.mdx b/docs/docs/protocol-specs/public-vm/gen/_instruction-set.mdx deleted file mode 100644 index c0855861896..00000000000 --- a/docs/docs/protocol-specs/public-vm/gen/_instruction-set.mdx +++ /dev/null @@ -1,1604 +0,0 @@ -[comment]: # (THIS IS A GENERATED FILE! DO NOT EDIT!) -[comment]: # (Generated via `yarn preprocess`) - -[comment]: # (Generated by genMarkdown.js, InstructionSet.js, InstructionSize.js) - -import Markdown from 'react-markdown' -import CodeBlock from '@theme/CodeBlock' - - -## Instructions Table - -Click on an instruction name to jump to its section. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
OpcodeNameSummaryExpression
0x00\[\`ADD\`\](#isa-section-add)Addition (a + b){ - `M[dstOffset] = M[aOffset] + M[bOffset] mod 2^k` - }
0x01\[\`SUB\`\](#isa-section-sub)Subtraction (a - b){ - `M[dstOffset] = M[aOffset] - M[bOffset] mod 2^k` - }
0x02\[\`MUL\`\](#isa-section-mul)Multiplication (a * b){ - `M[dstOffset] = M[aOffset] * M[bOffset] mod 2^k` - }
0x03\[\`DIV\`\](#isa-section-div)Unsigned integer division (a / b){ - `M[dstOffset] = M[aOffset] / M[bOffset]` - }
0x04\[\`FDIV\`\](#isa-section-fdiv)Field division (a / b){ - `M[dstOffset] = M[aOffset] / M[bOffset]` - }
0x05\[\`EQ\`\](#isa-section-eq)Equality check (a == b){ - `M[dstOffset] = M[aOffset] == M[bOffset] ? 1 : 0` - }
0x06\[\`LT\`\](#isa-section-lt)Less-than check (a < b){ - `M[dstOffset] = M[aOffset] < M[bOffset] ? 1 : 0` - }
0x07\[\`LTE\`\](#isa-section-lte)Less-than-or-equals check (a <= b){ - `M[dstOffset] = M[aOffset] <= M[bOffset] ? 1 : 0` - }
0x08\[\`AND\`\](#isa-section-and)Bitwise AND (a & b){ - `M[dstOffset] = M[aOffset] AND M[bOffset]` - }
0x09\[\`OR\`\](#isa-section-or)Bitwise OR (a | b){ - `M[dstOffset] = M[aOffset] OR M[bOffset]` - }
0x0a\[\`XOR\`\](#isa-section-xor)Bitwise XOR (a ^ b){ - `M[dstOffset] = M[aOffset] XOR M[bOffset]` - }
0x0b\[\`NOT\`\](#isa-section-not)Bitwise NOT (inversion){ - `M[dstOffset] = NOT M[aOffset]` - }
0x0c\[\`SHL\`\](#isa-section-shl)Bitwise leftward shift (a << b){ - `M[dstOffset] = M[aOffset] << M[bOffset]` - }
0x0d\[\`SHR\`\](#isa-section-shr)Bitwise rightward shift (a >> b){ - `M[dstOffset] = M[aOffset] >> M[bOffset]` - }
0x0e\[\`CAST\`\](#isa-section-cast)Type cast{ - `M[dstOffset] = cast(M[aOffset])` - }
0x0f\[\`ADDRESS\`\](#isa-section-address)Get the address of the currently executing l2 contract{ - `M[dstOffset] = context.environment.address` - }
0x10\[\`SENDER\`\](#isa-section-sender)Get the address of the sender (caller of the current context){ - `M[dstOffset] = context.environment.sender` - }
0x11\[\`TRANSACTIONFEE\`\](#isa-section-transactionfee)Get the computed transaction fee during teardown phase, zero otherwise{ - `M[dstOffset] = context.environment.transactionFee` - }
0x12\[\`CHAINID\`\](#isa-section-chainid)Get this rollup's L1 chain ID{ - `M[dstOffset] = context.environment.globals.chainId` - }
0x13\[\`VERSION\`\](#isa-section-version)Get this rollup's L2 version ID{ - `M[dstOffset] = context.environment.globals.version` - }
0x14\[\`BLOCKNUMBER\`\](#isa-section-blocknumber)Get this L2 block's number{ - `M[dstOffset] = context.environment.globals.blocknumber` - }
0x15\[\`TIMESTAMP\`\](#isa-section-timestamp)Get this L2 block's timestamp{ - `M[dstOffset] = context.environment.globals.timestamp` - }
0x16\[\`FEEPERL2GAS\`\](#isa-section-feeperl2gas)Get the fee to be paid per "L2 gas" - constant for entire transaction{ - `M[dstOffset] = context.environment.globals.feePerL2Gas` - }
0x17\[\`FEEPERDAGAS\`\](#isa-section-feeperdagas)Get the fee to be paid per "DA gas" - constant for entire transaction{ - `M[dstOffset] = context.environment.globals.feePerDaGas` - }
0x18\[\`CALLDATACOPY\`\](#isa-section-calldatacopy)Copy calldata into memory{ - `M[dstOffset:dstOffset+copySize] = context.environment.calldata[cdOffset:cdOffset+copySize]` - }
0x19\[\`L2GASLEFT\`\](#isa-section-l2gasleft)Remaining "L2 gas" for this call (after this instruction){ - `M[dstOffset] = context.MachineState.l2GasLeft` - }
0x1a\[\`DAGASLEFT\`\](#isa-section-dagasleft)Remaining "DA gas" for this call (after this instruction){ - `M[dstOffset] = context.machineState.daGasLeft` - }
0x1b\[\`JUMP\`\](#isa-section-jump)Jump to a location in the bytecode{ - `context.machineState.pc = loc` - }
0x1c\[\`JUMPI\`\](#isa-section-jumpi)Conditionally jump to a location in the bytecode{ - `context.machineState.pc = M[condOffset] > 0 ? loc : context.machineState.pc` - }
0x1d\[\`INTERNALCALL\`\](#isa-section-internalcall)Make an internal call. Push the current PC to the internal call stack and jump to the target location. -{`context.machineState.internalCallStack.push(context.machineState.pc) -context.machineState.pc = loc`} -
0x1e\[\`INTERNALRETURN\`\](#isa-section-internalreturn)Return from an internal call. Pop from the internal call stack and jump to the popped location.{ - `context.machineState.pc = context.machineState.internalCallStack.pop()` - }
0x1f\[\`SET\`\](#isa-section-set)Set a memory word from a constant in the bytecode{ - `M[dstOffset] = const` - }
0x20\[\`MOV\`\](#isa-section-mov)Move a word from source memory location to destination{ - `M[dstOffset] = M[srcOffset]` - }
0x21\[\`CMOV\`\](#isa-section-cmov)Move a word (conditionally chosen) from one memory location to another (`d = cond > 0 ? a : b`){ - `M[dstOffset] = M[condOffset] > 0 ? M[aOffset] : M[bOffset]` - }
0x22\[\`SLOAD\`\](#isa-section-sload)Load a word from this contract's persistent public storage. Zero is loaded for unwritten slots. -{`M[dstOffset] = S[M[slotOffset]]`} -
0x23\[\`SSTORE\`\](#isa-section-sstore)Write a word to this contract's persistent public storage -{`S[M[slotOffset]] = M[srcOffset]`} -
0x24\[\`NOTEHASHEXISTS\`\](#isa-section-notehashexists)Check whether a note hash exists in the note hash tree (as of the start of the current block) -{`exists = context.worldState.noteHashes.has({ - leafIndex: M[leafIndexOffset] - leaf: hash(context.environment.address, M[noteHashOffset]), -}) -M[existsOffset] = exists`} -
0x25\[\`EMITNOTEHASH\`\](#isa-section-emitnotehash)Emit a new note hash to be inserted into the note hash tree -{`context.worldState.noteHashes.append( - hash(context.environment.address, M[noteHashOffset]) -)`} -
0x26\[\`NULLIFIEREXISTS\`\](#isa-section-nullifierexists)Check whether a nullifier exists in the nullifier tree (including nullifiers from earlier in the current transaction or from earlier in the current block) -{`exists = pendingNullifiers.has(M[addressOffset], M[nullifierOffset]) || context.worldState.nullifiers.has( - hash(M[addressOffset], M[nullifierOffset]) -) -M[existsOffset] = exists`} -
0x27\[\`EMITNULLIFIER\`\](#isa-section-emitnullifier)Emit a new nullifier to be inserted into the nullifier tree -{`context.worldState.nullifiers.append( - hash(context.environment.address, M[nullifierOffset]) -)`} -
0x28\[\`L1TOL2MSGEXISTS\`\](#isa-section-l1tol2msgexists)Check if a message exists in the L1-to-L2 message tree -{`exists = context.worldState.l1ToL2Messages.has({ - leafIndex: M[msgLeafIndexOffset], leaf: M[msgHashOffset] -}) -M[existsOffset] = exists`} -
0x29\[\`GETCONTRACTINSTANCE\`\](#isa-section-getcontractinstance)Copies contract instance data to memory -{`M[dstOffset:dstOffset+CONTRACT_INSTANCE_SIZE+1] = [ - instance_found_in_address, - instance.salt ?? 0, - instance.deployer ?? 0, - instance.contractClassId ?? 0, - instance.initializationHash ?? 0, - instance.portalContractAddress ?? 0, - instance.publicKeysHash ?? 0, -]`} -
0x2a\[\`EMITUNENCRYPTEDLOG\`\](#isa-section-emitunencryptedlog)Emit an unencrypted log -{`context.accruedSubstate.unencryptedLogs.append( - UnencryptedLog { - address: context.environment.address, - log: M[logOffset:logOffset+M[logSizeOffset]], - } -)`} -
0x2b\[\`SENDL2TOL1MSG\`\](#isa-section-sendl2tol1msg)Send an L2-to-L1 message -{`context.accruedSubstate.sentL2ToL1Messages.append( - SentL2ToL1Message { - address: context.environment.address, - recipient: M[recipientOffset], - message: M[contentOffset] - } -)`} -
0x2c\[\`CALL\`\](#isa-section-call)Call into another contract -{`// instr.args are { gasOffset, addrOffset, argsOffset, retOffset, retSize } -chargeGas(context, - l2GasCost=M[instr.args.gasOffset], - daGasCost=M[instr.args.gasOffset+1]) -traceNestedCall(context, instr.args.addrOffset) -nestedContext = deriveContext(context, instr.args, isStaticCall=false) -execute(nestedContext) -updateContextAfterNestedCall(context, instr.args, nestedContext)`} -
0x2d\[\`STATICCALL\`\](#isa-section-staticcall)Call into another contract, disallowing World State and Accrued Substate modifications -{`// instr.args are { gasOffset, addrOffset, argsOffset, retOffset, retSize } -chargeGas(context, - l2GasCost=M[instr.args.gasOffset], - daGasCost=M[instr.args.gasOffset+1]) -traceNestedCall(context, instr.args.addrOffset) -nestedContext = deriveContext(context, instr.args, isStaticCall=true -execute(nestedContext) -updateContextAfterNestedCall(context, instr.args, nestedContext)`} -
0x2e\[\`RETURN\`\](#isa-section-return)Halt execution within this context (without revert), optionally returning some data -{`context.contractCallResults.output = M[retOffset:retOffset+retSize] -halt`} -
0x2f\[\`REVERT\`\](#isa-section-revert)Halt execution within this context as `reverted`, optionally returning some data -{`context.contractCallResults.output = M[retOffset:retOffset+retSize] -context.contractCallResults.reverted = true -halt`} -
0x30\[\`TORADIXLE\`\](#isa-section-to_radix_le)Convert a word to an array of limbs in little-endian radix formTBD: Storage of limbs and if T[dstOffset] is constrained to U8
- - -## Instructions - -### `ADD` -Addition (a + b) - -[See in table.](#isa-table-add) - -- **Opcode**: 0x00 -- **Category**: Compute - Arithmetic -- **Flags**: - - **indirect**: Toggles whether each memory-offset argument is an indirect offset. Rightmost bit corresponds to 0th offset arg, etc. Indirect offsets result in memory accesses like `M[M[offset]]` instead of the more standard `M[offset]`. - - **inTag**: The [tag/size](./memory-model#tags-and-tagged-memory) to check inputs against and tag the destination with. -- **Args**: - - **aOffset**: memory offset of the operation's left input - - **bOffset**: memory offset of the operation's right input - - **dstOffset**: memory offset specifying where to store operation's result -- **Expression**: `M[dstOffset] = M[aOffset] + M[bOffset] mod 2^k` -- **Details**: Wraps on overflow -- **Tag checks**: `T[aOffset] == T[bOffset] == inTag` -- **Tag updates**: `T[dstOffset] = inTag` -- **Bit-size**: 128 - -[![](/img/protocol-specs/public-vm/bit-formats/ADD.png)](/img/protocol-specs/public-vm/bit-formats/ADD.png) - -### `SUB` -Subtraction (a - b) - -[See in table.](#isa-table-sub) - -- **Opcode**: 0x01 -- **Category**: Compute - Arithmetic -- **Flags**: - - **indirect**: Toggles whether each memory-offset argument is an indirect offset. Rightmost bit corresponds to 0th offset arg, etc. Indirect offsets result in memory accesses like `M[M[offset]]` instead of the more standard `M[offset]`. - - **inTag**: The [tag/size](./memory-model#tags-and-tagged-memory) to check inputs against and tag the destination with. -- **Args**: - - **aOffset**: memory offset of the operation's left input - - **bOffset**: memory offset of the operation's right input - - **dstOffset**: memory offset specifying where to store operation's result -- **Expression**: `M[dstOffset] = M[aOffset] - M[bOffset] mod 2^k` -- **Details**: Wraps on undeflow -- **Tag checks**: `T[aOffset] == T[bOffset] == inTag` -- **Tag updates**: `T[dstOffset] = inTag` -- **Bit-size**: 128 - -[![](/img/protocol-specs/public-vm/bit-formats/SUB.png)](/img/protocol-specs/public-vm/bit-formats/SUB.png) - -### `MUL` -Multiplication (a * b) - -[See in table.](#isa-table-mul) - -- **Opcode**: 0x02 -- **Category**: Compute - Arithmetic -- **Flags**: - - **indirect**: Toggles whether each memory-offset argument is an indirect offset. Rightmost bit corresponds to 0th offset arg, etc. Indirect offsets result in memory accesses like `M[M[offset]]` instead of the more standard `M[offset]`. - - **inTag**: The [tag/size](./memory-model#tags-and-tagged-memory) to check inputs against and tag the destination with. -- **Args**: - - **aOffset**: memory offset of the operation's left input - - **bOffset**: memory offset of the operation's right input - - **dstOffset**: memory offset specifying where to store operation's result -- **Expression**: `M[dstOffset] = M[aOffset] * M[bOffset] mod 2^k` -- **Details**: Wraps on overflow -- **Tag checks**: `T[aOffset] == T[bOffset] == inTag` -- **Tag updates**: `T[dstOffset] = inTag` -- **Bit-size**: 128 - -[![](/img/protocol-specs/public-vm/bit-formats/MUL.png)](/img/protocol-specs/public-vm/bit-formats/MUL.png) - -### `DIV` -Unsigned integer division (a / b) - -[See in table.](#isa-table-div) - -- **Opcode**: 0x03 -- **Category**: Compute - Arithmetic -- **Flags**: - - **indirect**: Toggles whether each memory-offset argument is an indirect offset. Rightmost bit corresponds to 0th offset arg, etc. Indirect offsets result in memory accesses like `M[M[offset]]` instead of the more standard `M[offset]`. - - **inTag**: The [tag/size](./memory-model#tags-and-tagged-memory) to check inputs against and tag the destination with. -- **Args**: - - **aOffset**: memory offset of the operation's left input - - **bOffset**: memory offset of the operation's right input - - **dstOffset**: memory offset specifying where to store operation's result -- **Expression**: `M[dstOffset] = M[aOffset] / M[bOffset]` -- **Details**: If the input is a field, it will be interpreted as an integer -- **Tag checks**: `T[aOffset] == T[bOffset] == inTag` -- **Tag updates**: `T[dstOffset] = inTag` -- **Bit-size**: 128 - -[![](/img/protocol-specs/public-vm/bit-formats/DIV.png)](/img/protocol-specs/public-vm/bit-formats/DIV.png) - -### `FDIV` -Field division (a / b) - -[See in table.](#isa-table-fdiv) - -- **Opcode**: 0x04 -- **Category**: Compute - Arithmetic -- **Flags**: - - **indirect**: Toggles whether each memory-offset argument is an indirect offset. Rightmost bit corresponds to 0th offset arg, etc. Indirect offsets result in memory accesses like `M[M[offset]]` instead of the more standard `M[offset]`. -- **Args**: - - **aOffset**: memory offset of the operation's left input - - **bOffset**: memory offset of the operation's right input - - **dstOffset**: memory offset specifying where to store operation's result -- **Expression**: `M[dstOffset] = M[aOffset] / M[bOffset]` -- **Tag checks**: `T[aOffset] == T[bOffset] == field` -- **Tag updates**: `T[dstOffset] = field` -- **Bit-size**: 120 - - -### `EQ` -Equality check (a \=\= b) - -[See in table.](#isa-table-eq) - -- **Opcode**: 0x05 -- **Category**: Compute - Comparators -- **Flags**: - - **indirect**: Toggles whether each memory-offset argument is an indirect offset. Rightmost bit corresponds to 0th offset arg, etc. Indirect offsets result in memory accesses like `M[M[offset]]` instead of the more standard `M[offset]`. - - **inTag**: The [tag/size](./memory-model#tags-and-tagged-memory) to check inputs against and tag the destination with. -- **Args**: - - **aOffset**: memory offset of the operation's left input - - **bOffset**: memory offset of the operation's right input - - **dstOffset**: memory offset specifying where to store operation's result -- **Expression**: `M[dstOffset] = M[aOffset] == M[bOffset] ? 1 : 0` -- **Tag checks**: `T[aOffset] == T[bOffset] == inTag` -- **Tag updates**: `T[dstOffset] = u8` -- **Bit-size**: 128 - -[![](/img/protocol-specs/public-vm/bit-formats/EQ.png)](/img/protocol-specs/public-vm/bit-formats/EQ.png) - -### `LT` -Less-than check (a \< b) - -[See in table.](#isa-table-lt) - -- **Opcode**: 0x06 -- **Category**: Compute - Comparators -- **Flags**: - - **indirect**: Toggles whether each memory-offset argument is an indirect offset. Rightmost bit corresponds to 0th offset arg, etc. Indirect offsets result in memory accesses like `M[M[offset]]` instead of the more standard `M[offset]`. - - **inTag**: The [tag/size](./memory-model#tags-and-tagged-memory) to check inputs against and tag the destination with. -- **Args**: - - **aOffset**: memory offset of the operation's left input - - **bOffset**: memory offset of the operation's right input - - **dstOffset**: memory offset specifying where to store operation's result -- **Expression**: `M[dstOffset] = M[aOffset] < M[bOffset] ? 1 : 0` -- **Tag checks**: `T[aOffset] == T[bOffset] == inTag` -- **Tag updates**: `T[dstOffset] = u8` -- **Bit-size**: 128 - -[![](/img/protocol-specs/public-vm/bit-formats/LT.png)](/img/protocol-specs/public-vm/bit-formats/LT.png) - -### `LTE` -Less-than-or-equals check (a \<\= b) - -[See in table.](#isa-table-lte) - -- **Opcode**: 0x07 -- **Category**: Compute - Comparators -- **Flags**: - - **indirect**: Toggles whether each memory-offset argument is an indirect offset. Rightmost bit corresponds to 0th offset arg, etc. Indirect offsets result in memory accesses like `M[M[offset]]` instead of the more standard `M[offset]`. - - **inTag**: The [tag/size](./memory-model#tags-and-tagged-memory) to check inputs against and tag the destination with. -- **Args**: - - **aOffset**: memory offset of the operation's left input - - **bOffset**: memory offset of the operation's right input - - **dstOffset**: memory offset specifying where to store operation's result -- **Expression**: `M[dstOffset] = M[aOffset] <= M[bOffset] ? 1 : 0` -- **Tag checks**: `T[aOffset] == T[bOffset] == inTag` -- **Tag updates**: `T[dstOffset] = u8` -- **Bit-size**: 128 - -[![](/img/protocol-specs/public-vm/bit-formats/LTE.png)](/img/protocol-specs/public-vm/bit-formats/LTE.png) - -### `AND` -Bitwise AND (a & b) - -[See in table.](#isa-table-and) - -- **Opcode**: 0x08 -- **Category**: Compute - Bitwise -- **Flags**: - - **indirect**: Toggles whether each memory-offset argument is an indirect offset. Rightmost bit corresponds to 0th offset arg, etc. Indirect offsets result in memory accesses like `M[M[offset]]` instead of the more standard `M[offset]`. - - **inTag**: The [tag/size](./memory-model#tags-and-tagged-memory) to check inputs against and tag the destination with. `field` type is NOT supported for this instruction. -- **Args**: - - **aOffset**: memory offset of the operation's left input - - **bOffset**: memory offset of the operation's right input - - **dstOffset**: memory offset specifying where to store operation's result -- **Expression**: `M[dstOffset] = M[aOffset] AND M[bOffset]` -- **Tag checks**: `T[aOffset] == T[bOffset] == inTag` -- **Tag updates**: `T[dstOffset] = inTag` -- **Bit-size**: 128 - -[![](/img/protocol-specs/public-vm/bit-formats/AND.png)](/img/protocol-specs/public-vm/bit-formats/AND.png) - -### `OR` -Bitwise OR (a | b) - -[See in table.](#isa-table-or) - -- **Opcode**: 0x09 -- **Category**: Compute - Bitwise -- **Flags**: - - **indirect**: Toggles whether each memory-offset argument is an indirect offset. Rightmost bit corresponds to 0th offset arg, etc. Indirect offsets result in memory accesses like `M[M[offset]]` instead of the more standard `M[offset]`. - - **inTag**: The [tag/size](./memory-model#tags-and-tagged-memory) to check inputs against and tag the destination with. `field` type is NOT supported for this instruction. -- **Args**: - - **aOffset**: memory offset of the operation's left input - - **bOffset**: memory offset of the operation's right input - - **dstOffset**: memory offset specifying where to store operation's result -- **Expression**: `M[dstOffset] = M[aOffset] OR M[bOffset]` -- **Tag checks**: `T[aOffset] == T[bOffset] == inTag` -- **Tag updates**: `T[dstOffset] = inTag` -- **Bit-size**: 128 - -[![](/img/protocol-specs/public-vm/bit-formats/OR.png)](/img/protocol-specs/public-vm/bit-formats/OR.png) - -### `XOR` -Bitwise XOR (a ^ b) - -[See in table.](#isa-table-xor) - -- **Opcode**: 0x0a -- **Category**: Compute - Bitwise -- **Flags**: - - **indirect**: Toggles whether each memory-offset argument is an indirect offset. Rightmost bit corresponds to 0th offset arg, etc. Indirect offsets result in memory accesses like `M[M[offset]]` instead of the more standard `M[offset]`. - - **inTag**: The [tag/size](./memory-model#tags-and-tagged-memory) to check inputs against and tag the destination with. `field` type is NOT supported for this instruction. -- **Args**: - - **aOffset**: memory offset of the operation's left input - - **bOffset**: memory offset of the operation's right input - - **dstOffset**: memory offset specifying where to store operation's result -- **Expression**: `M[dstOffset] = M[aOffset] XOR M[bOffset]` -- **Tag checks**: `T[aOffset] == T[bOffset] == inTag` -- **Tag updates**: `T[dstOffset] = inTag` -- **Bit-size**: 128 - -[![](/img/protocol-specs/public-vm/bit-formats/XOR.png)](/img/protocol-specs/public-vm/bit-formats/XOR.png) - -### `NOT` -Bitwise NOT (inversion) - -[See in table.](#isa-table-not) - -- **Opcode**: 0x0b -- **Category**: Compute - Bitwise -- **Flags**: - - **indirect**: Toggles whether each memory-offset argument is an indirect offset. Rightmost bit corresponds to 0th offset arg, etc. Indirect offsets result in memory accesses like `M[M[offset]]` instead of the more standard `M[offset]`. - - **inTag**: The [tag/size](./memory-model#tags-and-tagged-memory) to check inputs against and tag the destination with. `field` type is NOT supported for this instruction. -- **Args**: - - **aOffset**: memory offset of the operation's input - - **dstOffset**: memory offset specifying where to store operation's result -- **Expression**: `M[dstOffset] = NOT M[aOffset]` -- **Tag checks**: `T[aOffset] == inTag` -- **Tag updates**: `T[dstOffset] = inTag` -- **Bit-size**: 96 - -[![](/img/protocol-specs/public-vm/bit-formats/NOT.png)](/img/protocol-specs/public-vm/bit-formats/NOT.png) - -### `SHL` -Bitwise leftward shift (a \<\< b) - -[See in table.](#isa-table-shl) - -- **Opcode**: 0x0c -- **Category**: Compute - Bitwise -- **Flags**: - - **indirect**: Toggles whether each memory-offset argument is an indirect offset. Rightmost bit corresponds to 0th offset arg, etc. Indirect offsets result in memory accesses like `M[M[offset]]` instead of the more standard `M[offset]`. - - **inTag**: The [tag/size](./memory-model#tags-and-tagged-memory) to check inputs against and tag the destination with. `field` type is NOT supported for this instruction. -- **Args**: - - **aOffset**: memory offset of the operation's left input - - **bOffset**: memory offset of the operation's right input - - **dstOffset**: memory offset specifying where to store operation's result -- **Expression**: `M[dstOffset] = M[aOffset] << M[bOffset]` -- **Tag checks**: `T[aOffset] == inTag`, `T[bOffset] == u8` -- **Tag updates**: `T[dstOffset] = inTag` -- **Bit-size**: 128 - -[![](/img/protocol-specs/public-vm/bit-formats/SHL.png)](/img/protocol-specs/public-vm/bit-formats/SHL.png) - -### `SHR` -Bitwise rightward shift (a \>\> b) - -[See in table.](#isa-table-shr) - -- **Opcode**: 0x0d -- **Category**: Compute - Bitwise -- **Flags**: - - **indirect**: Toggles whether each memory-offset argument is an indirect offset. Rightmost bit corresponds to 0th offset arg, etc. Indirect offsets result in memory accesses like `M[M[offset]]` instead of the more standard `M[offset]`. - - **inTag**: The [tag/size](./memory-model#tags-and-tagged-memory) to check inputs against and tag the destination with. `field` type is NOT supported for this instruction. -- **Args**: - - **aOffset**: memory offset of the operation's left input - - **bOffset**: memory offset of the operation's right input - - **dstOffset**: memory offset specifying where to store operation's result -- **Expression**: `M[dstOffset] = M[aOffset] >> M[bOffset]` -- **Tag checks**: `T[aOffset] == inTag`, `T[bOffset] == u8` -- **Tag updates**: `T[dstOffset] = inTag` -- **Bit-size**: 128 - -[![](/img/protocol-specs/public-vm/bit-formats/SHR.png)](/img/protocol-specs/public-vm/bit-formats/SHR.png) - -### `CAST` -Type cast - -[See in table.](#isa-table-cast) - -- **Opcode**: 0x0e -- **Category**: Type Conversions -- **Flags**: - - **indirect**: Toggles whether each memory-offset argument is an indirect offset. Rightmost bit corresponds to 0th offset arg, etc. Indirect offsets result in memory accesses like `M[M[offset]]` instead of the more standard `M[offset]`. - - **dstTag**: The [tag/size](./memory-model#tags-and-tagged-memory) to tag the destination with but not to check inputs against. -- **Args**: - - **aOffset**: memory offset of word to cast - - **dstOffset**: memory offset specifying where to store operation's result -- **Expression**: `M[dstOffset] = cast(M[aOffset])` -- **Details**: Cast a word in memory based on the `dstTag` specified in the bytecode. Truncates (`M[dstOffset] = M[aOffset] mod 2^dstsize`) when casting to a smaller type, left-zero-pads when casting to a larger type. See [here](./memory-model#cast-and-tag-conversions) for more details. -- **Tag updates**: `T[dstOffset] = dstTag` -- **Bit-size**: 96 - -[![](/img/protocol-specs/public-vm/bit-formats/CAST.png)](/img/protocol-specs/public-vm/bit-formats/CAST.png) - -### `ADDRESS` -Get the address of the currently executing l2 contract - -[See in table.](#isa-table-address) - -- **Opcode**: 0x0f -- **Category**: Execution Environment -- **Flags**: - - **indirect**: Toggles whether each memory-offset argument is an indirect offset. Rightmost bit corresponds to 0th offset arg, etc. Indirect offsets result in memory accesses like `M[M[offset]]` instead of the more standard `M[offset]`. -- **Args**: - - **dstOffset**: memory offset specifying where to store operation's result -- **Expression**: `M[dstOffset] = context.environment.address` -- **Tag updates**: `T[dstOffset] = field` -- **Bit-size**: 56 - -[![](/img/protocol-specs/public-vm/bit-formats/ADDRESS.png)](/img/protocol-specs/public-vm/bit-formats/ADDRESS.png) - -### `SENDER` -Get the address of the sender (caller of the current context) - -[See in table.](#isa-table-sender) - -- **Opcode**: 0x10 -- **Category**: Execution Environment -- **Flags**: - - **indirect**: Toggles whether each memory-offset argument is an indirect offset. Rightmost bit corresponds to 0th offset arg, etc. Indirect offsets result in memory accesses like `M[M[offset]]` instead of the more standard `M[offset]`. -- **Args**: - - **dstOffset**: memory offset specifying where to store operation's result -- **Expression**: `M[dstOffset] = context.environment.sender` -- **Tag updates**: `T[dstOffset] = field` -- **Bit-size**: 56 - -[![](/img/protocol-specs/public-vm/bit-formats/SENDER.png)](/img/protocol-specs/public-vm/bit-formats/SENDER.png) - -### `TRANSACTIONFEE` -Get the computed transaction fee during teardown phase, zero otherwise - -[See in table.](#isa-table-transactionfee) - -- **Opcode**: 0x11 -- **Category**: Execution Environment -- **Flags**: - - **indirect**: Toggles whether each memory-offset argument is an indirect offset. Rightmost bit corresponds to 0th offset arg, etc. Indirect offsets result in memory accesses like `M[M[offset]]` instead of the more standard `M[offset]`. -- **Args**: - - **dstOffset**: memory offset specifying where to store operation's result -- **Expression**: `M[dstOffset] = context.environment.transactionFee` -- **Tag updates**: `T[dstOffset] = field` -- **Bit-size**: 56 - - -### `CHAINID` -Get this rollup's L1 chain ID - -[See in table.](#isa-table-chainid) - -- **Opcode**: 0x12 -- **Category**: Execution Environment - Globals -- **Flags**: - - **indirect**: Toggles whether each memory-offset argument is an indirect offset. Rightmost bit corresponds to 0th offset arg, etc. Indirect offsets result in memory accesses like `M[M[offset]]` instead of the more standard `M[offset]`. -- **Args**: - - **dstOffset**: memory offset specifying where to store operation's result -- **Expression**: `M[dstOffset] = context.environment.globals.chainId` -- **Tag updates**: `T[dstOffset] = field` -- **Bit-size**: 56 - -[![](/img/protocol-specs/public-vm/bit-formats/CHAINID.png)](/img/protocol-specs/public-vm/bit-formats/CHAINID.png) - -### `VERSION` -Get this rollup's L2 version ID - -[See in table.](#isa-table-version) - -- **Opcode**: 0x13 -- **Category**: Execution Environment - Globals -- **Flags**: - - **indirect**: Toggles whether each memory-offset argument is an indirect offset. Rightmost bit corresponds to 0th offset arg, etc. Indirect offsets result in memory accesses like `M[M[offset]]` instead of the more standard `M[offset]`. -- **Args**: - - **dstOffset**: memory offset specifying where to store operation's result -- **Expression**: `M[dstOffset] = context.environment.globals.version` -- **Tag updates**: `T[dstOffset] = field` -- **Bit-size**: 56 - -[![](/img/protocol-specs/public-vm/bit-formats/VERSION.png)](/img/protocol-specs/public-vm/bit-formats/VERSION.png) - -### `BLOCKNUMBER` -Get this L2 block's number - -[See in table.](#isa-table-blocknumber) - -- **Opcode**: 0x14 -- **Category**: Execution Environment - Globals -- **Flags**: - - **indirect**: Toggles whether each memory-offset argument is an indirect offset. Rightmost bit corresponds to 0th offset arg, etc. Indirect offsets result in memory accesses like `M[M[offset]]` instead of the more standard `M[offset]`. -- **Args**: - - **dstOffset**: memory offset specifying where to store operation's result -- **Expression**: `M[dstOffset] = context.environment.globals.blocknumber` -- **Tag updates**: `T[dstOffset] = field` -- **Bit-size**: 56 - -[![](/img/protocol-specs/public-vm/bit-formats/BLOCKNUMBER.png)](/img/protocol-specs/public-vm/bit-formats/BLOCKNUMBER.png) - -### `TIMESTAMP` -Get this L2 block's timestamp - -[See in table.](#isa-table-timestamp) - -- **Opcode**: 0x15 -- **Category**: Execution Environment - Globals -- **Flags**: - - **indirect**: Toggles whether each memory-offset argument is an indirect offset. Rightmost bit corresponds to 0th offset arg, etc. Indirect offsets result in memory accesses like `M[M[offset]]` instead of the more standard `M[offset]`. -- **Args**: - - **dstOffset**: memory offset specifying where to store operation's result -- **Expression**: `M[dstOffset] = context.environment.globals.timestamp` -- **Tag updates**: `T[dstOffset] = u64` -- **Bit-size**: 56 - -[![](/img/protocol-specs/public-vm/bit-formats/TIMESTAMP.png)](/img/protocol-specs/public-vm/bit-formats/TIMESTAMP.png) - -### `FEEPERL2GAS` -Get the fee to be paid per "L2 gas" - constant for entire transaction - -[See in table.](#isa-table-feeperl2gas) - -- **Opcode**: 0x16 -- **Category**: Execution Environment - Globals - Gas -- **Flags**: - - **indirect**: Toggles whether each memory-offset argument is an indirect offset. Rightmost bit corresponds to 0th offset arg, etc. Indirect offsets result in memory accesses like `M[M[offset]]` instead of the more standard `M[offset]`. -- **Args**: - - **dstOffset**: memory offset specifying where to store operation's result -- **Expression**: `M[dstOffset] = context.environment.globals.feePerL2Gas` -- **Tag updates**: `T[dstOffset] = field` -- **Bit-size**: 56 - -[![](/img/protocol-specs/public-vm/bit-formats/FEEPERL2GAS.png)](/img/protocol-specs/public-vm/bit-formats/FEEPERL2GAS.png) - -### `FEEPERDAGAS` -Get the fee to be paid per "DA gas" - constant for entire transaction - -[See in table.](#isa-table-feeperdagas) - -- **Opcode**: 0x17 -- **Category**: Execution Environment - Globals - Gas -- **Flags**: - - **indirect**: Toggles whether each memory-offset argument is an indirect offset. Rightmost bit corresponds to 0th offset arg, etc. Indirect offsets result in memory accesses like `M[M[offset]]` instead of the more standard `M[offset]`. -- **Args**: - - **dstOffset**: memory offset specifying where to store operation's result -- **Expression**: `M[dstOffset] = context.environment.globals.feePerDaGas` -- **Tag updates**: `T[dstOffset] = field` -- **Bit-size**: 56 - -[![](/img/protocol-specs/public-vm/bit-formats/FEEPERDAGAS.png)](/img/protocol-specs/public-vm/bit-formats/FEEPERDAGAS.png) - -### `CALLDATACOPY` -Copy calldata into memory - -[See in table.](#isa-table-calldatacopy) - -- **Opcode**: 0x18 -- **Category**: Execution Environment - Calldata -- **Flags**: - - **indirect**: Toggles whether each memory-offset argument is an indirect offset. Rightmost bit corresponds to 0th offset arg, etc. Indirect offsets result in memory accesses like `M[M[offset]]` instead of the more standard `M[offset]`. -- **Args**: - - **cdOffset**: offset into calldata to copy from - - **copySize**: number of words to copy - - **dstOffset**: memory offset specifying where to copy the first word to -- **Expression**: `M[dstOffset:dstOffset+copySize] = context.environment.calldata[cdOffset:cdOffset+copySize]` -- **Details**: Calldata is read-only and cannot be directly operated on by other instructions. This instruction moves words from calldata into memory so they can be operated on normally. -- **Tag updates**: `T[dstOffset:dstOffset+copySize] = field` -- **Bit-size**: 120 - -[![](/img/protocol-specs/public-vm/bit-formats/CALLDATACOPY.png)](/img/protocol-specs/public-vm/bit-formats/CALLDATACOPY.png) - -### `L2GASLEFT` -Remaining "L2 gas" for this call (after this instruction) - -[See in table.](#isa-table-l2gasleft) - -- **Opcode**: 0x19 -- **Category**: Machine State - Gas -- **Flags**: - - **indirect**: Toggles whether each memory-offset argument is an indirect offset. Rightmost bit corresponds to 0th offset arg, etc. Indirect offsets result in memory accesses like `M[M[offset]]` instead of the more standard `M[offset]`. -- **Args**: - - **dstOffset**: memory offset specifying where to store operation's result -- **Expression**: `M[dstOffset] = context.MachineState.l2GasLeft` -- **Tag updates**: `T[dstOffset] = u32` -- **Bit-size**: 56 - -[![](/img/protocol-specs/public-vm/bit-formats/L2GASLEFT.png)](/img/protocol-specs/public-vm/bit-formats/L2GASLEFT.png) - -### `DAGASLEFT` -Remaining "DA gas" for this call (after this instruction) - -[See in table.](#isa-table-dagasleft) - -- **Opcode**: 0x1a -- **Category**: Machine State - Gas -- **Flags**: - - **indirect**: Toggles whether each memory-offset argument is an indirect offset. Rightmost bit corresponds to 0th offset arg, etc. Indirect offsets result in memory accesses like `M[M[offset]]` instead of the more standard `M[offset]`. -- **Args**: - - **dstOffset**: memory offset specifying where to store operation's result -- **Expression**: `M[dstOffset] = context.machineState.daGasLeft` -- **Tag updates**: `T[dstOffset] = u32` -- **Bit-size**: 56 - -[![](/img/protocol-specs/public-vm/bit-formats/DAGASLEFT.png)](/img/protocol-specs/public-vm/bit-formats/DAGASLEFT.png) - -### `JUMP` -Jump to a location in the bytecode - -[See in table.](#isa-table-jump) - -- **Opcode**: 0x1b -- **Category**: Machine State - Control Flow -- **Args**: - - **loc**: target location to jump to -- **Expression**: `context.machineState.pc = loc` -- **Details**: Target location is an immediate value (a constant in the bytecode). -- **Bit-size**: 48 - -[![](/img/protocol-specs/public-vm/bit-formats/JUMP.png)](/img/protocol-specs/public-vm/bit-formats/JUMP.png) - -### `JUMPI` -Conditionally jump to a location in the bytecode - -[See in table.](#isa-table-jumpi) - -- **Opcode**: 0x1c -- **Category**: Machine State - Control Flow -- **Flags**: - - **indirect**: Toggles whether each memory-offset argument is an indirect offset. Rightmost bit corresponds to 0th offset arg, etc. Indirect offsets result in memory accesses like `M[M[offset]]` instead of the more standard `M[offset]`. -- **Args**: - - **loc**: target location conditionally jump to - - **condOffset**: memory offset of the operations 'conditional' input -- **Expression**: `context.machineState.pc = M[condOffset] > 0 ? loc : context.machineState.pc` -- **Details**: Target location is an immediate value (a constant in the bytecode). `T[condOffset]` is not checked because the greater-than-zero suboperation is the same regardless of type. -- **Bit-size**: 88 - -[![](/img/protocol-specs/public-vm/bit-formats/JUMPI.png)](/img/protocol-specs/public-vm/bit-formats/JUMPI.png) - -### `INTERNALCALL` -Make an internal call. Push the current PC to the internal call stack and jump to the target location. - -[See in table.](#isa-table-internalcall) - -- **Opcode**: 0x1d -- **Category**: Machine State - Control Flow -- **Args**: - - **loc**: target location to jump/call to -- **Expression**: - -{`context.machineState.internalCallStack.push(context.machineState.pc) -context.machineState.pc = loc`} - -- **Details**: Target location is an immediate value (a constant in the bytecode). -- **Bit-size**: 48 - -[![](/img/protocol-specs/public-vm/bit-formats/INTERNALCALL.png)](/img/protocol-specs/public-vm/bit-formats/INTERNALCALL.png) - -### `INTERNALRETURN` -Return from an internal call. Pop from the internal call stack and jump to the popped location. - -[See in table.](#isa-table-internalreturn) - -- **Opcode**: 0x1e -- **Category**: Machine State - Control Flow -- **Expression**: `context.machineState.pc = context.machineState.internalCallStack.pop()` -- **Bit-size**: 16 - -[![](/img/protocol-specs/public-vm/bit-formats/INTERNALRETURN.png)](/img/protocol-specs/public-vm/bit-formats/INTERNALRETURN.png) - -### `SET` -Set a memory word from a constant in the bytecode - -[See in table.](#isa-table-set) - -- **Opcode**: 0x1f -- **Category**: Machine State - Memory -- **Flags**: - - **indirect**: Toggles whether each memory-offset argument is an indirect offset. Rightmost bit corresponds to 0th offset arg, etc. Indirect offsets result in memory accesses like `M[M[offset]]` instead of the more standard `M[offset]`. - - **inTag**: The [type/size](./memory-model#tags-and-tagged-memory) to check inputs against and tag the destination with. `field` type is NOT supported for SET. -- **Args**: - - **const**: an N-bit constant value from the bytecode to store in memory (any type except `field`) - - **dstOffset**: memory offset specifying where to store the constant -- **Expression**: `M[dstOffset] = const` -- **Details**: Set memory word at `dstOffset` to `const`'s immediate value. `const`'s bit-size (N) can be 8, 16, 32, 64, or 128 based on `inTag`. It _cannot be 254 (`field` type)_! -- **Tag updates**: `T[dstOffset] = inTag` -- **Bit-size**: 64+N - -[![](/img/protocol-specs/public-vm/bit-formats/SET.png)](/img/protocol-specs/public-vm/bit-formats/SET.png) - -### `MOV` -Move a word from source memory location to destination - -[See in table.](#isa-table-mov) - -- **Opcode**: 0x20 -- **Category**: Machine State - Memory -- **Flags**: - - **indirect**: Toggles whether each memory-offset argument is an indirect offset. Rightmost bit corresponds to 0th offset arg, etc. Indirect offsets result in memory accesses like `M[M[offset]]` instead of the more standard `M[offset]`. -- **Args**: - - **srcOffset**: memory offset of word to move - - **dstOffset**: memory offset specifying where to store that word -- **Expression**: `M[dstOffset] = M[srcOffset]` -- **Tag updates**: `T[dstOffset] = T[srcOffset]` -- **Bit-size**: 88 - -[![](/img/protocol-specs/public-vm/bit-formats/MOV.png)](/img/protocol-specs/public-vm/bit-formats/MOV.png) - -### `CMOV` -Move a word (conditionally chosen) from one memory location to another (`d \= cond \> 0 ? a : b`) - -[See in table.](#isa-table-cmov) - -- **Opcode**: 0x21 -- **Category**: Machine State - Memory -- **Flags**: - - **indirect**: Toggles whether each memory-offset argument is an indirect offset. Rightmost bit corresponds to 0th offset arg, etc. Indirect offsets result in memory accesses like `M[M[offset]]` instead of the more standard `M[offset]`. -- **Args**: - - **aOffset**: memory offset of word 'a' to conditionally move - - **bOffset**: memory offset of word 'b' to conditionally move - - **condOffset**: memory offset of the operations 'conditional' input - - **dstOffset**: memory offset specifying where to store operation's result -- **Expression**: `M[dstOffset] = M[condOffset] > 0 ? M[aOffset] : M[bOffset]` -- **Details**: One of two source memory locations is chosen based on the condition. `T[condOffset]` is not checked because the greater-than-zero suboperation is the same regardless of type. -- **Tag updates**: `T[dstOffset] = M[condOffset] > 0 ? T[aOffset] : T[bOffset]` -- **Bit-size**: 152 - -[![](/img/protocol-specs/public-vm/bit-formats/CMOV.png)](/img/protocol-specs/public-vm/bit-formats/CMOV.png) - -### `SLOAD` -Load a word from this contract's persistent public storage. Zero is loaded for unwritten slots. - -[See in table.](#isa-table-sload) - -- **Opcode**: 0x22 -- **Category**: World State - Public Storage -- **Flags**: - - **indirect**: Toggles whether each memory-offset argument is an indirect offset. Rightmost bit corresponds to 0th offset arg, etc. Indirect offsets result in memory accesses like `M[M[offset]]` instead of the more standard `M[offset]`. -- **Args**: - - **slotOffset**: memory offset of the storage slot to load from - - **dstOffset**: memory offset specifying where to store operation's result -- **Expression**: - -{`M[dstOffset] = S[M[slotOffset]]`} - -- **Details**: - -{`// Expression is shorthand for -leafIndex = hash(context.environment.address, M[slotOffset]) -exists = context.worldState.publicStorage.has(leafIndex) // exists == previously-written -if exists: - value = context.worldState.publicStorage.get(leafIndex: leafIndex) -else: - value = 0 -M[dstOffset] = value`} - -- **World State access tracing**: - -{`context.worldStateAccessTrace.publicStorageReads.append( - TracedStorageRead { - callPointer: context.environment.callPointer, - slot: M[slotOffset], - exists: exists, // defined above - value: value, // defined above - counter: ++context.worldStateAccessTrace.accessCounter, - } -)`} - -- **Triggers downstream circuit operations**: Storage slot siloing (hash with contract address), public data tree membership check -- **Tag updates**: `T[dstOffset] = field` -- **Bit-size**: 88 - -[![](/img/protocol-specs/public-vm/bit-formats/SLOAD.png)](/img/protocol-specs/public-vm/bit-formats/SLOAD.png) - -### `SSTORE` -Write a word to this contract's persistent public storage - -[See in table.](#isa-table-sstore) - -- **Opcode**: 0x23 -- **Category**: World State - Public Storage -- **Flags**: - - **indirect**: Toggles whether each memory-offset argument is an indirect offset. Rightmost bit corresponds to 0th offset arg, etc. Indirect offsets result in memory accesses like `M[M[offset]]` instead of the more standard `M[offset]`. -- **Args**: - - **srcOffset**: memory offset of the word to store - - **slotOffset**: memory offset containing the storage slot to store to -- **Expression**: - -{`S[M[slotOffset]] = M[srcOffset]`} - -- **Details**: - -{`// Expression is shorthand for -context.worldState.publicStorage.set({ - leafIndex: hash(context.environment.address, M[slotOffset]), - leaf: M[srcOffset], -})`} - -- **World State access tracing**: - -{`context.worldStateAccessTrace.publicStorageWrites.append( - TracedStorageWrite { - callPointer: context.environment.callPointer, - slot: M[slotOffset], - value: M[srcOffset], - counter: ++context.worldStateAccessTrace.accessCounter, - } -)`} - -- **Triggers downstream circuit operations**: Storage slot siloing (hash with contract address), public data tree update -- **Bit-size**: 88 - -[![](/img/protocol-specs/public-vm/bit-formats/SSTORE.png)](/img/protocol-specs/public-vm/bit-formats/SSTORE.png) - -### `NOTEHASHEXISTS` -Check whether a note hash exists in the note hash tree (as of the start of the current block) - -[See in table.](#isa-table-notehashexists) - -- **Opcode**: 0x24 -- **Category**: World State - Notes & Nullifiers -- **Flags**: - - **indirect**: Toggles whether each memory-offset argument is an indirect offset. Rightmost bit corresponds to 0th offset arg, etc. Indirect offsets result in memory accesses like `M[M[offset]]` instead of the more standard `M[offset]`. -- **Args**: - - **noteHashOffset**: memory offset of the note hash - - **leafIndexOffset**: memory offset of the leaf index - - **existsOffset**: memory offset specifying where to store operation's result (whether the note hash leaf exists) -- **Expression**: - -{`exists = context.worldState.noteHashes.has({ - leafIndex: M[leafIndexOffset] - leaf: hash(context.environment.address, M[noteHashOffset]), -}) -M[existsOffset] = exists`} - -- **World State access tracing**: - -{`context.worldStateAccessTrace.noteHashChecks.append( - TracedNoteHashCheck { - callPointer: context.environment.callPointer, - leafIndex: M[leafIndexOffset] - noteHash: M[noteHashOffset], - exists: exists, // defined above - counter: ++context.worldStateAccessTrace.accessCounter, - } -)`} - -- **Triggers downstream circuit operations**: Note hash siloing (hash with storage contract address), note hash tree membership check -- **Tag updates**: `T[existsOffset] = u8` -- **Bit-size**: 120 - - -### `EMITNOTEHASH` -Emit a new note hash to be inserted into the note hash tree - -[See in table.](#isa-table-emitnotehash) - -- **Opcode**: 0x25 -- **Category**: World State - Notes & Nullifiers -- **Flags**: - - **indirect**: Toggles whether each memory-offset argument is an indirect offset. Rightmost bit corresponds to 0th offset arg, etc. Indirect offsets result in memory accesses like `M[M[offset]]` instead of the more standard `M[offset]`. -- **Args**: - - **noteHashOffset**: memory offset of the note hash -- **Expression**: - -{`context.worldState.noteHashes.append( - hash(context.environment.address, M[noteHashOffset]) -)`} - -- **World State access tracing**: - -{`context.worldStateAccessTrace.noteHashes.append( - TracedNoteHash { - callPointer: context.environment.callPointer, - noteHash: M[noteHashOffset], // unsiloed note hash - counter: ++context.worldStateAccessTrace.accessCounter, - } -)`} - -- **Triggers downstream circuit operations**: Note hash siloing (hash with contract address), note hash tree insertion. -- **Bit-size**: 56 - -[![](/img/protocol-specs/public-vm/bit-formats/EMITNOTEHASH.png)](/img/protocol-specs/public-vm/bit-formats/EMITNOTEHASH.png) - -### `NULLIFIEREXISTS` -Check whether a nullifier exists in the nullifier tree (including nullifiers from earlier in the current transaction or from earlier in the current block) - -[See in table.](#isa-table-nullifierexists) - -- **Opcode**: 0x26 -- **Category**: World State - Notes & Nullifiers -- **Flags**: - - **indirect**: Toggles whether each memory-offset argument is an indirect offset. Rightmost bit corresponds to 0th offset arg, etc. Indirect offsets result in memory accesses like `M[M[offset]]` instead of the more standard `M[offset]`. -- **Args**: - - **nullifierOffset**: memory offset of the unsiloed nullifier - - **addressOffset**: memory offset of the storage address - - **existsOffset**: memory offset specifying where to store operation's result (whether the nullifier exists) -- **Expression**: - -{`exists = pendingNullifiers.has(M[addressOffset], M[nullifierOffset]) || context.worldState.nullifiers.has( - hash(M[addressOffset], M[nullifierOffset]) -) -M[existsOffset] = exists`} - -- **World State access tracing**: - -{`context.worldStateAccessTrace.nullifierChecks.append( - TracedNullifierCheck { - callPointer: context.environment.callPointer, - nullifier: M[nullifierOffset], - address: M[addressOffset], - exists: exists, // defined above - counter: ++context.worldStateAccessTrace.accessCounter, - } -)`} - -- **Triggers downstream circuit operations**: Nullifier siloing (hash with storage contract address), nullifier tree membership check -- **Tag updates**: `T[existsOffset] = u8` -- **Bit-size**: 120 - - -### `EMITNULLIFIER` -Emit a new nullifier to be inserted into the nullifier tree - -[See in table.](#isa-table-emitnullifier) - -- **Opcode**: 0x27 -- **Category**: World State - Notes & Nullifiers -- **Flags**: - - **indirect**: Toggles whether each memory-offset argument is an indirect offset. Rightmost bit corresponds to 0th offset arg, etc. Indirect offsets result in memory accesses like `M[M[offset]]` instead of the more standard `M[offset]`. -- **Args**: - - **nullifierOffset**: memory offset of nullifier -- **Expression**: - -{`context.worldState.nullifiers.append( - hash(context.environment.address, M[nullifierOffset]) -)`} - -- **World State access tracing**: - -{`context.worldStateAccessTrace.nullifiers.append( - TracedNullifier { - callPointer: context.environment.callPointer, - nullifier: M[nullifierOffset], // unsiloed nullifier - counter: ++context.worldStateAccessTrace.accessCounter, - } -)`} - -- **Triggers downstream circuit operations**: Nullifier siloing (hash with contract address), nullifier tree non-membership-check and insertion. -- **Bit-size**: 56 - -[![](/img/protocol-specs/public-vm/bit-formats/EMITNULLIFIER.png)](/img/protocol-specs/public-vm/bit-formats/EMITNULLIFIER.png) - -### `L1TOL2MSGEXISTS` -Check if a message exists in the L1-to-L2 message tree - -[See in table.](#isa-table-l1tol2msgexists) - -- **Opcode**: 0x28 -- **Category**: World State - Messaging -- **Flags**: - - **indirect**: Toggles whether each memory-offset argument is an indirect offset. Rightmost bit corresponds to 0th offset arg, etc. Indirect offsets result in memory accesses like `M[M[offset]]` instead of the more standard `M[offset]`. -- **Args**: - - **msgHashOffset**: memory offset of the message hash - - **msgLeafIndexOffset**: memory offset of the message's leaf index in the L1-to-L2 message tree - - **existsOffset**: memory offset specifying where to store operation's result (whether the message exists in the L1-to-L2 message tree) -- **Expression**: - -{`exists = context.worldState.l1ToL2Messages.has({ - leafIndex: M[msgLeafIndexOffset], leaf: M[msgHashOffset] -}) -M[existsOffset] = exists`} - -- **World State access tracing**: - -{`context.worldStateAccessTrace.l1ToL2MessagesChecks.append( - L1ToL2Message { - callPointer: context.environment.callPointer, - leafIndex: M[msgLeafIndexOffset], - msgHash: M[msgHashOffset], - exists: exists, // defined above - } -)`} - -- **Triggers downstream circuit operations**: L1-to-L2 message tree membership check -- **Tag updates**: - -{`T[existsOffset] = u8,`} - -- **Bit-size**: 120 - - -### `GETCONTRACTINSTANCE` -Copies contract instance data to memory - -[See in table.](#isa-table-getcontractinstance) - -- **Opcode**: 0x29 -- **Category**: Other -- **Flags**: - - **indirect**: Toggles whether each memory-offset argument is an indirect offset. Rightmost bit corresponds to 0th offset arg, etc. Indirect offsets result in memory accesses like `M[M[offset]]` instead of the more standard `M[offset]`. -- **Args**: - - **addressOffset**: memory offset of the contract instance address - - **dstOffset**: location to write the contract instance information to -- **Expression**: - -{`M[dstOffset:dstOffset+CONTRACT_INSTANCE_SIZE+1] = [ - instance_found_in_address, - instance.salt ?? 0, - instance.deployer ?? 0, - instance.contractClassId ?? 0, - instance.initializationHash ?? 0, - instance.portalContractAddress ?? 0, - instance.publicKeysHash ?? 0, -]`} - -- **Additional AVM circuit checks**: TO-DO -- **Triggers downstream circuit operations**: TO-DO -- **Tag updates**: T[dstOffset:dstOffset+CONTRACT_INSTANCE_SIZE+1] = field -- **Bit-size**: 88 - - -### `EMITUNENCRYPTEDLOG` -Emit an unencrypted log - -[See in table.](#isa-table-emitunencryptedlog) - -- **Opcode**: 0x2a -- **Category**: Accrued Substate - Logging -- **Flags**: - - **indirect**: Toggles whether each memory-offset argument is an indirect offset. Rightmost bit corresponds to 0th offset arg, etc. Indirect offsets result in memory accesses like `M[M[offset]]` instead of the more standard `M[offset]`. -- **Args**: - - **logOffset**: memory offset of the data to log - - **logSizeOffset**: memory offset to number of words to log -- **Expression**: - -{`context.accruedSubstate.unencryptedLogs.append( - UnencryptedLog { - address: context.environment.address, - log: M[logOffset:logOffset+M[logSizeOffset]], - } -)`} - -- **Bit-size**: 88 - -[![](/img/protocol-specs/public-vm/bit-formats/EMITUNENCRYPTEDLOG.png)](/img/protocol-specs/public-vm/bit-formats/EMITUNENCRYPTEDLOG.png) - -### `SENDL2TOL1MSG` -Send an L2-to-L1 message - -[See in table.](#isa-table-sendl2tol1msg) - -- **Opcode**: 0x2b -- **Category**: Accrued Substate - Messaging -- **Flags**: - - **indirect**: Toggles whether each memory-offset argument is an indirect offset. Rightmost bit corresponds to 0th offset arg, etc. Indirect offsets result in memory accesses like `M[M[offset]]` instead of the more standard `M[offset]`. -- **Args**: - - **recipientOffset**: memory offset of the message recipient - - **contentOffset**: memory offset of the message content -- **Expression**: - -{`context.accruedSubstate.sentL2ToL1Messages.append( - SentL2ToL1Message { - address: context.environment.address, - recipient: M[recipientOffset], - message: M[contentOffset] - } -)`} - -- **Bit-size**: 88 - -[![](/img/protocol-specs/public-vm/bit-formats/SENDL2TOL1MSG.png)](/img/protocol-specs/public-vm/bit-formats/SENDL2TOL1MSG.png) - -### `CALL` -Call into another contract - -[See in table.](#isa-table-call) - -- **Opcode**: 0x2c -- **Category**: Control Flow - Contract Calls -- **Flags**: - - **indirect**: Toggles whether each memory-offset argument is an indirect offset. Rightmost bit corresponds to 0th offset arg, etc. Indirect offsets result in memory accesses like `M[M[offset]]` instead of the more standard `M[offset]`. -- **Args**: - - **gasOffset**: offset to two words containing `{l2GasLeft, daGasLeft}`: amount of gas to provide to the callee - - **addrOffset**: address of the contract to call - - **argsOffset**: memory offset to args (will become the callee's calldata) - - **argsSizeOffset**: memory offset for the number of words to pass via callee's calldata - - **retOffset**: destination memory offset specifying where to store the data returned from the callee - - **retSize**: number of words to copy from data returned by callee - - **successOffset**: destination memory offset specifying where to store the call's success (0: failure, 1: success) -- **Expression**: - -{`// instr.args are { gasOffset, addrOffset, argsOffset, retOffset, retSize } -chargeGas(context, - l2GasCost=M[instr.args.gasOffset], - daGasCost=M[instr.args.gasOffset+1]) -traceNestedCall(context, instr.args.addrOffset) -nestedContext = deriveContext(context, instr.args, isStaticCall=false) -execute(nestedContext) -updateContextAfterNestedCall(context, instr.args, nestedContext)`} - -- **Details**: Creates a new (nested) execution context and triggers execution within that context. - Execution proceeds in the nested context until it reaches a halt at which point - execution resumes in the current/calling context. - A non-existent contract or one with no code will return success. - ["Nested contract calls"](./nested-calls) provides a full explanation of this - instruction along with the shorthand used in the expression above. - The explanation includes details on charging gas for nested calls, - nested context derivation, world state tracing, and updating the parent context - after the nested call halts. -- **Tag checks**: `T[gasOffset] == T[gasOffset+1] == T[gasOffset+2] == u32` -- **Tag updates**: - -{`T[successOffset] = u8 -T[retOffset:retOffset+retSize] = field`} - -- **Bit-size**: 248 - -[![](/img/protocol-specs/public-vm/bit-formats/CALL.png)](/img/protocol-specs/public-vm/bit-formats/CALL.png) - -### `STATICCALL` -Call into another contract, disallowing World State and Accrued Substate modifications - -[See in table.](#isa-table-staticcall) - -- **Opcode**: 0x2d -- **Category**: Control Flow - Contract Calls -- **Flags**: - - **indirect**: Toggles whether each memory-offset argument is an indirect offset. Rightmost bit corresponds to 0th offset arg, etc. Indirect offsets result in memory accesses like `M[M[offset]]` instead of the more standard `M[offset]`. -- **Args**: - - **gasOffset**: offset to two words containing `{l2GasLeft, daGasLeft}`: amount of gas to provide to the callee - - **addrOffset**: address of the contract to call - - **argsOffset**: memory offset to args (will become the callee's calldata) - - **argsSizeOffset**: memory offset for the number of words to pass via callee's calldata - - **retOffset**: destination memory offset specifying where to store the data returned from the callee - - **retSize**: number of words to copy from data returned by callee - - **successOffset**: destination memory offset specifying where to store the call's success (0: failure, 1: success) -- **Expression**: - -{`// instr.args are { gasOffset, addrOffset, argsOffset, retOffset, retSize } -chargeGas(context, - l2GasCost=M[instr.args.gasOffset], - daGasCost=M[instr.args.gasOffset+1]) -traceNestedCall(context, instr.args.addrOffset) -nestedContext = deriveContext(context, instr.args, isStaticCall=true -execute(nestedContext) -updateContextAfterNestedCall(context, instr.args, nestedContext)`} - -- **Details**: Same as `CALL`, but disallows World State and Accrued Substate modifications. - ["Nested contract calls"](./nested-calls) provides a full explanation of this - instruction along with the shorthand used in the expression above. - The explanation includes details on charging gas for nested calls, - nested context derivation, world state tracing, and updating the parent context - after the nested call halts. -- **Tag checks**: `T[gasOffset] == T[gasOffset+1] == T[gasOffset+2] == u32` -- **Tag updates**: - -{`T[successOffset] = u8 -T[retOffset:retOffset+retSize] = field`} - -- **Bit-size**: 248 - -[![](/img/protocol-specs/public-vm/bit-formats/STATICCALL.png)](/img/protocol-specs/public-vm/bit-formats/STATICCALL.png) - -### `RETURN` -Halt execution within this context (without revert), optionally returning some data - -[See in table.](#isa-table-return) - -- **Opcode**: 0x2e -- **Category**: Control Flow - Contract Calls -- **Flags**: - - **indirect**: Toggles whether each memory-offset argument is an indirect offset. Rightmost bit corresponds to 0th offset arg, etc. Indirect offsets result in memory accesses like `M[M[offset]]` instead of the more standard `M[offset]`. -- **Args**: - - **retOffset**: memory offset of first word to return - - **retSize**: number of words to return -- **Expression**: - -{`context.contractCallResults.output = M[retOffset:retOffset+retSize] -halt`} - -- **Details**: Return control flow to the calling context/contract. Caller will accept World State and Accrued Substate modifications. See ["Halting"](./execution#halting) to learn more. See ["Nested contract calls"](./nested-calls) to see how the caller updates its context after the nested call halts. -- **Bit-size**: 88 - -[![](/img/protocol-specs/public-vm/bit-formats/RETURN.png)](/img/protocol-specs/public-vm/bit-formats/RETURN.png) - -### `REVERT` -Halt execution within this context as `reverted`, optionally returning some data - -[See in table.](#isa-table-revert) - -- **Opcode**: 0x2f -- **Category**: Control Flow - Contract Calls -- **Flags**: - - **indirect**: Toggles whether each memory-offset argument is an indirect offset. Rightmost bit corresponds to 0th offset arg, etc. Indirect offsets result in memory accesses like `M[M[offset]]` instead of the more standard `M[offset]`. -- **Args**: - - **retOffset**: memory offset of first word to return - - **retSize**: number of words to return -- **Expression**: - -{`context.contractCallResults.output = M[retOffset:retOffset+retSize] -context.contractCallResults.reverted = true -halt`} - -- **Details**: Return control flow to the calling context/contract. Caller will reject World State and Accrued Substate modifications. See ["Halting"](./execution#halting) to learn more. See ["Nested contract calls"](./nested-calls) to see how the caller updates its context after the nested call halts. -- **Bit-size**: 88 - -[![](/img/protocol-specs/public-vm/bit-formats/REVERT.png)](/img/protocol-specs/public-vm/bit-formats/REVERT.png) - -### `TORADIXLE` -Convert a word to an array of limbs in little-endian radix form - -[See in table.](#isa-table-to_radix_le) - -- **Opcode**: 0x30 -- **Category**: Conversions -- **Flags**: - - **indirect**: Toggles whether each memory-offset argument is an indirect offset. Rightmost bit corresponds to 0th offset arg, etc. Indirect offsets result in memory accesses like `M[M[offset]]` instead of the more standard `M[offset]`. -- **Args**: - - **srcOffset**: memory offset of word to convert. - - **dstOffset**: memory offset specifying where the first limb of the radix-conversion result is stored. - - **radix**: the maximum bit-size of each limb. - - **numLimbs**: the number of limbs the word will be converted into. -- **Expression**: TBD: Storage of limbs and if T[dstOffset] is constrained to U8 -- **Details**: The limbs will be stored in a contiguous memory block starting at `dstOffset`. -- **Tag checks**: `T[srcOffset] == field` -- **Bit-size**: 152 - diff --git a/docs/package.json b/docs/package.json index 98bd39e0a2f..0e9563dbfce 100644 --- a/docs/package.json +++ b/docs/package.json @@ -73,4 +73,4 @@ ] }, "packageManager": "yarn@4.5.2" -} \ No newline at end of file +} diff --git a/noir-projects/aztec-nr/aztec/src/pxe_db/mod.nr b/noir-projects/aztec-nr/aztec/src/capsules/mod.nr similarity index 68% rename from noir-projects/aztec-nr/aztec/src/pxe_db/mod.nr rename to noir-projects/aztec-nr/aztec/src/capsules/mod.nr index 99a1cf6e040..10595a6b87b 100644 --- a/noir-projects/aztec-nr/aztec/src/pxe_db/mod.nr +++ b/noir-projects/aztec-nr/aztec/src/capsules/mod.nr @@ -1,24 +1,24 @@ -use crate::oracle::pxe_db; +use crate::oracle::capsules; use protocol_types::{address::AztecAddress, traits::{Deserialize, Serialize}}; -/// A dynamically sized array backed by PXE's non-volatile database. Values are persisted until deleted, so they can be -/// e.g. stored during simulation of a transaction and later retrieved during witness generation. All values are scoped -/// per contract address, so external contracts cannot access them. -pub struct DBArray { +/// A dynamically sized array backed by PXE's non-volatile database (called capsules). Values are persisted until +/// deleted, so they can be e.g. stored during simulation of a transaction and later retrieved during witness +/// generation. All values are scoped per contract address, so external contracts cannot access them. +pub struct CapsuleArray { contract_address: AztecAddress, - /// An array's base slot is the slot in PXE's database in which the array length is stored. Array elements are - /// stored contiguously in the following slots, so e.g. if the base slot is 5, then the length is stored at slot 5, - /// the first element (index 0) at slot 6, the second (index 1) at slot 7, and so on. + /// The base slot is where the array length is stored in capsules. Array elements are stored in consecutive slots + /// after the base slot. For example, with base slot 5: the length is at slot 5, the first element (index 0) is at + /// slot 6, the second element (index 1) is at slot 7, and so on. base_slot: Field, } -impl DBArray +impl CapsuleArray where T: Serialize + Deserialize, { - /// Returns a DBArray connected to a contact's PXE database at a base database slot. Array elements are be stored at + /// Returns a CapsuleArray connected to a contract's capsules at a base slot. Array elements are stored in /// contiguous slots following the base slot, so there should be sufficient space between array base slots to - /// accomodate elements. A reasonable strategy is to make the base slot a hash of a unique value. + /// accommodate elements. A reasonable strategy is to make the base slot a hash of a unique value. pub unconstrained fn at(contract_address: AztecAddress, base_slot: Field) -> Self { Self { contract_address, base_slot } } @@ -26,7 +26,7 @@ where /// Returns the number of elements stored in the array. pub unconstrained fn len(self) -> u32 { // An uninitialized array defaults to a length of 0. - pxe_db::load(self.contract_address, self.base_slot).unwrap_or(0) as u32 + capsules::load(self.contract_address, self.base_slot).unwrap_or(0) as u32 } /// Stores a value at the end of the array. @@ -35,31 +35,31 @@ where // The slot corresponding to the index `current_length` is the first slot immediately after the end of the // array, which is where we want to place the new value. - pxe_db::store(self.contract_address, self.slot_at(current_length), value); + capsules::store(self.contract_address, self.slot_at(current_length), value); // Then we simply update the length. let new_length = current_length + 1; - pxe_db::store(self.contract_address, self.base_slot, new_length); + capsules::store(self.contract_address, self.base_slot, new_length); } /// Retrieves the value stored in the array at `index`. Throws if the index is out of bounds. pub unconstrained fn get(self, index: u32) -> T { - assert(index < self.len(), "Attempted to read past the length of a DBArray"); + assert(index < self.len(), "Attempted to read past the length of a CapsuleArray"); - pxe_db::load(self.contract_address, self.slot_at(index)).unwrap() + capsules::load(self.contract_address, self.slot_at(index)).unwrap() } /// Deletes the value stored in the array at `index`. Throws if the index is out of bounds. pub unconstrained fn remove(self, index: u32) { let current_length = self.len(); - assert(index < current_length, "Attempted to delete past the length of a DBArray"); + assert(index < current_length, "Attempted to delete past the length of a CapsuleArray"); // In order to be able to remove elements at arbitrary indices, we need to shift the entire contents of the // array past the removed element one slot backward so that we don't end up with a gap and preserve the // contiguous slots. We can skip this when deleting the last element however. if index != current_length - 1 { - // The souce and destination regions overlap, but `copy` supports this. - pxe_db::copy( + // The source and destination regions overlap, but `copy` supports this. + capsules::copy( self.contract_address, self.slot_at(index + 1), self.slot_at(index), @@ -69,8 +69,8 @@ where // We can now delete the last element (which has either been copied to the slot immediately before it, or was // the element we meant to delete in the first place) and update the length. - pxe_db::delete(self.contract_address, self.slot_at(current_length - 1)); - pxe_db::store(self.contract_address, self.base_slot, current_length - 1); + capsules::delete(self.contract_address, self.slot_at(current_length - 1)); + capsules::store(self.contract_address, self.base_slot, current_length - 1); } unconstrained fn slot_at(self, index: u32) -> Field { @@ -82,7 +82,7 @@ where mod test { use crate::test::helpers::test_environment::TestEnvironment; - use super::DBArray; + use super::CapsuleArray; use protocol_types::address::AztecAddress; global SLOT: Field = 1230; @@ -95,15 +95,15 @@ mod test { unconstrained fn empty_array() { let contract_address = setup(); - let array: DBArray = DBArray::at(contract_address, SLOT); + let array: CapsuleArray = CapsuleArray::at(contract_address, SLOT); assert_eq(array.len(), 0); } - #[test(should_fail_with = "Attempted to read past the length of a DBArray")] + #[test(should_fail_with = "Attempted to read past the length of a CapsuleArray")] unconstrained fn empty_array_read() { let contract_address = setup(); - let array = DBArray::at(contract_address, SLOT); + let array = CapsuleArray::at(contract_address, SLOT); let _: Field = array.get(0); } @@ -111,18 +111,18 @@ mod test { unconstrained fn array_push() { let contract_address = setup(); - let array = DBArray::at(contract_address, SLOT); + let array = CapsuleArray::at(contract_address, SLOT); array.push(5); assert_eq(array.len(), 1); assert_eq(array.get(0), 5); } - #[test(should_fail_with = "Attempted to read past the length of a DBArray")] + #[test(should_fail_with = "Attempted to read past the length of a CapsuleArray")] unconstrained fn read_past_len() { let contract_address = setup(); - let array = DBArray::at(contract_address, SLOT); + let array = CapsuleArray::at(contract_address, SLOT); array.push(5); let _ = array.get(1); @@ -132,7 +132,7 @@ mod test { unconstrained fn array_remove_last() { let contract_address = setup(); - let array = DBArray::at(contract_address, SLOT); + let array = CapsuleArray::at(contract_address, SLOT); array.push(5); array.remove(0); @@ -144,7 +144,7 @@ mod test { unconstrained fn array_remove_some() { let contract_address = setup(); - let array = DBArray::at(contract_address, SLOT); + let array = CapsuleArray::at(contract_address, SLOT); array.push(7); array.push(8); @@ -166,7 +166,7 @@ mod test { unconstrained fn array_remove_all() { let contract_address = setup(); - let array = DBArray::at(contract_address, SLOT); + let array = CapsuleArray::at(contract_address, SLOT); array.push(7); array.push(8); diff --git a/noir-projects/aztec-nr/aztec/src/lib.nr b/noir-projects/aztec-nr/aztec/src/lib.nr index 0e1e7d7b16d..1748a6ee90a 100644 --- a/noir-projects/aztec-nr/aztec/src/lib.nr +++ b/noir-projects/aztec-nr/aztec/src/lib.nr @@ -8,7 +8,7 @@ mod note; mod event; mod oracle; mod state_vars; -mod pxe_db; +mod capsules; mod prelude; mod encrypted_logs; mod unencrypted_logs; diff --git a/noir-projects/aztec-nr/aztec/src/oracle/pxe_db.nr b/noir-projects/aztec-nr/aztec/src/oracle/capsules.nr similarity index 96% rename from noir-projects/aztec-nr/aztec/src/oracle/pxe_db.nr rename to noir-projects/aztec-nr/aztec/src/oracle/capsules.nr index acaa7d1673c..26c3b82b629 100644 --- a/noir-projects/aztec-nr/aztec/src/oracle/pxe_db.nr +++ b/noir-projects/aztec-nr/aztec/src/oracle/capsules.nr @@ -10,7 +10,7 @@ where store_oracle(contract_address, slot, serialized); } -/// Returns data previously stored via `dbStore` in the per-contract non-volatile database. Returns Option::none() if +/// Returns data previously stored via `storeCapsule` in the per-contract non-volatile database. Returns Option::none() if /// nothing was stored at the given slot. pub unconstrained fn load(contract_address: AztecAddress, slot: Field) -> Option where @@ -26,7 +26,7 @@ pub unconstrained fn delete(contract_address: AztecAddress, slot: Field) { } /// Copies a number of contiguous entries in the per-contract non-volatile database. This allows for efficient data -/// structures by avoiding repeated calls to `dbLoad` and `dbStore`. +/// structures by avoiding repeated calls to `loadCapsule` and `storeCapsule`. /// Supports overlapping source and destination regions (which will result in the overlapped source values being /// overwritten). All copied slots must exist in the database (i.e. have been stored and not deleted) pub unconstrained fn copy( @@ -38,7 +38,7 @@ pub unconstrained fn copy( copy_oracle(contract_address, src_slot, dst_slot, num_entries); } -#[oracle(dbStore)] +#[oracle(storeCapsule)] unconstrained fn store_oracle( contract_address: AztecAddress, slot: Field, @@ -52,17 +52,17 @@ unconstrained fn store_oracle( /// require for the oracle resolver to know the shape of T (e.g. if T were a struct of 3 u32 values then the expected /// response shape would be 3 single items, whereas it were a struct containing `u32, [Field;10], u32` then the expected /// shape would be single, array, single.). Instead, we return the serialization and deserialize in Noir. -#[oracle(dbLoad)] +#[oracle(loadCapsule)] unconstrained fn load_oracle( contract_address: AztecAddress, slot: Field, array_len: u32, ) -> Option<[Field; N]> {} -#[oracle(dbDelete)] +#[oracle(deleteCapsule)] unconstrained fn delete_oracle(contract_address: AztecAddress, slot: Field) {} -#[oracle(dbCopy)] +#[oracle(copyCapsule)] unconstrained fn copy_oracle( contract_address: AztecAddress, src_slot: Field, @@ -76,7 +76,7 @@ mod test { // oracles are hooked up correctly. use crate::{ - oracle::pxe_db::{copy, delete, load, store}, + oracle::capsules::{copy, delete, load, store}, test::{helpers::test_environment::TestEnvironment, mocks::mock_struct::MockStruct}, }; use protocol_types::{address::AztecAddress, traits::{FromField, ToField}}; diff --git a/noir-projects/aztec-nr/aztec/src/oracle/mod.nr b/noir-projects/aztec-nr/aztec/src/oracle/mod.nr index e960d38f5af..7edfefe7d7d 100644 --- a/noir-projects/aztec-nr/aztec/src/oracle/mod.nr +++ b/noir-projects/aztec-nr/aztec/src/oracle/mod.nr @@ -18,7 +18,7 @@ pub mod block_header; pub mod notes; pub mod storage; pub mod logs; -pub mod pxe_db; +pub mod capsules; pub mod execution_cache; // debug_log oracle is used by both noir-protocol-circuits and this crate and for this reason we just re-export it diff --git a/noir-projects/noir-contracts/contracts/contract_class_registerer_contract/src/main.nr b/noir-projects/noir-contracts/contracts/contract_class_registerer_contract/src/main.nr index 9707b3bdbdb..58b469d0b30 100644 --- a/noir-projects/noir-contracts/contracts/contract_class_registerer_contract/src/main.nr +++ b/noir-projects/noir-contracts/contracts/contract_class_registerer_contract/src/main.nr @@ -31,9 +31,9 @@ pub contract ContractClassRegisterer { }, }; - // docs:start:import_pop_capsule - use dep::aztec::oracle::pxe_db; - // docs:end:import_pop_capsule + // docs:start:import_capsules + use dep::aztec::oracle::capsules; + // docs:end:import_capsules #[private] fn register( @@ -44,15 +44,15 @@ pub contract ContractClassRegisterer { ) { // TODO: Validate public_bytecode_commitment is the correct commitment of packed_public_bytecode // TODO: We should be able to remove public_bytecode_commitment from the input if it's calculated in this function - // docs:start:pop_capsule + // docs:start:load_capsule let packed_public_bytecode: [Field; MAX_PACKED_PUBLIC_BYTECODE_SIZE_IN_FIELDS] = unsafe { - pxe_db::load( + capsules::load( context.this_address(), REGISTERER_CONTRACT_BYTECODE_CAPSULE_SLOT, ) .unwrap() }; - // docs:end:pop_capsule + // docs:end:load_capsule // First field element contains the length of the bytecode let bytecode_length_in_bytes: u32 = packed_public_bytecode[0] as u32; let bytecode_length_in_fields: u32 = @@ -125,7 +125,7 @@ pub contract ContractClassRegisterer { function_data: InnerPrivateFunction, ) { let private_bytecode: [Field; MAX_PACKED_BYTECODE_SIZE_PER_PRIVATE_FUNCTION_IN_FIELDS] = unsafe { - pxe_db::load( + capsules::load( context.this_address(), REGISTERER_CONTRACT_BYTECODE_CAPSULE_SLOT, ) @@ -171,7 +171,7 @@ pub contract ContractClassRegisterer { function_data: InnerUnconstrainedFunction, ) { let unconstrained_bytecode: [Field; MAX_PACKED_BYTECODE_SIZE_PER_UNCONSTRAINED_FUNCTION_IN_FIELDS] = unsafe { - pxe_db::load( + capsules::load( context.this_address(), REGISTERER_CONTRACT_BYTECODE_CAPSULE_SLOT, ) diff --git a/noir-projects/noir-contracts/contracts/test_contract/src/main.nr b/noir-projects/noir-contracts/contracts/test_contract/src/main.nr index 1103419ac3d..6eb2a1db305 100644 --- a/noir-projects/noir-contracts/contracts/test_contract/src/main.nr +++ b/noir-projects/noir-contracts/contracts/test_contract/src/main.nr @@ -36,7 +36,6 @@ pub contract Test { note_getter::{get_notes, view_notes}, note_getter_options::NoteStatus, }, - oracle::pxe_db, test::mocks::mock_struct::MockStruct, utils::comparison::Comparator, }; @@ -485,16 +484,6 @@ pub contract Test { constant.value } - unconstrained fn store_in_pxe_db(key: Field, arbitrary_struct: MockStruct) { - pxe_db::store(context.this_address(), key, arbitrary_struct); - } - - unconstrained fn load_from_pxe_db(key: Field) -> pub [Field; 2] { - let maybe_arbitrary_struct: Option = pxe_db::load(context.this_address(), key); - let arbitrary_struct = maybe_arbitrary_struct.unwrap_or(MockStruct::new(0, 0)); - arbitrary_struct.serialize() - } - #[private] fn test_nullifier_key_freshness(address: AztecAddress, public_nullifying_key: Point) { assert_eq(get_public_keys(address).npk_m.inner, public_nullifying_key); diff --git a/yarn-project/aztec.js/src/deployment/broadcast_function.ts b/yarn-project/aztec.js/src/deployment/broadcast_function.ts index 6e13cd3ef30..a3cfd850cb3 100644 --- a/yarn-project/aztec.js/src/deployment/broadcast_function.ts +++ b/yarn-project/aztec.js/src/deployment/broadcast_function.ts @@ -60,7 +60,7 @@ export async function broadcastPrivateFunction( MAX_PACKED_BYTECODE_SIZE_PER_PRIVATE_FUNCTION_IN_FIELDS, ); - await wallet.addCapsule( + await wallet.storeCapsule( AztecAddress.fromNumber(REGISTERER_CONTRACT_ADDRESS), new Fr(REGISTERER_CONTRACT_BYTECODE_CAPSULE_SLOT), bytecode, @@ -122,7 +122,7 @@ export async function broadcastUnconstrainedFunction( MAX_PACKED_BYTECODE_SIZE_PER_PRIVATE_FUNCTION_IN_FIELDS, ); - await wallet.addCapsule( + await wallet.storeCapsule( AztecAddress.fromNumber(REGISTERER_CONTRACT_ADDRESS), new Fr(REGISTERER_CONTRACT_BYTECODE_CAPSULE_SLOT), bytecode, diff --git a/yarn-project/aztec.js/src/deployment/register_class.ts b/yarn-project/aztec.js/src/deployment/register_class.ts index 5ee1abbe006..2c2320d888e 100644 --- a/yarn-project/aztec.js/src/deployment/register_class.ts +++ b/yarn-project/aztec.js/src/deployment/register_class.ts @@ -28,7 +28,7 @@ export async function registerContractClass( await getContractClassFromArtifact(artifact); const encodedBytecode = bufferAsFields(packedBytecode, MAX_PACKED_PUBLIC_BYTECODE_SIZE_IN_FIELDS); const registerer = await getRegistererContract(wallet); - await wallet.addCapsule( + await wallet.storeCapsule( AztecAddress.fromNumber(REGISTERER_CONTRACT_ADDRESS), new Fr(REGISTERER_CONTRACT_BYTECODE_CAPSULE_SLOT), encodedBytecode, diff --git a/yarn-project/aztec.js/src/wallet/base_wallet.ts b/yarn-project/aztec.js/src/wallet/base_wallet.ts index 691c2a2cbe0..4ca34622bb7 100644 --- a/yarn-project/aztec.js/src/wallet/base_wallet.ts +++ b/yarn-project/aztec.js/src/wallet/base_wallet.ts @@ -67,8 +67,8 @@ export abstract class BaseWallet implements Wallet { getAddress() { return this.getCompleteAddress().address; } - addCapsule(contract: AztecAddress, storageSlot: Fr, capsule: Fr[]): Promise { - return this.pxe.addCapsule(contract, storageSlot, capsule); + storeCapsule(contract: AztecAddress, storageSlot: Fr, capsule: Fr[]): Promise { + return this.pxe.storeCapsule(contract, storageSlot, capsule); } registerAccount(secretKey: Fr, partialAddress: PartialAddress): Promise { return this.pxe.registerAccount(secretKey, partialAddress); diff --git a/yarn-project/circuit-types/src/interfaces/pxe.test.ts b/yarn-project/circuit-types/src/interfaces/pxe.test.ts index 261c41c24fd..e99e700eb94 100644 --- a/yarn-project/circuit-types/src/interfaces/pxe.test.ts +++ b/yarn-project/circuit-types/src/interfaces/pxe.test.ts @@ -111,8 +111,8 @@ describe('PXESchema', () => { expect(result).toEqual([expect.any(Fr)]); }); - it('addCapsule', async () => { - await context.client.addCapsule(address, Fr.random(), times(3, Fr.random)); + it('storeCapsule', async () => { + await context.client.storeCapsule(address, Fr.random(), times(3, Fr.random)); }); it('registerAccount', async () => { @@ -344,7 +344,7 @@ class MockPXE implements PXE { expect(messageHash).toBeInstanceOf(Fr); return Promise.resolve([Fr.random()]); } - addCapsule(contract: AztecAddress, storageSlot: Fr, capsule: Fr[]): Promise { + storeCapsule(contract: AztecAddress, storageSlot: Fr, capsule: Fr[]): Promise { expect(contract).toBeInstanceOf(AztecAddress); expect(storageSlot).toBeInstanceOf(Fr); expect(capsule.every(c => c instanceof Fr)).toBeTruthy(); diff --git a/yarn-project/circuit-types/src/interfaces/pxe.ts b/yarn-project/circuit-types/src/interfaces/pxe.ts index ac6ac0351bb..24420a7d47b 100644 --- a/yarn-project/circuit-types/src/interfaces/pxe.ts +++ b/yarn-project/circuit-types/src/interfaces/pxe.ts @@ -91,7 +91,7 @@ export interface PXE { * to public contract storage in that it's indexed by the contract address and storage slot but instead of the global * network state it's backed by local PXE db. */ - addCapsule(contract: AztecAddress, storageSlot: Fr, capsule: Fr[]): Promise; + storeCapsule(contract: AztecAddress, storageSlot: Fr, capsule: Fr[]): Promise; /** * Registers a user account in PXE given its master encryption private key. @@ -468,7 +468,7 @@ export const PXESchema: ApiSchemaFor = { .function() .args(schemas.Fr) .returns(z.union([z.undefined(), z.array(schemas.Fr)])), - addCapsule: z.function().args(schemas.AztecAddress, schemas.Fr, z.array(schemas.Fr)).returns(z.void()), + storeCapsule: z.function().args(schemas.AztecAddress, schemas.Fr, z.array(schemas.Fr)).returns(z.void()), registerAccount: z.function().args(schemas.Fr, schemas.Fr).returns(CompleteAddress.schema), getRegisteredAccounts: z.function().returns(z.array(CompleteAddress.schema)), registerSender: z.function().args(schemas.AztecAddress).returns(schemas.AztecAddress), diff --git a/yarn-project/end-to-end/src/e2e_pxe_db.test.ts b/yarn-project/end-to-end/src/e2e_pxe_db.test.ts deleted file mode 100644 index 8c8430085c7..00000000000 --- a/yarn-project/end-to-end/src/e2e_pxe_db.test.ts +++ /dev/null @@ -1,50 +0,0 @@ -import { Fr, type Wallet } from '@aztec/aztec.js'; -import { TestContract } from '@aztec/noir-contracts.js/Test'; - -import { jest } from '@jest/globals'; - -import { setup } from './fixtures/utils.js'; - -const TIMEOUT = 120_000; - -// TODO(#10724): Nuke this once the linked issue is implemented (then the code will be well-tested). There is also -// a TXE test in `pxe_db.nr` but I decided to keep this ugly test around as it tests the PXE oracle callback handler -// (which is not tested by the TXE test). Dont't forget to remove `store_in_pxe_db` and `load_from_pxe_db` from -// the test contract when removing this test. -describe('PXE db', () => { - jest.setTimeout(TIMEOUT); - - let teardown: () => Promise; - - let testContract: TestContract; - - beforeAll(async () => { - let wallet: Wallet; - ({ teardown, wallet } = await setup(1)); - testContract = await TestContract.deploy(wallet).send().deployed(); - }); - - afterAll(() => teardown()); - - it('stores and loads data', async () => { - // In this test we feed arbitrary struct to a test contract, the test contract stores it in the PXE db and then - // we load it back. - const arbitraryStruct = { - a: Fr.random(), - b: Fr.random(), - }; - - const key = 6n; - await testContract.methods.store_in_pxe_db(key, arbitraryStruct).simulate(); - - // Now we try to load the data back from the PXE db. - const expectedReturnValue = [arbitraryStruct.a, arbitraryStruct.b].map(v => v.toBigInt()); - expect(await testContract.methods.load_from_pxe_db(key).simulate()).toEqual(expectedReturnValue); - }); - - it('handles non-existent data', async () => { - // In this test we try to load a key from the PXE db that does not exist. We should get an array of zeros. - const key = 7n; - expect(await testContract.methods.load_from_pxe_db(key).simulate()).toEqual([0n, 0n]); - }); -}); diff --git a/yarn-project/pxe/src/database/kv_pxe_database.ts b/yarn-project/pxe/src/database/kv_pxe_database.ts index e205ecb3d81..936e86490d4 100644 --- a/yarn-project/pxe/src/database/kv_pxe_database.ts +++ b/yarn-project/pxe/src/database/kv_pxe_database.ts @@ -64,7 +64,7 @@ export class KVPxeDatabase implements PxeDatabase { #taggingSecretIndexesForRecipients: AztecAsyncMap; // Arbitrary data stored by contracts. Key is computed as `${contractAddress}:${key}` - #contractStore: AztecAsyncMap; + #capsules: AztecAsyncMap; debug: LogFn; @@ -105,7 +105,7 @@ export class KVPxeDatabase implements PxeDatabase { this.#taggingSecretIndexesForSenders = db.openMap('tagging_secret_indexes_for_senders'); this.#taggingSecretIndexesForRecipients = db.openMap('tagging_secret_indexes_for_recipients'); - this.#contractStore = db.openMap('contract_store'); + this.#capsules = db.openMap('capsules'); this.debug = createDebugOnlyLogger('aztec:kv-pxe-database'); } @@ -616,31 +616,28 @@ export class KVPxeDatabase implements PxeDatabase { }); } - async dbStore(contractAddress: AztecAddress, slot: Fr, values: Fr[]): Promise { - await this.#contractStore.set( - dbSlotToKey(contractAddress, slot), - Buffer.concat(values.map(value => value.toBuffer())), - ); + async storeCapsule(contractAddress: AztecAddress, slot: Fr, capsule: Fr[]): Promise { + await this.#capsules.set(dbSlotToKey(contractAddress, slot), Buffer.concat(capsule.map(value => value.toBuffer()))); } - async dbLoad(contractAddress: AztecAddress, slot: Fr): Promise { - const dataBuffer = await this.#contractStore.getAsync(dbSlotToKey(contractAddress, slot)); + async loadCapsule(contractAddress: AztecAddress, slot: Fr): Promise { + const dataBuffer = await this.#capsules.getAsync(dbSlotToKey(contractAddress, slot)); if (!dataBuffer) { this.debug(`Data not found for contract ${contractAddress.toString()} and slot ${slot.toString()}`); return null; } - const values: Fr[] = []; + const capsule: Fr[] = []; for (let i = 0; i < dataBuffer.length; i += Fr.SIZE_IN_BYTES) { - values.push(Fr.fromBuffer(dataBuffer.subarray(i, i + Fr.SIZE_IN_BYTES))); + capsule.push(Fr.fromBuffer(dataBuffer.subarray(i, i + Fr.SIZE_IN_BYTES))); } - return values; + return capsule; } - async dbDelete(contractAddress: AztecAddress, slot: Fr): Promise { - await this.#contractStore.delete(dbSlotToKey(contractAddress, slot)); + async deleteCapsule(contractAddress: AztecAddress, slot: Fr): Promise { + await this.#capsules.delete(dbSlotToKey(contractAddress, slot)); } - async dbCopy(contractAddress: AztecAddress, srcSlot: Fr, dstSlot: Fr, numEntries: number): Promise { + async copyCapsule(contractAddress: AztecAddress, srcSlot: Fr, dstSlot: Fr, numEntries: number): Promise { // In order to support overlapping source and destination regions, we need to check the relative positions of source // and destination. If destination is ahead of source, then by the time we overwrite source elements using forward // indexes we'll have already read those. On the contrary, if source is ahead of destination we need to use backward @@ -655,12 +652,12 @@ export class KVPxeDatabase implements PxeDatabase { const currentSrcSlot = dbSlotToKey(contractAddress, srcSlot.add(new Fr(i))); const currentDstSlot = dbSlotToKey(contractAddress, dstSlot.add(new Fr(i))); - const toCopy = await this.#contractStore.getAsync(currentSrcSlot); + const toCopy = await this.#capsules.getAsync(currentSrcSlot); if (!toCopy) { throw new Error(`Attempted to copy empty slot ${currentSrcSlot} for contract ${contractAddress.toString()}`); } - await this.#contractStore.set(currentDstSlot, toCopy); + await this.#capsules.set(currentDstSlot, toCopy); } } } diff --git a/yarn-project/pxe/src/database/pxe_database.ts b/yarn-project/pxe/src/database/pxe_database.ts index 72e7a87bb95..f00de303703 100644 --- a/yarn-project/pxe/src/database/pxe_database.ts +++ b/yarn-project/pxe/src/database/pxe_database.ts @@ -201,32 +201,35 @@ export interface PxeDatabase extends ContractArtifactDatabase, ContractInstanceD resetNoteSyncData(): Promise; /** - * Stores arbitrary information in a per-contract non-volatile database, which can later be retrieved with `dbLoad`. - * If data was already stored at this slot, it is overwritten. + * Stores arbitrary information in a per-contract non-volatile database (called capsules), which can later + * be retrieved with `loadCapsule`. If data was already stored at this slot, it is overwritten. * @param contractAddress - The contract address to scope the data under. * @param slot - The slot in the database in which to store the value. Slots need not be contiguous. - * @param values - The data to store. + * @param capsule - An array of field elements representing the capsule. + * @remarks A capsule is a "blob" of data that is passed to the contract through an oracle. It works similarly + * to public contract storage in that it's indexed by the contract address and storage slot but instead of the global + * network state it's backed by local PXE db. */ - dbStore(contractAddress: AztecAddress, slot: Fr, values: Fr[]): Promise; + storeCapsule(contractAddress: AztecAddress, slot: Fr, capsule: Fr[]): Promise; /** - * Returns data previously stored via `dbStore` in the per-contract non-volatile database. + * Returns data previously stored via `storeCapsule` in the per-contract non-volatile database (called capsules). * @param contractAddress - The contract address under which the data is scoped. * @param slot - The slot in the database to read. * @returns The stored data or `null` if no data is stored under the slot. */ - dbLoad(contractAddress: AztecAddress, slot: Fr): Promise; + loadCapsule(contractAddress: AztecAddress, slot: Fr): Promise; /** - * Deletes data in the per-contract non-volatile database. Does nothing if no data was present. + * Deletes data in the per-contract non-volatile database (called capsules). Does nothing if no data was present. * @param contractAddress - The contract address under which the data is scoped. * @param slot - The slot in the database to delete. */ - dbDelete(contractAddress: AztecAddress, slot: Fr): Promise; + deleteCapsule(contractAddress: AztecAddress, slot: Fr): Promise; /** - * Copies a number of contiguous entries in the per-contract non-volatile database. This allows for efficient data - * structures by avoiding repeated calls to `dbLoad` and `dbStore`. + * Copies a number of contiguous entries in the per-contract non-volatile database (called capsules). This allows for + * efficient data structures by avoiding repeated calls to `loadCapsule` and `storeCapsule`. * Supports overlapping source and destination regions (which will result in the overlapped source values being * overwritten). All copied slots must exist in the database (i.e. have been stored and not deleted) * @@ -235,5 +238,5 @@ export interface PxeDatabase extends ContractArtifactDatabase, ContractInstanceD * @param dstSlot - The first slot to copy to. * @param numEntries - The number of entries to copy. */ - dbCopy(contractAddress: AztecAddress, srcSlot: Fr, dstSlot: Fr, numEntries: number): Promise; + copyCapsule(contractAddress: AztecAddress, srcSlot: Fr, dstSlot: Fr, numEntries: number): Promise; } diff --git a/yarn-project/pxe/src/database/pxe_database_test_suite.ts b/yarn-project/pxe/src/database/pxe_database_test_suite.ts index e6b5c5dee27..6d19df0fe88 100644 --- a/yarn-project/pxe/src/database/pxe_database_test_suite.ts +++ b/yarn-project/pxe/src/database/pxe_database_test_suite.ts @@ -410,8 +410,8 @@ export function describePxeDatabase(getDatabase: () => PxeDatabase) { const slot = new Fr(1); const values = [new Fr(42)]; - await database.dbStore(contract, slot, values); - const result = await database.dbLoad(contract, slot); + await database.storeCapsule(contract, slot, values); + const result = await database.loadCapsule(contract, slot); expect(result).toEqual(values); }); @@ -419,8 +419,8 @@ export function describePxeDatabase(getDatabase: () => PxeDatabase) { const slot = new Fr(1); const values = [new Fr(42), new Fr(43), new Fr(44)]; - await database.dbStore(contract, slot, values); - const result = await database.dbLoad(contract, slot); + await database.storeCapsule(contract, slot, values); + const result = await database.loadCapsule(contract, slot); expect(result).toEqual(values); }); @@ -429,10 +429,10 @@ export function describePxeDatabase(getDatabase: () => PxeDatabase) { const initialValues = [new Fr(42)]; const newValues = [new Fr(100)]; - await database.dbStore(contract, slot, initialValues); - await database.dbStore(contract, slot, newValues); + await database.storeCapsule(contract, slot, initialValues); + await database.storeCapsule(contract, slot, newValues); - const result = await database.dbLoad(contract, slot); + const result = await database.loadCapsule(contract, slot); expect(result).toEqual(newValues); }); @@ -442,11 +442,11 @@ export function describePxeDatabase(getDatabase: () => PxeDatabase) { const values1 = [new Fr(42)]; const values2 = [new Fr(100)]; - await database.dbStore(contract, slot, values1); - await database.dbStore(anotherContract, slot, values2); + await database.storeCapsule(contract, slot, values1); + await database.storeCapsule(anotherContract, slot, values2); - const result1 = await database.dbLoad(contract, slot); - const result2 = await database.dbLoad(anotherContract, slot); + const result1 = await database.loadCapsule(contract, slot); + const result2 = await database.loadCapsule(anotherContract, slot); expect(result1).toEqual(values1); expect(result2).toEqual(values2); @@ -454,7 +454,7 @@ export function describePxeDatabase(getDatabase: () => PxeDatabase) { it('returns null for non-existent slots', async () => { const slot = Fr.random(); - const result = await database.dbLoad(contract, slot); + const result = await database.loadCapsule(contract, slot); expect(result).toBeNull(); }); @@ -462,99 +462,99 @@ export function describePxeDatabase(getDatabase: () => PxeDatabase) { const slot = new Fr(1); const values = [new Fr(42)]; - await database.dbStore(contract, slot, values); - await database.dbDelete(contract, slot); + await database.storeCapsule(contract, slot, values); + await database.deleteCapsule(contract, slot); - expect(await database.dbLoad(contract, slot)).toBeNull(); + expect(await database.loadCapsule(contract, slot)).toBeNull(); }); it('deletes an empty slot', async () => { const slot = new Fr(1); - await database.dbDelete(contract, slot); + await database.deleteCapsule(contract, slot); - expect(await database.dbLoad(contract, slot)).toBeNull(); + expect(await database.loadCapsule(contract, slot)).toBeNull(); }); it('copies a single value', async () => { const slot = new Fr(1); const values = [new Fr(42)]; - await database.dbStore(contract, slot, values); + await database.storeCapsule(contract, slot, values); const dstSlot = new Fr(5); - await database.dbCopy(contract, slot, dstSlot, 1); + await database.copyCapsule(contract, slot, dstSlot, 1); - expect(await database.dbLoad(contract, dstSlot)).toEqual(values); + expect(await database.loadCapsule(contract, dstSlot)).toEqual(values); }); it('copies multiple non-overlapping values', async () => { const src = new Fr(1); const valuesArray = [[new Fr(42)], [new Fr(1337)], [new Fr(13)]]; - await database.dbStore(contract, src, valuesArray[0]); - await database.dbStore(contract, src.add(new Fr(1)), valuesArray[1]); - await database.dbStore(contract, src.add(new Fr(2)), valuesArray[2]); + await database.storeCapsule(contract, src, valuesArray[0]); + await database.storeCapsule(contract, src.add(new Fr(1)), valuesArray[1]); + await database.storeCapsule(contract, src.add(new Fr(2)), valuesArray[2]); const dst = new Fr(5); - await database.dbCopy(contract, src, dst, 3); + await database.copyCapsule(contract, src, dst, 3); - expect(await database.dbLoad(contract, dst)).toEqual(valuesArray[0]); - expect(await database.dbLoad(contract, dst.add(new Fr(1)))).toEqual(valuesArray[1]); - expect(await database.dbLoad(contract, dst.add(new Fr(2)))).toEqual(valuesArray[2]); + expect(await database.loadCapsule(contract, dst)).toEqual(valuesArray[0]); + expect(await database.loadCapsule(contract, dst.add(new Fr(1)))).toEqual(valuesArray[1]); + expect(await database.loadCapsule(contract, dst.add(new Fr(2)))).toEqual(valuesArray[2]); }); it('copies overlapping values with src ahead', async () => { const src = new Fr(1); const valuesArray = [[new Fr(42)], [new Fr(1337)], [new Fr(13)]]; - await database.dbStore(contract, src, valuesArray[0]); - await database.dbStore(contract, src.add(new Fr(1)), valuesArray[1]); - await database.dbStore(contract, src.add(new Fr(2)), valuesArray[2]); + await database.storeCapsule(contract, src, valuesArray[0]); + await database.storeCapsule(contract, src.add(new Fr(1)), valuesArray[1]); + await database.storeCapsule(contract, src.add(new Fr(2)), valuesArray[2]); const dst = new Fr(2); - await database.dbCopy(contract, src, dst, 3); + await database.copyCapsule(contract, src, dst, 3); - expect(await database.dbLoad(contract, dst)).toEqual(valuesArray[0]); - expect(await database.dbLoad(contract, dst.add(new Fr(1)))).toEqual(valuesArray[1]); - expect(await database.dbLoad(contract, dst.add(new Fr(2)))).toEqual(valuesArray[2]); + expect(await database.loadCapsule(contract, dst)).toEqual(valuesArray[0]); + expect(await database.loadCapsule(contract, dst.add(new Fr(1)))).toEqual(valuesArray[1]); + expect(await database.loadCapsule(contract, dst.add(new Fr(2)))).toEqual(valuesArray[2]); // Slots 2 and 3 (src[1] and src[2]) should have been overwritten since they are also dst[0] and dst[1] - expect(await database.dbLoad(contract, src)).toEqual(valuesArray[0]); // src[0] (unchanged) - expect(await database.dbLoad(contract, src.add(new Fr(1)))).toEqual(valuesArray[0]); // dst[0] - expect(await database.dbLoad(contract, src.add(new Fr(2)))).toEqual(valuesArray[1]); // dst[1] + expect(await database.loadCapsule(contract, src)).toEqual(valuesArray[0]); // src[0] (unchanged) + expect(await database.loadCapsule(contract, src.add(new Fr(1)))).toEqual(valuesArray[0]); // dst[0] + expect(await database.loadCapsule(contract, src.add(new Fr(2)))).toEqual(valuesArray[1]); // dst[1] }); it('copies overlapping values with dst ahead', async () => { const src = new Fr(5); const valuesArray = [[new Fr(42)], [new Fr(1337)], [new Fr(13)]]; - await database.dbStore(contract, src, valuesArray[0]); - await database.dbStore(contract, src.add(new Fr(1)), valuesArray[1]); - await database.dbStore(contract, src.add(new Fr(2)), valuesArray[2]); + await database.storeCapsule(contract, src, valuesArray[0]); + await database.storeCapsule(contract, src.add(new Fr(1)), valuesArray[1]); + await database.storeCapsule(contract, src.add(new Fr(2)), valuesArray[2]); const dst = new Fr(4); - await database.dbCopy(contract, src, dst, 3); + await database.copyCapsule(contract, src, dst, 3); - expect(await database.dbLoad(contract, dst)).toEqual(valuesArray[0]); - expect(await database.dbLoad(contract, dst.add(new Fr(1)))).toEqual(valuesArray[1]); - expect(await database.dbLoad(contract, dst.add(new Fr(2)))).toEqual(valuesArray[2]); + expect(await database.loadCapsule(contract, dst)).toEqual(valuesArray[0]); + expect(await database.loadCapsule(contract, dst.add(new Fr(1)))).toEqual(valuesArray[1]); + expect(await database.loadCapsule(contract, dst.add(new Fr(2)))).toEqual(valuesArray[2]); // Slots 5 and 6 (src[0] and src[1]) should have been overwritten since they are also dst[1] and dst[2] - expect(await database.dbLoad(contract, src)).toEqual(valuesArray[1]); // dst[1] - expect(await database.dbLoad(contract, src.add(new Fr(1)))).toEqual(valuesArray[2]); // dst[2] - expect(await database.dbLoad(contract, src.add(new Fr(2)))).toEqual(valuesArray[2]); // src[2] (unchanged) + expect(await database.loadCapsule(contract, src)).toEqual(valuesArray[1]); // dst[1] + expect(await database.loadCapsule(contract, src.add(new Fr(1)))).toEqual(valuesArray[2]); // dst[2] + expect(await database.loadCapsule(contract, src.add(new Fr(2)))).toEqual(valuesArray[2]); // src[2] (unchanged) }); it('copying fails if any value is empty', async () => { const src = new Fr(1); const valuesArray = [[new Fr(42)], [new Fr(1337)], [new Fr(13)]]; - await database.dbStore(contract, src, valuesArray[0]); + await database.storeCapsule(contract, src, valuesArray[0]); // We skip src[1] - await database.dbStore(contract, src.add(new Fr(2)), valuesArray[2]); + await database.storeCapsule(contract, src.add(new Fr(2)), valuesArray[2]); const dst = new Fr(5); - await expect(database.dbCopy(contract, src, dst, 3)).rejects.toThrow('Attempted to copy empty slot'); + await expect(database.copyCapsule(contract, src, dst, 3)).rejects.toThrow('Attempted to copy empty slot'); }); }); }); diff --git a/yarn-project/pxe/src/pxe_service/pxe_service.ts b/yarn-project/pxe/src/pxe_service/pxe_service.ts index 48c70c6b81e..95e89d81f07 100644 --- a/yarn-project/pxe/src/pxe_service/pxe_service.ts +++ b/yarn-project/pxe/src/pxe_service/pxe_service.ts @@ -137,8 +137,8 @@ export class PXEService implements PXE { return this.db.getAuthWitness(messageHash); } - public addCapsule(contract: AztecAddress, storageSlot: Fr, capsule: Fr[]) { - return this.db.dbStore(contract, storageSlot, capsule); + public storeCapsule(contract: AztecAddress, storageSlot: Fr, capsule: Fr[]) { + return this.db.storeCapsule(contract, storageSlot, capsule); } public getContractInstance(address: AztecAddress): Promise { diff --git a/yarn-project/pxe/src/simulator_oracle/index.ts b/yarn-project/pxe/src/simulator_oracle/index.ts index b2c6d2fb405..f0dd865c11c 100644 --- a/yarn-project/pxe/src/simulator_oracle/index.ts +++ b/yarn-project/pxe/src/simulator_oracle/index.ts @@ -825,20 +825,20 @@ export class SimulatorOracle implements DBOracle { ); } - dbStore(contractAddress: AztecAddress, slot: Fr, values: Fr[]): Promise { - return this.db.dbStore(contractAddress, slot, values); + storeCapsule(contractAddress: AztecAddress, slot: Fr, capsule: Fr[]): Promise { + return this.db.storeCapsule(contractAddress, slot, capsule); } - dbLoad(contractAddress: AztecAddress, slot: Fr): Promise { - return this.db.dbLoad(contractAddress, slot); + loadCapsule(contractAddress: AztecAddress, slot: Fr): Promise { + return this.db.loadCapsule(contractAddress, slot); } - dbDelete(contractAddress: AztecAddress, slot: Fr): Promise { - return this.db.dbDelete(contractAddress, slot); + deleteCapsule(contractAddress: AztecAddress, slot: Fr): Promise { + return this.db.deleteCapsule(contractAddress, slot); } - dbCopy(contractAddress: AztecAddress, srcSlot: Fr, dstSlot: Fr, numEntries: number): Promise { - return this.db.dbCopy(contractAddress, srcSlot, dstSlot, numEntries); + copyCapsule(contractAddress: AztecAddress, srcSlot: Fr, dstSlot: Fr, numEntries: number): Promise { + return this.db.copyCapsule(contractAddress, srcSlot, dstSlot, numEntries); } } diff --git a/yarn-project/simulator/src/acvm/oracle/oracle.ts b/yarn-project/simulator/src/acvm/oracle/oracle.ts index c0129cd3535..51f78bfc317 100644 --- a/yarn-project/simulator/src/acvm/oracle/oracle.ts +++ b/yarn-project/simulator/src/acvm/oracle/oracle.ts @@ -396,20 +396,20 @@ export class Oracle { return toACVMField(true); } - async dbStore([contractAddress]: ACVMField[], [slot]: ACVMField[], values: ACVMField[]) { - await this.typedOracle.dbStore( + async storeCapsule([contractAddress]: ACVMField[], [slot]: ACVMField[], capsule: ACVMField[]) { + await this.typedOracle.storeCapsule( AztecAddress.fromField(fromACVMField(contractAddress)), fromACVMField(slot), - values.map(fromACVMField), + capsule.map(fromACVMField), ); } - async dbLoad( + async loadCapsule( [contractAddress]: ACVMField[], [slot]: ACVMField[], [tSize]: ACVMField[], ): Promise<(ACVMField | ACVMField[])[]> { - const values = await this.typedOracle.dbLoad( + const values = await this.typedOracle.loadCapsule( AztecAddress.fromField(fromACVMField(contractAddress)), fromACVMField(slot), ); @@ -425,17 +425,17 @@ export class Oracle { } } - async dbDelete([contractAddress]: ACVMField[], [slot]: ACVMField[]) { - await this.typedOracle.dbDelete(AztecAddress.fromField(fromACVMField(contractAddress)), fromACVMField(slot)); + async deleteCapsule([contractAddress]: ACVMField[], [slot]: ACVMField[]) { + await this.typedOracle.deleteCapsule(AztecAddress.fromField(fromACVMField(contractAddress)), fromACVMField(slot)); } - async dbCopy( + async copyCapsule( [contractAddress]: ACVMField[], [srcSlot]: ACVMField[], [dstSlot]: ACVMField[], [numEntries]: ACVMField[], ) { - await this.typedOracle.dbCopy( + await this.typedOracle.copyCapsule( AztecAddress.fromField(fromACVMField(contractAddress)), fromACVMField(srcSlot), fromACVMField(dstSlot), diff --git a/yarn-project/simulator/src/acvm/oracle/typed_oracle.ts b/yarn-project/simulator/src/acvm/oracle/typed_oracle.ts index 74ba9884ed4..7b87099bdce 100644 --- a/yarn-project/simulator/src/acvm/oracle/typed_oracle.ts +++ b/yarn-project/simulator/src/acvm/oracle/typed_oracle.ts @@ -242,19 +242,19 @@ export abstract class TypedOracle { throw new OracleMethodNotAvailableError('deliverNote'); } - dbStore(_contractAddress: AztecAddress, _key: Fr, _values: Fr[]): Promise { - throw new OracleMethodNotAvailableError('dbStore'); + storeCapsule(_contractAddress: AztecAddress, _key: Fr, _capsule: Fr[]): Promise { + throw new OracleMethodNotAvailableError('storeCapsule'); } - dbLoad(_contractAddress: AztecAddress, _key: Fr): Promise { - throw new OracleMethodNotAvailableError('dbLoad'); + loadCapsule(_contractAddress: AztecAddress, _key: Fr): Promise { + throw new OracleMethodNotAvailableError('loadCapsule'); } - dbDelete(_contractAddress: AztecAddress, _key: Fr): Promise { - throw new OracleMethodNotAvailableError('dbDelete'); + deleteCapsule(_contractAddress: AztecAddress, _key: Fr): Promise { + throw new OracleMethodNotAvailableError('deleteCapsule'); } - dbCopy(_contractAddress: AztecAddress, _srcKey: Fr, _dstKey: Fr, _numEntries: number): Promise { - throw new OracleMethodNotAvailableError('dbCopy'); + copyCapsule(_contractAddress: AztecAddress, _srcKey: Fr, _dstKey: Fr, _numEntries: number): Promise { + throw new OracleMethodNotAvailableError('copyCapsule'); } } diff --git a/yarn-project/simulator/src/client/db_oracle.ts b/yarn-project/simulator/src/client/db_oracle.ts index ecbd2828175..05c5a9ec113 100644 --- a/yarn-project/simulator/src/client/db_oracle.ts +++ b/yarn-project/simulator/src/client/db_oracle.ts @@ -256,32 +256,35 @@ export interface DBOracle extends CommitmentsDB { removeNullifiedNotes(contractAddress: AztecAddress): Promise; /** - * Stores arbitrary information in a per-contract non-volatile database, which can later be retrieved with `dbLoad`. - * * If data was already stored at this slot, it is overwrriten. + * Stores arbitrary information in a per-contract non-volatile database, which can later be retrieved with `loadCapsule`. + * * If data was already stored at this slot, it is overwritten. * @param contractAddress - The contract address to scope the data under. * @param slot - The slot in the database in which to store the value. Slots need not be contiguous. - * @param values - The data to store. + * @param capsule - An array of field elements representing the capsule. + * @remarks A capsule is a "blob" of data that is passed to the contract through an oracle. It works similarly + * to public contract storage in that it's indexed by the contract address and storage slot but instead of the global + * network state it's backed by local PXE db. */ - dbStore(contractAddress: AztecAddress, slot: Fr, values: Fr[]): Promise; + storeCapsule(contractAddress: AztecAddress, slot: Fr, capsule: Fr[]): Promise; /** - * Returns data previously stored via `dbStore` in the per-contract non-volatile database. + * Returns data previously stored via `storeCapsule` in the per-contract non-volatile database. * @param contractAddress - The contract address under which the data is scoped. * @param slot - The slot in the database to read. * @returns The stored data or `null` if no data is stored under the slot. */ - dbLoad(contractAddress: AztecAddress, slot: Fr): Promise; + loadCapsule(contractAddress: AztecAddress, slot: Fr): Promise; /** * Deletes data in the per-contract non-volatile database. Does nothing if no data was present. * @param contractAddress - The contract address under which the data is scoped. * @param slot - The slot in the database to delete. */ - dbDelete(contractAddress: AztecAddress, slot: Fr): Promise; + deleteCapsule(contractAddress: AztecAddress, slot: Fr): Promise; /** * Copies a number of contiguous entries in the per-contract non-volatile database. This allows for efficient data - * structures by avoiding repeated calls to `dbLoad` and `dbStore`. + * structures by avoiding repeated calls to `loadCapsule` and `storeCapsule`. * Supports overlapping source and destination regions (which will result in the overlapped source values being * overwritten). All copied slots must exist in the database (i.e. have been stored and not deleted) * @@ -290,5 +293,5 @@ export interface DBOracle extends CommitmentsDB { * @param dstSlot - The first slot to copy to. * @param numEntries - The number of entries to copy. */ - dbCopy(contractAddress: AztecAddress, srcSlot: Fr, dstSlot: Fr, numEntries: number): Promise; + copyCapsule(contractAddress: AztecAddress, srcSlot: Fr, dstSlot: Fr, numEntries: number): Promise; } diff --git a/yarn-project/simulator/src/client/view_data_oracle.ts b/yarn-project/simulator/src/client/view_data_oracle.ts index 26fef8e003a..d43c8c87bfe 100644 --- a/yarn-project/simulator/src/client/view_data_oracle.ts +++ b/yarn-project/simulator/src/client/view_data_oracle.ts @@ -319,35 +319,40 @@ export class ViewDataOracle extends TypedOracle { await this.db.deliverNote(contractAddress, storageSlot, nonce, content, noteHash, nullifier, txHash, recipient); } - public override dbStore(contractAddress: AztecAddress, slot: Fr, values: Fr[]): Promise { + public override storeCapsule(contractAddress: AztecAddress, slot: Fr, capsule: Fr[]): Promise { if (!contractAddress.equals(this.contractAddress)) { // TODO(#10727): instead of this check that this.contractAddress is allowed to access the external DB throw new Error(`Contract ${contractAddress} is not allowed to access ${this.contractAddress}'s PXE DB`); } - return this.db.dbStore(this.contractAddress, slot, values); + return this.db.storeCapsule(this.contractAddress, slot, capsule); } - public override dbLoad(contractAddress: AztecAddress, slot: Fr): Promise { + public override loadCapsule(contractAddress: AztecAddress, slot: Fr): Promise { if (!contractAddress.equals(this.contractAddress)) { // TODO(#10727): instead of this check that this.contractAddress is allowed to access the external DB throw new Error(`Contract ${contractAddress} is not allowed to access ${this.contractAddress}'s PXE DB`); } - return this.db.dbLoad(this.contractAddress, slot); + return this.db.loadCapsule(this.contractAddress, slot); } - public override dbDelete(contractAddress: AztecAddress, slot: Fr): Promise { + public override deleteCapsule(contractAddress: AztecAddress, slot: Fr): Promise { if (!contractAddress.equals(this.contractAddress)) { // TODO(#10727): instead of this check that this.contractAddress is allowed to access the external DB throw new Error(`Contract ${contractAddress} is not allowed to access ${this.contractAddress}'s PXE DB`); } - return this.db.dbDelete(this.contractAddress, slot); + return this.db.deleteCapsule(this.contractAddress, slot); } - public override dbCopy(contractAddress: AztecAddress, srcSlot: Fr, dstSlot: Fr, numEntries: number): Promise { + public override copyCapsule( + contractAddress: AztecAddress, + srcSlot: Fr, + dstSlot: Fr, + numEntries: number, + ): Promise { if (!contractAddress.equals(this.contractAddress)) { // TODO(#10727): instead of this check that this.contractAddress is allowed to access the external DB throw new Error(`Contract ${contractAddress} is not allowed to access ${this.contractAddress}'s PXE DB`); } - return this.db.dbCopy(this.contractAddress, srcSlot, dstSlot, numEntries); + return this.db.copyCapsule(this.contractAddress, srcSlot, dstSlot, numEntries); } } diff --git a/yarn-project/txe/src/oracle/txe_oracle.ts b/yarn-project/txe/src/oracle/txe_oracle.ts index 0ca041aaba1..9b79fe13f0a 100644 --- a/yarn-project/txe/src/oracle/txe_oracle.ts +++ b/yarn-project/txe/src/oracle/txe_oracle.ts @@ -1156,35 +1156,35 @@ export class TXE implements TypedOracle { return preimage.value; } - dbStore(contractAddress: AztecAddress, slot: Fr, values: Fr[]): Promise { + storeCapsule(contractAddress: AztecAddress, slot: Fr, capsule: Fr[]): Promise { if (!contractAddress.equals(this.contractAddress)) { // TODO(#10727): instead of this check that this.contractAddress is allowed to access the external DB throw new Error(`Contract ${contractAddress} is not allowed to access ${this.contractAddress}'s PXE DB`); } - return this.txeDatabase.dbStore(this.contractAddress, slot, values); + return this.txeDatabase.storeCapsule(this.contractAddress, slot, capsule); } - dbLoad(contractAddress: AztecAddress, slot: Fr): Promise { + loadCapsule(contractAddress: AztecAddress, slot: Fr): Promise { if (!contractAddress.equals(this.contractAddress)) { // TODO(#10727): instead of this check that this.contractAddress is allowed to access the external DB throw new Error(`Contract ${contractAddress} is not allowed to access ${this.contractAddress}'s PXE DB`); } - return this.txeDatabase.dbLoad(this.contractAddress, slot); + return this.txeDatabase.loadCapsule(this.contractAddress, slot); } - dbDelete(contractAddress: AztecAddress, slot: Fr): Promise { + deleteCapsule(contractAddress: AztecAddress, slot: Fr): Promise { if (!contractAddress.equals(this.contractAddress)) { // TODO(#10727): instead of this check that this.contractAddress is allowed to access the external DB throw new Error(`Contract ${contractAddress} is not allowed to access ${this.contractAddress}'s PXE DB`); } - return this.txeDatabase.dbDelete(this.contractAddress, slot); + return this.txeDatabase.deleteCapsule(this.contractAddress, slot); } - dbCopy(contractAddress: AztecAddress, srcSlot: Fr, dstSlot: Fr, numEntries: number): Promise { + copyCapsule(contractAddress: AztecAddress, srcSlot: Fr, dstSlot: Fr, numEntries: number): Promise { if (!contractAddress.equals(this.contractAddress)) { // TODO(#10727): instead of this check that this.contractAddress is allowed to access the external DB throw new Error(`Contract ${contractAddress} is not allowed to access ${this.contractAddress}'s PXE DB`); } - return this.txeDatabase.dbCopy(this.contractAddress, srcSlot, dstSlot, numEntries); + return this.txeDatabase.copyCapsule(this.contractAddress, srcSlot, dstSlot, numEntries); } } diff --git a/yarn-project/txe/src/txe_service/txe_service.ts b/yarn-project/txe/src/txe_service/txe_service.ts index b9553bb3f38..eb66ed5389e 100644 --- a/yarn-project/txe/src/txe_service/txe_service.ts +++ b/yarn-project/txe/src/txe_service/txe_service.ts @@ -534,17 +534,20 @@ export class TXEService { return toForeignCallResult([]); } - async dbStore(contractAddress: ForeignCallSingle, slot: ForeignCallSingle, values: ForeignCallArray) { - await this.typedOracle.dbStore( + async storeCapsule(contractAddress: ForeignCallSingle, slot: ForeignCallSingle, capsule: ForeignCallArray) { + await this.typedOracle.storeCapsule( AztecAddress.fromField(fromSingle(contractAddress)), fromSingle(slot), - fromArray(values), + fromArray(capsule), ); return toForeignCallResult([]); } - async dbLoad(contractAddress: ForeignCallSingle, slot: ForeignCallSingle, tSize: ForeignCallSingle) { - const values = await this.typedOracle.dbLoad(AztecAddress.fromField(fromSingle(contractAddress)), fromSingle(slot)); + async loadCapsule(contractAddress: ForeignCallSingle, slot: ForeignCallSingle, tSize: ForeignCallSingle) { + const values = await this.typedOracle.loadCapsule( + AztecAddress.fromField(fromSingle(contractAddress)), + fromSingle(slot), + ); // We are going to return a Noir Option struct to represent the possibility of null values. Options are a struct // with two fields: `some` (a boolean) and `value` (a field array in this case). if (values === null) { @@ -556,18 +559,18 @@ export class TXEService { } } - async dbDelete(contractAddress: ForeignCallSingle, slot: ForeignCallSingle) { - await this.typedOracle.dbDelete(AztecAddress.fromField(fromSingle(contractAddress)), fromSingle(slot)); + async deleteCapsule(contractAddress: ForeignCallSingle, slot: ForeignCallSingle) { + await this.typedOracle.deleteCapsule(AztecAddress.fromField(fromSingle(contractAddress)), fromSingle(slot)); return toForeignCallResult([]); } - async dbCopy( + async copyCapsule( contractAddress: ForeignCallSingle, srcSlot: ForeignCallSingle, dstSlot: ForeignCallSingle, numEntries: ForeignCallSingle, ) { - await this.typedOracle.dbCopy( + await this.typedOracle.copyCapsule( AztecAddress.fromField(fromSingle(contractAddress)), fromSingle(srcSlot), fromSingle(dstSlot),