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,384256-bit values (uint256_t). Full 256-bit numbers, addresses, and hash literals live in the constant pool and are loaded into registers via theLoadKopcode using a 14-bit immediate index (imm14). Small constants (0 to 16,383) are loaded directly viaLoadIwithout 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, registerr0is hardwired to constant zero (kZeroRegister = 0). Any read fromr0always yields0x0, and any write tor0is silently discarded. This single design choice eliminates the need for numerous specialized opcodes: clearing a registerrdis executed asADD rd, r0, r0, testing if registerrs1is zero is executed asEQ rd, rs1, r0, copying a register isADD rd, rs1, r0, and discarding an unwanted instruction result is achieved simply by designatingr0as the destination register.r1–r7(Argument & Return Registers): Reserved by the calling convention for passing function parameters into call frames and returning output values to callers.r8–r23(General Computation Registers): Dedicated scratchpad registers used for arithmetic, bitwise logic, cryptographic hashing, and temporary state calculations.r24–r31(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 (rdat bits [23:19],rs1at bits [18:14],rs2at bits [13:9]) and a 9-bitfunctfield (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 (imm14at 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 (imm24at 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 immediateStaticCallViolationfault, 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:
- Module Ingestion: The deployment transaction carries the compiled
.kvmmodule directly in its payload. - Total Static Verification: Before executing any logic,
Module::parseverifies the entire module: it checks the4B 56 4D 00magic 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. - 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. - State Trie Commitment: If the constructor completes successfully with
HaltReason::StoppedorHaltReason::Returned, the verified module bytecode is committed directly to thecodecolumn 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 at1,048,576instructions (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 at16,384constants (2^14 entries), strictly aligning with the 14-bit immediate addressing capacity of theLoadKopcode. - Maximum Encoded Module Size (
kMaxModuleBytes): Hard ceiling of8 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 at32 MiB(33,554,432 bytes). Linear memory expansion is governed by the quadratic gas cost formula:Gas = 3n + floor(n^2 / 512)forn32-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 bykMaxCallDepth = 1,024frames. Cross-contract nested call frames are strictly limited tokMaxInteropDepth = 128to prevent host thread stack exhaustion.