Skip to content

Latest commit

 

History

History
270 lines (262 loc) · 10 KB

File metadata and controls

270 lines (262 loc) · 10 KB

Instruction Set: Quick Reference

Quick reference for all Aztec Virtual Machine (AVM) opcodes.

Supporting Materials

Before diving into the instruction set, familiarize yourself with these core concepts:

  • Introduction: What is the AVM and why do we need it?
  • State: World state (persistent) vs execution state (transient)
  • Memory Model: Memory notation and tagged memory (M[x] and T[x])
  • Addressing Modes: Direct, indirect, and relative addressing along with their gas implications
  • Execution Lifecycle: VM initialization, PC rules, halting, gas charging order
  • Gas Metering: How L2 and DA gas costs are calculated and charged during instruction execution
  • Errors: Error types, triggers, and gas/state behavior
  • Wire Formats: How instructions are encoded in bytecode and why opcodes have variants like ADD_8 and ADD_16

Quick Reference

Click on an opcode name to view its detailed documentation.

  • 🔗ADD: Addition (a + b)
    • Opcodes 0x00-0x01 (2 wire formats)
    M[dstOffset] = M[aOffset] + M[bOffset]
  • 🔗SUB: Subtraction (a - b)
    • Opcodes 0x02-0x03 (2 wire formats)
    M[dstOffset] = M[aOffset] - M[bOffset]
  • 🔗MUL: Multiplication (a * b)
    • Opcodes 0x04-0x05 (2 wire formats)
    M[dstOffset] = M[aOffset] * M[bOffset]
  • 🔗DIV: Integer division (a / b)
    • Opcodes 0x06-0x07 (2 wire formats)
    M[dstOffset] = M[aOffset] / M[bOffset]
  • 🔗FDIV: Field division (a / b)
    • Opcodes 0x08-0x09 (2 wire formats)
    M[dstOffset] = M[aOffset] / M[bOffset]
  • 🔗EQ: Equality check (a == b)
    • Opcodes 0x0A-0x0B (2 wire formats)
    M[dstOffset] = (M[aOffset] == M[bOffset]) ? 1 : 0
  • 🔗LT: Less than (a < b)
    • Opcodes 0x0C-0x0D (2 wire formats)
    M[dstOffset] = (M[aOffset] < M[bOffset]) ? 1 : 0
  • 🔗LTE: Less than or equal (a <= b)
    • Opcodes 0x0E-0x0F (2 wire formats)
    M[dstOffset] = (M[aOffset] <= M[bOffset]) ? 1 : 0
  • 🔗AND: Bitwise AND (a & b)
    • Opcodes 0x10-0x11 (2 wire formats)
    M[dstOffset] = M[aOffset] & M[bOffset]
  • 🔗OR: Bitwise OR (a | b)
    • Opcodes 0x12-0x13 (2 wire formats)
    M[dstOffset] = M[aOffset] | M[bOffset]
  • 🔗XOR: Bitwise XOR (a ^ b)
    • Opcodes 0x14-0x15 (2 wire formats)
    M[dstOffset] = M[aOffset] ^ M[bOffset]
  • 🔗NOT: Bitwise NOT (~a)
    • Opcodes 0x16-0x17 (2 wire formats)
    M[dstOffset] = ~M[srcOffset]
  • 🔗SHL: Shift left (a << b)
    • Opcodes 0x18-0x19 (2 wire formats)
    M[dstOffset] = M[aOffset] << M[bOffset]
  • 🔗SHR: Shift right (a >> b)
    • Opcodes 0x1A-0x1B (2 wire formats)
    M[dstOffset] = M[aOffset] >> M[bOffset]
  • 🔗CAST: Type cast memory value
    • Opcodes 0x1C-0x1D (2 wire formats)
    M[dstOffset] = M[srcOffset] as tag
  • 🔗GETENVVAR: Get environment variable
    • Opcode 0x1E
    M[dstOffset] = environmentVariable[varEnum]
  • 🔗CALLDATACOPY: Copy calldata to memory
    • Opcode 0x1F
    M[dstOffset:dstOffset+M[copySizeOffset]] = calldata[M[cdStartOffset]:M[cdStartOffset]+M[copySizeOffset]]
  • 🔗SUCCESSCOPY: Get success status of latest external call
    • Opcode 0x20
    M[dstOffset] = nestedCallSuccess ? 1 : 0
  • 🔗RETURNDATASIZE: Get returndata size
    • Opcode 0x21
    M[dstOffset] = nestedReturndata.length
  • 🔗RETURNDATACOPY: Copy returndata to memory
    • Opcode 0x22
    M[dstOffset:dstOffset+M[copySizeOffset]] = nestedReturndata[M[rdStartOffset]:M[rdStartOffset]+M[copySizeOffset]]
  • 🔗JUMP: Unconditional jump
    • Opcode 0x23
    PC = jumpOffset
  • 🔗JUMPI: Conditional jump
    • Opcode 0x24
    if M[condOffset] != 0 then PC = loc else PC = PC + instructionSize
  • 🔗INTERNALCALL: Internal function call
    • Opcode 0x25
    internalCallStack.push({callPc: PC, returnPc: PC + instructionSize}); PC = loc
  • 🔗INTERNALRETURN: Return from internal call
    • Opcode 0x26
    PC = internalCallStack.pop().returnPc
  • 🔗SET: Set memory to immediate value
    • Opcodes 0x27-0x2C (6 wire formats)
    M[dstOffset] = value
  • 🔗MOV: Move value between memory locations
    • Opcodes 0x2D-0x2E (2 wire formats)
    M[dstOffset] = M[srcOffset]
  • 🔗SLOAD: Load value from public storage
    • Opcode 0x2F
    M[dstOffset] = storage[contractAddress][M[slotOffset]]
  • 🔗SSTORE: Store value to public storage
    • Opcode 0x30
    storage[contractAddress][M[slotOffset]] = M[srcOffset]
  • 🔗NOTEHASHEXISTS: Check existence of note hash
    • Opcode 0x31
    M[existsOffset] = noteHashTree.exists(M[noteHashOffset], M[leafIndexOffset]) ? 1 : 0
  • 🔗EMITNOTEHASH: Emit note hash
    • Opcode 0x32
    noteHashes.append(M[noteHashOffset])
  • 🔗NULLIFIEREXISTS: Check existence of nullifier
    • Opcode 0x33
    M[existsOffset] = nullifierTree.exists(M[addressOffset], M[nullifierOffset]) ? 1 : 0
  • 🔗EMITNULLIFIER: Emit nullifier
    • Opcode 0x34
    nullifiers.append(M[nullifierOffset])
  • 🔗L1TOL2MSGEXISTS: Check existence of L1-to-L2 message
    • Opcode 0x35
    M[existsOffset] = l1ToL2Messages.exists(M[msgHashOffset], M[msgLeafIndexOffset]) ? 1 : 0
  • 🔗GETCONTRACTINSTANCE: Get contract instance information
    • Opcode 0x36
    M[dstOffset] = contractInstance.exists ? 1 : 0; M[dstOffset+1] = contractInstance[memberEnum]
  • 🔗EMITUNENCRYPTEDLOG: Emit public log
    • Opcode 0x37
    unencryptedLogs.append(M[logOffset:logOffset+M[logSizeOffset]])
  • 🔗SENDL2TOL1MSG: Send L2-to-L1 message
    • Opcode 0x38
    l2ToL1Messages.append({recipient: M[recipientOffset], content: M[contentOffset]})
  • 🔗CALL: Call external contract
    • Opcode 0x39
    nestedCallResult = executeContract(
        /*address=*/M[addrOffset],
        /*args=*/M[argsOffset:argsOffset+M[argsSizeOffset]],
        {l2Gas: M[l2GasOffset], daGas: M[daGasOffset]}
    )
  • 🔗STATICCALL: Static call to external contract
    • Opcode 0x3A
    nestedCallResult = executeContractStatic(
        /*address=*/M[addrOffset],
        /*args=*/M[argsOffset:argsOffset+M[argsSizeOffset]],
        {l2Gas: M[l2GasOffset], daGas: M[daGasOffset]}
    )
  • 🔗RETURN: Return from call
    • Opcode 0x3B
    return M[returnOffset:returnOffset+M[returnSizeOffset]]; halt
  • 🔗REVERT: Revert execution
    • Opcodes 0x3C-0x3D (2 wire formats)
    revert M[returnOffset:returnOffset+M[retSizeOffset]]; halt
  • 🔗DEBUGLOG: Output debug log
    • Opcode 0x3E
    debugLog(level, message, M[fieldsOffset:fieldsOffset+M[fieldsSizeOffset]])
  • 🔗POSEIDON2: Poseidon2 permutation
    • Opcode 0x3F
    M[outputStateOffset:outputStateOffset+4] = poseidon2Permutation(/*input=*/M[inputStateOffset:inputStateOffset+4])
  • 🔗SHA256COMPRESSION: SHA-256 compression
    • Opcode 0x40
    M[outputOffset:outputOffset+8] = sha256compress(/*state=*/M[stateOffset:stateOffset+8], /*inputs=*/M[inputsOffset:inputsOffset+16])
  • 🔗KECCAKF1600: Keccak-f[1600] permutation
    • Opcode 0x41
    M[dstOffset:dstOffset+25] = keccakf1600(/*input=*/M[inputOffset:inputOffset+25])
  • 🔗ECADD: Grumpkin elliptic curve addition
    • Opcode 0x42
    M[dstOffset:dstOffset+3] = grumpkinAdd(
        /*point1=*/{x: M[p1XOffset], y: M[p1YOffset], isInfinite: M[p1IsInfiniteOffset]},
        /*point2=*/{x: M[p2XOffset], y: M[p2YOffset], isInfinite: M[p2IsInfiniteOffset]}
    )
  • 🔗TORADIXBE: Convert to radix (big-endian)
    • Opcode 0x43
    M[dstOffset:dstOffset+M[numLimbsOffset]] = toRadixBE(
        /*value=*/M[srcOffset],
        /*radix=*/M[radixOffset],
        /*numLimbs=*/M[numLimbsOffset],
        /*outputBits=*/M[outputBitsOffset]
    )

← Previous: Wire Formats | Next: Tooling and Compilation