Documentation Index
8 min readChapter 15

15. KVM Concurrency Model

In distributed ledger technology, smart contract execution must remain deterministic across every validating node in the network. The Kortana Virtual Machine (KVM) guarantees this determinism through a strictly governed concurrency and state access model. Rather than employing traditional OS-level preemptive multithreading—which introduces non-deterministic race conditions—the KVM structures state access through isolated, transactional execution boundaries managed by a centralized state host (StateHost) and a unified state trie (WorldState).

This chapter explores how the KVM coordinates state access, manages cross-contract execution trees, segregates read-only operations from state-modifying operations using Quorlin's type system, and orders storage slot accesses.


15.1 Overview of KVM Execution and State Isolation

At runtime, every KVM contract executes within an isolated VM instance bound to a specific execution context (BlockContext). Contracts do not directly access host memory or physical disk storage. Instead, all interactions with persistent blockchain storage are mediated through the StateHost.

                  +-----------------------------------+
                  |        KVM Interpreter            |
                  |  (Executes KVM Bytecode Instructions) |
                  +-----------------+-----------------+
                                    |
                          Storage Reads / Writes
                                    |
                                    v
                  +-----------------------------------+
                  |            StateHost              |
                  |  (Mediates Access & Gas Accounting)|
                  +-----------------+-----------------+
                                    |
                       Delegates to World State
                                    |
                                    v
                  +-----------------------------------+
                  |     Unified State Trie            |
                  |        (world_)                   |
                  +-----------------------------------+

When a transaction triggers a contract function, the KVM loads the contract's module—formatted as big-endian instruction sequences alongside a 32-byte constant pool—and begins execution at the specified entry point. Storage mutation is isolated to the contract's unique account address key in the unified state trie.


15.2 The Unified State Trie and StateHost

The underlying world state in Kortana is structured as a single unified trie. The StateHost implementation acts as the explicit state-handling interface during instruction execution, mapping requests from KVM bytecode to state::WorldState.

The StateHost Architecture

Extracting from the host context (kvm/state_host.cpp), StateHost maintains references to the underlying world state, current block environment, recent block hashes, and an optional call dispatcher:

StateHost::StateHost(state::WorldState& world, BlockContext block, std::map<uint64_t, Hash256> recent_block_hashes, ICallDispatcher* dispatcher) : world_(world), block_(std::move(block)), dispatcher_(dispatcher), recent_block_hashes_(std::move(recent_block_hashes)) {}

Every read (get_storage) and write (set_storage) query processed during execution passes through this interface:

Result<uint256_t> StateHost::get_storage(const Address& address, const uint256_t& key) const { return world_.get_storage(address, key); }

By funneling all storage operations through world_, KVM ensures that contract executions operate against a single, canonical view of state, eliminating out-of-order writes between concurrent state transitions.


15.3 Slot Key Identification and SlotLess Ordering

To keep track of storage slots touched during state transitions (for block diff computations, state access lists, and concurrent transaction conflict checking), the KVM identifies state locations using a tuple consisting of an account Address and a 256-bit storage slot key (uint256_t).

To reliably compare and order storage operations across execution threads, KVM uses the SlotLess comparator in StateHost:

bool StateHost::SlotLess::operator()(const std::pair<Address, uint256_t>& a, const std::pair<Address, uint256_t>& b) const { if (a.first != b.first) { return std::lexicographical_compare(a.first.begin(), a.first.end(), b.first.begin(), b.first.end()); } return a.second < b.second; }

Ordering Mechanics:

  1. Address Comparison: Compares the 20-byte Address targets using standard byte-by-byte lexicographical ordering (std::lexicographical_compare).
  2. Slot Key Comparison: If both operations target the same contract address (a.first == b.first), the comparator evaluates the 256-bit storage keys (a.second < b.second).

This total ordering ensures that any parallel validator evaluating conflict sets or state access dependencies resolves target slots deterministically.


15.4 Quorlin Read/Write Isolation (reads vs writes)

At the language level, Quorlin enforces strict state access guarantees. Smart contract functions must explicitly declare whether they read state without modifying it (reads) or commit state mutations (writes).

Function Mutability Declarations

