Documentation
Docs11. KVM — Kortana Virtual Machine

11. KVM — Kortana Virtual Machine

The Kortana Virtual Machine (KVM) is a custom-engineered, capability-secure execution environment designed from first principles to deliver mathematical determinism, formal verification guarantees, and high-assurance smart contract execution. Operating side-by-side with the Kortana Ethereum Virtual Machine (KEVM) over a single, unified Merkle-Patricia State Trie, the KVM represents the foundational execution tier of the Kortana protocol (codified in the specification under §21 and implemented across Phase 3 Milestone 19).

Traditional virtual machines in the blockchain space evaluate untrusted bytecode dynamically at runtime, constantly checking instruction validity, scanning for variable-length opcode boundaries, and performing stack depth verifications on every executed operation. The KVM fundamentally transforms this paradigm by shifting verification entirely to deploy time. A deployed KVM contract is not an unstructured sequence of raw bytes, but a strictly validated, structured binary container known as a Module. When a contract module is deployed, the node's verification engine performs total static verification: it proves that every instruction is defined, every register operand is within bounds, every jump destination aligns with an instruction boundary, and every constant reference points to an existing pool entry. As a result, the KVM's high-speed execution loop in interpreter.cpp operates without runtime program counter bounds checks or opcode validity dispatch tests, achieving peak execution performance while maintaining absolute mathematical security.

11.1 Overview: Register Machine vs. Stack Machine

The fundamental architectural departure of the KVM from legacy blockchain execution environments is its design as a 32-Register Machine rather than a stack-based machine. In a traditional stack machine (such as the EVM), the execution state is governed by a 1024-element operand stack. Because stack machines can only manipulate operands residing at the very top of the stack, compilers must generate large sequences of non-computational stack-shuffling instructions—including DUP1 through DUP16, SWAP1 through SWAP16, and POP—simply to position values for arithmetic operations. In complex smart contracts, these stack manipulations consume up to 30% of total gas and frequently trigger "stack too deep" compiler failures when local variables exceed stack reach.

The KVM eliminates stack juggling entirely. By providing 32 general-purpose 256-bit registers (r0 through r31), instructions explicitly name their destination and source registers (rd, rs1, rs2). High-level expressions compile into clean, direct register allocations without intermediate shuffling. Furthermore, this design dramatically simplifies formal verification, static analysis, and symbolic execution. In a stack machine, tracking the dataflow of a variable requires modeling dynamic stack depths across all branches; in the KVM, dataflow graphs are explicit in the named register operands of each instruction. While individual KVM opcodes are priced identically to their EVM equivalents to prevent economic arbitrage, a KVM contract performs substantially fewer total instructions for the same business logic, resulting in lower net gas costs for end users.

11.2 Module Format

A deployed KVM smart contract is encapsulated in a structured binary format defined in kvm/module.hpp and kvm/module.cpp. Rather than storing bare, unstructured byte arrays, a KVM module consists of a structured header, an entry point pointer, an indexed constant pool, and a fixed-width code stream. This structural encapsulation enables total verification at deployment and provides deterministic code hashing.

The binary encoding of a KVM module consists of the following components:

  • Magic Prefix (4 bytes): The exact ASCII byte sequence 4B 56 4D 00 ("KVM\0"). This magic header ensures that EVM bytecode handed to the KVM—or KVM modules handed to the KEVM—is rejected immediately at the router boundary without misinterpretation.
  • Module Version (2 bytes): An unsigned 16-bit integer (currently strictly kModuleVersion = 1). Modules declaring unknown versions are rejected, enabling forward-compatible protocol upgrades without ambiguous interpretation.
  • Entry Point (4 bytes): A 32-bit unsigned integer specifying the instruction index where execution begins. For verified modules, this is guaranteed to be a valid index into the code section.
  • Constant Pool: A structured table containing up to kMaxConstants = 16,384 256-bit values (uint256_t). Full 256-bit numbers, addresses, and hash literals live in the constant pool and are loaded into registers via the LoadK opcode using a 14-bit immediate index (imm14). Small constants (0 to 16,383) are loaded directly via LoadI without consuming pool space.
  • Code Section: A stream of fixed-width 4-byte instructions bounded by kMaxInstructions = 1,048,576 (4 MiB of code).

