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:
- Address Comparison: Compares the 20-byte
Addresstargets using standard byte-by-byte lexicographical ordering (std::lexicographical_compare). - 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
readsis mapped toMutability::View. It cannot execute instructions that emit events or performset_storageoperations. - A function marked as
writesis assignedMutability::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:
- The KVM packs arguments into a
CallRequestpayload containing target address, gas stipend, input selector, and parameters. - The interpreter pauses execution of contract
Aand callsdispatcher_->dispatch(request). - The host dispatcher instantiates a nested KVM interpreter context for contract
B. - Contract
Bexecutes against the sharedWorldState. - Upon termination (
Stop,Return, orRevert), contractBreturns aCallResult. - 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 (kGasVeryLowor cold-access penalties). - Overwriting (
previous != 0andvalue != 0): Consumes a standard modification gas fee. - Slot Initialization (
previous == 0andvalue != 0): Consumes a higher gas fee to account for expanding the unified trie. - Slot Clearing (
previous != 0andvalue == 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:
- The current KVM frame halts immediately.
- Uncommitted state mutations attempted by the frame within
world_are aborted. - Remaining frame gas is calculated according to halt reason (e.g.,
Opcode::Revertreturns remaining gas;Opcode::Invalidconsumes all remaining gas). - The parent caller receives a failed
CallResultand 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.