contract Vault { map<address, number> balances; // Guaranteed to be side-effect free reads number getBalance(address account) { return balances[account]; } // Declares explicit intent to mutate persistent state writes truth deposit(number amount) { require amount > 0, "Zero deposit"; balances[caller] = balances[caller] + amount; return yes; } }

During semantic analysis (quorlin/sema.cpp), the Quorlin compiler verifies mutability invariants:

  • A function marked as reads is mapped to Mutability::View. It cannot execute instructions that emit events or perform set_storage operations.
  • A function marked as writes is assigned Mutability::Mut. It is granted authorization to invoke state modifications.

This static differentiation enables KVM node runners to optimize concurrent processing: static read requests (reads) can be executed across arbitrary reader threads without locking account state slots, whereas transaction writes (writes) are routed through state pipeline checks.


15.5 Inter-Contract Call Dispatching via ICallDispatcher

Smart contracts frequently need to call other contracts. When a contract call occurs, the calling thread transfers control flow to a child sub-context.

Inter-contract execution in the KVM is decoupled through the ICallDispatcher abstract interface:

CallResult StateHost::call(const CallRequest& request) { if (dispatcher_ == nullptr) return CallResult{}; return dispatcher_->dispatch(request); }

Mechanics of Cross-Contract Invocation

When contract A invokes a function on contract B:

  1. The KVM packs arguments into a CallRequest payload containing target address, gas stipend, input selector, and parameters.
  2. The interpreter pauses execution of contract A and calls dispatcher_->dispatch(request).
  3. The host dispatcher instantiates a nested KVM interpreter context for contract B.
  4. Contract B executes against the shared WorldState.
  5. Upon termination (Stop, Return, or Revert), contract B returns a CallResult.
  6. Control returns to contract A, which inspects execution success or handles failure.
interface IERC20 { reads number balanceOf(address owner); writes truth transfer(address recipient, number amount); } contract TokenRouter { writes truth forwardTokens(address tokenAddress, address recipient, number amount) { IERC20 token = IERC20(tokenAddress); // Dispatches external call through ICallDispatcher truth success = token.transfer(recipient, amount); require success, "Transfer failed"; return yes; } }

15.6 Storage Writes and Gas Accounting Dynamics

State persistence incurs significant computational and disk storage costs on node hosts. To prevent state bloat and denial-of-service vectors, the KVM ties storage modifications directly to gas pricing through set_storage.

When writing to state via set_storage, StateHost inspects the slot's current contents prior to updating:

Result<uint256_t> StateHost::set_storage(const Address& address, const uint256_t& key, const uint256_t& value) { // The previous value is returned so the interpreter can price the write without a second read: // filling an empty slot costs several times an overwrite, and it needs to know which this is. KORTANA_TRY_ASSIGN(const uint256_t previous, world_.get_storage(address, key)); KORTANA_TRY(world_.put_storage(address, key, value)); return previous; }

Gas Pricing Model for Storage Operations

  • Reading (get_storage): Consumes gas corresponding to storage read operations (kGasVeryLow or cold-access penalties).
  • Overwriting (previous != 0 and value != 0): Consumes a standard modification gas fee.
  • Slot Initialization (previous == 0 and value != 0): Consumes a higher gas fee to account for expanding the unified trie.
  • Slot Clearing (previous != 0 and value == 0): May trigger gas refunds or reduced execution cost according to the protocol gas schedule (kvm/gas.cpp).

Because set_storage returns previous directly from the same atomic state read, the KVM calculates accurate gas consumption without executing duplicate host lookups.


15.7 Reentrancy, Call Trees, and Transactional Atomicity

Because cross-contract calls suspend execution while calling into secondary contracts, developer code can be vulnerable to reentrancy if balance updates occur after external invocations.

The Reentrancy Hazard

// VULNERABLE CONTRACT EXAMPLE contract VulnerableBank { map<address, number> userBalances; writes truth withdraw(number amount) { number balance = userBalances[caller]; require balance >= amount, "Insufficient funds"; // External call executed BEFORE updating state balance! // Unsafe inter-contract dispatching allows caller to execute reentrant calls. userBalances[caller] = balance - amount; return yes; } }

Secure Checks-Effects-Interactions Pattern in Quorlin

To ensure atomic state consistency across execution call stacks, contracts must perform state modifications before calling untrusted addresses:

contract SecureBank { map<address, number> userBalances; event Withdrawal(address indexed account, number amount); writes truth withdraw(number amount) { number balance = userBalances[caller]; require balance >= amount, "Insufficient funds"; // 1. CHECKS & EFFECTS (State updated first) userBalances[caller] = balance - amount; // 2. EMIT EVENT emit Withdrawal(caller, amount); // 3. INTERACTIONS (External calls executed last) // External call safe against balance drain via reentrancy return yes; } }

Execution Reversion and Rollback

If an error occurs during execution, or an explicit Revert opcode is executed:

  1. The current KVM frame halts immediately.
  2. Uncommitted state mutations attempted by the frame within world_ are aborted.
  3. Remaining frame gas is calculated according to halt reason (e.g., Opcode::Revert returns remaining gas; Opcode::Invalid consumes all remaining gas).
  4. The parent caller receives a failed CallResult and can cleanly handle or propagate the failure.
contract OrderProcessor { map<address, number> orderCounts; writes truth processOrder(address client, number count) { require count > 0, "Invalid count"; orderCounts[client] = orderCounts[client] + count; if (count > 100) { // Force execution revert: undoes state modifications made in this tx frame require no, "Order exceeds safety limit"; } return yes; } }

Summary

The KVM concurrency and state management model couples strong type-level mutability guarantees (reads vs writes) with explicit runtime abstraction (StateHost). By enforcing state access through a unified state trie (world_), deterministic slot key ordering via SlotLess, dynamic write gas accounting, and atomic execution call trees, the KVM ensures parallelizable queries alongside reliable transaction execution across the network.