The module format enforces perfect round-trip serialization: parse(m.encode()) == m. This property ensures that the canonical code hash (code_hash) stored in the Merkle-Patricia State Trie is calculated directly over the verified binary encoding.

11.3 Register Architecture

The KVM provides an execution context centered around 32 general-purpose 256-bit registers (r0 through r31), defined in kvm/isa.hpp and managed within the Interpreter::Machine execution frame. A complete register file occupies exactly 1,024 bytes (1 KiB) of memory—large enough that high-level compilers rarely need to spill variables to memory, yet compact enough to be zero-initialized with negligible CPU overhead on entry.

The register set enforces standardized architectural conventions across the runtime and ABI:

  • r0 (Hardwired Zero Register): In accordance with classical RISC processor architecture, register r0 is hardwired to constant zero (kZeroRegister = 0). Any read from r0 always yields 0x0, and any write to r0 is silently discarded. This single design choice eliminates the need for numerous specialized opcodes: clearing a register rd is executed as ADD rd, r0, r0, testing if register rs1 is zero is executed as EQ rd, rs1, r0, copying a register is ADD rd, rs1, r0, and discarding an unwanted instruction result is achieved simply by designating r0 as the destination register.
  • r1r7 (Argument & Return Registers): Reserved by the calling convention for passing function parameters into call frames and returning output values to callers.
  • r8r23 (General Computation Registers): Dedicated scratchpad registers used for arithmetic, bitwise logic, cryptographic hashing, and temporary state calculations.
  • r24r31 (Context & Frame Control Registers): Preserved registers used for managing execution context, callee-saved state, and internal subroutine call frames.

When a function call or external contract invocation occurs, an isolated Machine register window is instantiated. This ensures total frame encapsulation, preventing callee functions from mutating or inspecting the caller's registers.

11.4 Instruction Set Architecture (ISA)

The KVM Instruction Set Architecture consists of 75 specialized opcodes designed for cryptographic safety, high performance, and exact semantic alignment with blockchain execution invariants. Every instruction in the KVM is strictly 4 bytes wide (32 bits), codified under kInstructionSize = 4.

The 32-bit instruction word is partitioned into four canonical encoding formats:

  • Format R (Opcode | rd | rs1 | rs2 | funct): Encodes three 5-bit register operands (rd at bits [23:19], rs1 at bits [18:14], rs2 at bits [13:9]) and a 9-bit funct field (bits [8:0]) used to specify auxiliary parameters such as log topic counts. Used for arithmetic (Add, Sub, Mul), bitwise operations (And, Or, Xor), and memory moves (MCopy).
  • Format I (Opcode | rd | rs1 | imm14): Encodes two 5-bit register operands and a 14-bit unsigned immediate field (imm14 at bits [13:0], max value 16,383). Used for constant loading (LoadK, LoadI), conditional branching (JmpIf, JmpIfNot), and memory loads.
  • Format J (Opcode | imm24): Encodes an 8-bit opcode and a 24-bit unsigned immediate field (imm24 at bits [23:0], max value 16,777,215). Used for unconditional jumps (Jmp) and internal subroutine invocations (Call).
  • Format None (Opcode | unused): Encodes single opcodes (Stop, Ret, Invalid) where all remaining 24 bits must be strictly zero.

Fixed-width 32-bit instructions completely eliminate the infamous JUMPDEST security vulnerability found in variable-length bytecode architectures. In the EVM, an engine must dynamically scan the entire contract to prevent jumps from landing inside push-data literals. In the KVM, all instruction boundaries occur at exact 4-byte alignments; jump targets are instruction indices rather than byte offsets. Verifying a jump destination is a single numerical range comparison (target < code.size()) rather than a complex dynamic scan. Furthermore, all arithmetic opcodes enforce checked 256-bit integer arithmetic, triggering an immediate execution fault upon overflow or underflow unless wrapping operations are explicitly invoked.

11.5 System Calls & Cross-Contract Calls

Interactions between a KVM smart contract and the surrounding blockchain environment are handled through dedicated system call opcodes. These include execution context queries (such as Address, Caller, Origin, CallValue, GasPrice), block context accessors (BlockNumber, BlockTimestamp, BlockBaseFee, PohSequence), and external contract dispatch instructions.

Cross-contract execution is governed by four primary system call opcodes:

  • ExternalCall (0xC0): Initiates a standard, state-mutating external call to a target contract account, transferring optional native DNR value and passing input calldata from memory.
  • StaticCall (0xC1): Initiates a strictly read-only call to a target contract. During a static call, any attempt by the callee to execute state-mutating operations (SStore, TStore, Create, or value-bearing calls) triggers an immediate StaticCallViolation fault, reverting execution.
  • DelegateCall (0xC2): Executes code from a target contract within the context of the calling contract, preserving the current caller, value, and storage space.
  • Create (0xC3): Deploys a new contract module to a deterministically calculated address in the state trie.

Cross-contract calls route through the unified ExecutionRouter, seamlessly bridging calls between KVM and KEVM contracts. To protect against stack overflow attacks, the KVM enforces two independent call limits: internal subroutine calls (Call/Ret) are bounded by kMaxCallDepth = 1,024, while cross-contract interop calls are strictly bounded by kMaxInteropDepth = 128. In accordance with EIP-150, the calling frame retains at least 1/64 of its remaining gas (kGasRetainedDivisor = 64), guaranteeing sufficient gas to process return data or handle revert errors.

11.6 KVM Deployment

Contract deployment on the KVM differs fundamentally from the EVM's legacy init-code model. In the EVM, a deployment transaction transmits initialization bytecode which executes on the stack and returns runtime bytecode. This indirection incurs substantial deployment gas overhead, complicates formal verification, and creates security vulnerabilities during constructor execution.

In the KVM, deployment is direct and transparent:

  1. Module Ingestion: The deployment transaction carries the compiled .kvm module directly in its payload.
  2. Total Static Verification: Before executing any logic, Module::parse verifies the entire module: it checks the 4B 56 4D 00 magic prefix, validates the module version, verifies that instruction counts and constant pool entries are within limits, confirms that every instruction opcode is valid, and proves that all jump targets and constant indices are strictly in range.
  3. Constructor Execution: Once verified, the node initializes an interpreter frame and executes the module's constructor logic starting at entry_point. The constructor sets initial storage slots and validates parameters.
  4. State Trie Commitment: If the constructor completes successfully with HaltReason::Stopped or HaltReason::Returned, the verified module bytecode is committed directly to the code column family in RocksDB, and the contract's account state is initialized in the shared Merkle-Patricia State Trie.

This direct deployment model eliminates deployment-time memory expansion costs and ensures that unverified or corrupted bytecode can never enter the blockchain state.

11.7 Limits & Resource Bounds

To protect validating nodes against denial-of-service (DDoS) attacks, memory exhaustion, and infinite execution loops, the KVM enforces strict, non-negotiable architectural limits codified in kvm/module.hpp, kvm/interpreter.hpp, and kvm/gas.hpp:

  • Maximum Instructions per Module (kMaxInstructions): Bounded at 1,048,576 instructions (2^20 instructions, or 4 MiB of code). This limit matches the 24-bit jump address reach of the J-format instruction encoding, ensuring that all code is directly addressable without unreachable segments.
  • Maximum Constant Pool Entries (kMaxConstants): Capped at 16,384 constants (2^14 entries), strictly aligning with the 14-bit immediate addressing capacity of the LoadK opcode.
  • Maximum Encoded Module Size (kMaxModuleBytes): Hard ceiling of 8 MiB (8,388,608 bytes). Any deployment payload declaring a size exceeding this threshold is rejected prior to memory allocation.
  • Maximum Linear Memory (kMaxMemoryBytes): Capped at 32 MiB (33,554,432 bytes). Linear memory expansion is governed by the quadratic gas cost formula: Gas = 3n + floor(n^2 / 512) for n 32-byte words. This quadratic curve makes excessive memory allocation economically prohibitive long before reaching the 32 MiB ceiling.
  • Call Stack Limits: Internal module subroutine calls (Call/Ret) are bounded by kMaxCallDepth = 1,024 frames. Cross-contract nested call frames are strictly limited to kMaxInteropDepth = 128 to prevent host thread stack exhaustion.