<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:content="http://purl.org/rss/1.0/modules/content/">
<channel><title>BlockVM Field Notes</title><link>https://blockvm.com/blog/</link><description>Technical guides to blockchain virtual machines, EVM, WASM, zkVMs, and rollup execution.</description><language>en-us</language><lastBuildDate>Sun, 18 Oct 2026 12:00:00 +0000</lastBuildDate><generator>BlockVM.com Static Publisher</generator><item><title>Parallel Blockchain Execution: Scheduling Transactions Without Breaking Determinism</title><link>https://blockvm.com/blog/parallel-blockchain-execution/</link><guid isPermaLink="true">https://blockvm.com/blog/parallel-blockchain-execution/</guid><pubDate>Fri, 02 Oct 2026 12:00:00 +0000</pubDate><category>Scaling &amp; Performance</category><description>Learn how declared access lists, optimistic concurrency, conflict detection, state partitioning, and deterministic commit order can increase blockchain execution throughput.</description><content:encoded><![CDATA[
<p>Blockchains traditionally execute transactions in a canonical sequence so every validator observes the same intermediate state. That model is simple but can leave modern multicore hardware underused. Parallel execution tries to run independent work at the same time while preserving exactly the result that the protocol defines.</p>
<p>The hard part is not starting several threads. It is identifying conflicts, handling dynamic access, choosing a deterministic commit order, bounding retries, and making every client reach the same state even when local scheduling differs. A parallel runtime is therefore a concurrency-control system embedded in consensus.</p>

<h2>Independence is about state, not transaction labels</h2>
<p>Two transactions are independent when their reads and writes cannot affect each other's result under the protocol's ordering rules. Transfers between unrelated accounts may be independent. Calls to different contracts may still conflict if both touch a shared token, registry, oracle, fee account, or global counter.</p>
<p>Static destination addresses are not enough because contracts can call other contracts and compute storage keys dynamically. A scheduler needs either declared access information, runtime discovery, a state model that makes dependencies explicit, or an optimistic mechanism that detects conflicts after speculative work.</p>
<p>Solana's description of <a href="https://solana.com/news/sealevel---parallel-processing-thousands-of-smart-contracts" rel="noopener">Sealevel parallel execution</a> highlights an account-based approach in which transactions declare the state they will read and write. That model allows the runtime to identify many nonoverlapping transactions before execution.</p>

<h2>Declared access sets enable pre-scheduling</h2>
<p>A transaction can list the accounts, objects, or keys it intends to read and write. The scheduler builds a conflict graph: read-read overlap is generally safe, while write-read and write-write overlap require ordering. Independent groups can run concurrently.</p>
<p>This approach makes dependencies visible and can reduce speculative waste. It also changes the application interface. Developers must provide complete access sets, and the runtime must reject or trap attempts to touch undeclared state. Large or overly broad declarations reduce parallelism even when the actual path uses little state.</p>
<p>Access declarations can support fee models because locking a popular writable object consumes a scarce scheduling resource. They can also improve prefetching and database locality. The cost is more transaction metadata and a programming model that exposes concurrency concerns to developers.</p>

<h2>Optimistic concurrency discovers conflicts at runtime</h2>
<p>Another approach executes transactions speculatively against a versioned snapshot while recording their read and write sets. At commit time, the system checks whether earlier committed transactions changed any value the speculative execution read. If so, the transaction is invalidated and retried against the newer state.</p>
<p>Optimism preserves a simpler transaction interface, but workloads with heavy contention can waste substantial work. An attacker may deliberately create conflicts that force repeated execution. The protocol needs limits and a fallback path, often serial execution, so retries cannot become unbounded.</p>
<p>Determinism requires a canonical validation and commit order. Local threads may finish in any order, but the accepted state must match the protocol's transaction order or another explicitly defined order. Timing cannot decide which conflicting result wins.</p>

<h2>State models can make parallelism easier</h2>
<p>An account with a large mutable storage map can hide many dependencies behind one address. Object-oriented models can represent assets and application components as distinct records with ownership and version information. UTXO-like systems consume explicit inputs and create new outputs, making conflicts visible when two transactions try to spend the same record.</p>
<p>No model eliminates shared hotspots. Order books, pools, global configuration, bridges, and popular tokens naturally coordinate many users. Good application architecture can shard or batch some state, but semantics determine whether that is acceptable.</p>
<p>Changing the state model for throughput affects composability and developer ergonomics. Explicit ownership may make parallel scheduling safer while making synchronous shared-state patterns harder. Evaluate application design, not only benchmark transactions per second.</p>

<h2>Nested calls expand the dependency set</h2>
<p>A top-level transaction may invoke contracts whose addresses and storage keys depend on input or state. Declared-access systems can require every nested dependency up front, use capability objects passed through calls, or permit limited dynamic expansion with additional checks. Each choice affects composability.</p>
<p>Optimistic systems record dependencies as they occur, but nested calls make read sets larger and retries more expensive. Reentrancy can create surprising access patterns, especially when callbacks revisit state touched earlier in the transaction.</p>
<p>Cross-program calls should preserve a single transaction-level isolation model. If one subsystem uses locks and another uses snapshots, the boundary needs precise rules for visibility and rollback.</p>

<h2>Deterministic commit is the consensus anchor</h2>
<p>Parallel workers may execute against snapshots and produce tentative writes, logs, gas usage, and return values. The commit phase validates those results in a canonical order. A transaction whose assumptions remain valid is applied; one with a conflict is retried or serialized.</p>
<p>Every observable output must follow the same rule. Logs cannot be emitted permanently before the transaction commits. Gas or fee accounting must include retries in a protocol-defined way, or retries must remain an internal node cost. Random local scheduling cannot change receipt order, state roots, or user balances.</p>
<p>Clients should test that different thread counts and scheduler seeds produce identical blocks. A single-thread reference mode is valuable for differential comparison.</p>

<h2>Database architecture can limit real gains</h2>
<p>VM parallelism does not guarantee end-to-end throughput. Workers may contend on a database lock, state cache, Merkle-tree update, memory allocator, or receipt writer. Hashing and state commitment can become the serial tail after execution completes.</p>
<p>Partitioned caches, multiversion storage, batched tree updates, and asynchronous persistence can help, but they add complexity and recovery requirements. Measure CPU utilization, conflict rate, retry work, cache misses, commit time, and root-construction time separately.</p>
<p>State that fits in memory during a benchmark may behave differently on a long-lived node with cold data. Test realistic history and hardware, not only freshly initialized accounts.</p>

<h2>Hotspots define the workload ceiling</h2>
<p>Some applications inherently serialize around shared state. An automated market maker pool updates common reserves. A nonce scheme may serialize transactions from one account. A global auction, sequencer inbox, or bridge queue can become a lock. Parallel runtimes improve independent workloads but cannot violate application dependencies.</p>
<p>Batching can transform the problem. An application may collect intents, net transfers, or aggregate updates, then apply one state transition. That shifts complexity to the batch builder and fairness rules. It can be effective when the application can tolerate asynchronous or order-abstracted execution.</p>
<p>Benchmarks should report workload composition and contention. A headline throughput number from unrelated account transfers says little about a chain dominated by shared DeFi contracts.</p>

<h2>Security and denial-of-service concerns</h2>
<p>Concurrency introduces new attack surfaces: conflict storms, lock exhaustion, oversized access lists, adversarial retry chains, scheduler starvation, and races in host resources. The protocol should bound metadata, locks, speculative versions, and retries. A worst-case block must remain processable within node limits.</p>
<p>State access tracking must be complete. An unrecorded read can let a speculative transaction commit against stale data. An unrecorded write can corrupt isolation. Precompiles, host functions, system accounts, fee deduction, and logs all need to participate in dependency analysis.</p>
<p>Implementation races that do not affect the abstract protocol can still crash nodes or corrupt caches. Thread sanitizers, deterministic stress tests, crash recovery, and cross-client replay belong in the security program.</p>

<h2>Benchmark the scheduler, not just the VM</h2>
<ul>
<li>Report transaction mix, state size, read/write-set size, and hotspot frequency.</li>
<li>Measure useful committed work separately from speculative work that is discarded.</li>
<li>Compare one thread, several thread counts, and a serial reference implementation.</li>
<li>Vary database warmth, history length, and state-commitment workload.</li>
<li>Include nested calls, dynamic state, failures, and adversarial conflicts.</li>
<li>Record p50 and tail latency, not only aggregate throughput.</li>
<li>Verify identical roots, receipts, fees, and logs across scheduler seeds.</li>
<li>Test overload behavior and the transition to a serial fallback path.</li>
</ul>

<h2>Choosing a parallel execution strategy</h2>
<p>Declared access fits systems willing to expose state dependencies in the transaction model. It offers predictable scheduling and can align with object or account locking. Optimistic concurrency fits systems that want to preserve dynamic access and can tolerate retries under moderate contention. Hybrid designs use declarations when available and speculation or serial fallback for the rest.</p>
<p>The right design depends on application workload, state model, developer interface, database architecture, and adversarial assumptions. Parallelism is most valuable when independent work is common and the scheduler can identify it cheaply.</p>

<h2>Conclusion</h2>
<p>Parallel blockchain execution is deterministic concurrency control. The runtime must discover or declare dependencies, run independent work, validate conflicts, and commit one canonical result. The engineering challenge spans transaction format, VM calls, state storage, fee accounting, receipts, and node recovery.</p>
<p>Place these choices in context with the <a href="/blockchain-virtual-machines/">blockchain VM architecture guide</a>, the <a href="/blog/evm-vs-wasm/">EVM versus WASM comparison</a>, and the <a href="/blog/rollup-execution-environments/">rollup execution pipeline</a>.</p>
]]></content:encoded></item><item><title>How to Review Blockchain VM Security: Boundaries, Traces and Failure Modes</title><link>https://blockvm.com/blog/virtual-machine-security-review/</link><guid isPermaLink="true">https://blockvm.com/blog/virtual-machine-security-review/</guid><pubDate>Thu, 30 Jul 2026 12:00:00 +0000</pubDate><category>EVM Development</category><description>Review blockchain VM security across bytecode validation, host functions, metering, state journaling, calls, upgrades, client diversity, tracing, and operational controls.</description><content:encoded><![CDATA[
<p>A blockchain virtual machine security review asks whether untrusted programs can make honest nodes disagree, consume disproportionate resources, escape the intended capability boundary, corrupt state, or exploit privileged upgrade paths. Contract review is one layer, but runtime security also includes bytecode validation, host implementations, gas schedules, state journaling, client diversity, tracing, and operations.</p>
<p>The most productive method is boundary-first. Identify every transition between untrusted and trusted components, then follow representative inputs through validation, execution, state commitment, and failure. Lists of opcodes and cryptographic primitives are useful, but many severe bugs appear where two correct-looking components disagree about lengths, versions, context, or rollback.</p>

<h2>Define the assets and failure outcomes</h2>
<p>Before reviewing code, state what the VM protects. Assets include consensus agreement, account and contract state, user funds, liveness, node availability, proof soundness, historical replay, and the integrity of logs or receipts consumed by external systems.</p>
<p>Failure outcomes differ. A consensus split occurs when honest nodes compute different results. A denial-of-service bug makes certain valid or invalid inputs too expensive to process. A sandbox escape exposes host capabilities. A state-journaling bug commits writes that should revert. A governance compromise changes runtime rules or implementations through an authorized-looking path.</p>
<p>The <a href="https://docs.soliditylang.org/en/latest/security-considerations.html" rel="noopener">Solidity security considerations</a> catalog application-level patterns such as reentrancy and gas-related pitfalls. A VM review should include those effects while extending the scope down into execution clients and protocol semantics.</p>

<h2>Map trust boundaries and data formats</h2>
<p>Draw the path from transaction bytes to decoded fields, signature checks, account lookup, bytecode loading, VM entry, host calls, database updates, receipts, and state-root computation. For a rollup, add batch decoding, derivation, proof inputs, and settlement. For a zkVM, add guest image identity, witness construction, proof generation, and verification.</p>
<p>At each boundary, record who produces the data, how length and type are validated, whether multiple encodings are accepted, what version governs interpretation, and how errors propagate. Ambiguous or noncanonical formats can create mismatched hashes and divergent validation.</p>
<p>Fuzz decoders independently from execution. Inputs that fail should fail early, deterministically, and with bounded work. A parser should not allocate based on an unchecked length or recurse deeply before verifying structural limits.</p>

<h2>Review bytecode and module validation</h2>
<p>Malformed programs should not reach the main execution loop. Validation may check instruction boundaries, stack effects, jump destinations, function types, imports, memory limits, section order, feature flags, and code size. The exact checks depend on the machine.</p>
<p>Validation itself is an attack surface. Adversaries can submit deeply nested control flow, huge type sections, pathological metadata, or code that triggers worst-case analysis. Limit every count and size, avoid quadratic algorithms, and measure invalid-input cost.</p>
<p>Version handling must be explicit. A module valid under one feature set may be invalid or behave differently under another. Nodes should select rules from protocol context rather than local engine defaults.</p>

<h2>Audit host functions as consensus code</h2>
<p>Host functions bridge guest code to state, cryptography, calls, logs, and environmental data. They receive values or pointers controlled by the guest. Validate pointer arithmetic for overflow, ensure ranges remain inside guest memory, cap copy lengths, and define behavior for overlapping regions.</p>
<p>Every host result must be deterministic. Avoid locale-sensitive parsing, platform-specific integer behavior, unordered iteration, wall clocks, operating-system randomness, filesystem state, and network access. If native libraries are used, compare behavior across supported platforms and versions.</p>
<p>Host errors need clear semantics. Does an invalid signature input return false, trap the guest, or revert the transaction? Are partial writes possible before failure? Do all clients charge the same amount on the same error path? These questions belong in conformance tests.</p>

<h2>Attack the metering model</h2>
<p>Search for operations whose real resource cost exceeds their gas or fuel charge. Include database misses, large memory growth, repeated hashing, signature verification, precompile edge cases, module compilation, event data, call setup, and proof-trace expansion. Benchmark worst cases rather than normal application traffic.</p>
<p>Check that metering occurs before expensive work when possible. A host function should charge or validate a budget before allocating a giant buffer. A module should not compile fully before a size or feature limit is enforced. Failure paths must also be priced; invalid inputs are often easier for attackers to generate than valid ones.</p>
<p>Resource accounting should compose across nested calls. Confirm that forwarded budgets cannot be amplified, refunds cannot exceed intended limits, and exceptions do not restore spent resources incorrectly.</p>

<h2>Verify state journaling and rollback</h2>
<p>Execution often stages writes in a journal so a revert can discard changes. Review nested calls, contract creation, value transfer, self-destruct or deletion semantics, logs, access lists, transient state, refunds, and host caches. Each side effect must either commit or roll back at the correct frame boundary.</p>
<p>A useful test creates several nested calls that write overlapping state, emit logs, transfer value, and then fail at different depths. Compare final state, receipts, gas, and return data across clients. Bugs often appear when one subsystem rolls back while another retains a cache entry or receipt field.</p>
<p>Snapshot and database layers should not leak failed writes into later transactions. Crash recovery is also relevant: a node interrupted between state update and root persistence must restart into a consistent view.</p>

<h2>Examine calls, context, and reentrancy</h2>
<p>Call variants change address, storage, sender, value, and mutability context. Confirm that the runtime initializes each frame correctly and restores parent context on return. Depth limits, return-data buffers, static restrictions, and exception propagation should be covered.</p>
<p>At the application layer, external calls can reenter before state transitions are complete. At the runtime layer, callbacks can exercise unusual combinations of journals, gas, and shared host resources. Test reentrancy across precompiles, token hooks, proxy delegates, and cross-program invocation mechanisms.</p>
<p>Do not assume read-only calls are harmless. A static restriction should prevent state-changing opcodes and stateful host functions, including indirect changes through nested calls.</p>

<h2>Use differential and stateful testing</h2>
<p>Multiple implementations are valuable only when their behavior is compared. Run shared test vectors through each client and compare success status, gas, return data, logs, state writes, receipts, and roots. Generate random but valid programs or transactions, then minimize any divergence.</p>
<p>Stateful fuzzing should produce sequences: deploy, call, revert, upgrade, create, delete, and call again. Many consensus bugs need history. Record seeds, protocol versions, and initial state so failures are reproducible.</p>
<p>A reference interpreter can provide a simple correctness oracle even if it is slow. For proof systems, compare native guest execution, trace generation, proof verification, and any on-chain verifier against the same test cases.</p>

<h2>Review upgrades and privileged controls</h2>
<p>Runtime upgrades can change opcode semantics, costs, host functions, precompiles, or verifier code. Identify who proposes and activates them, which delays and emergency powers exist, how binaries are distributed, and how nodes reject incompatible versions.</p>
<p>For contract-based execution components, inspect proxy admins, storage layout, initializers, and code-hash verification. For native clients, inspect release signing, reproducible builds, dependency updates, and feature flags. A secure runtime can be replaced by insecure code through a weak control plane.</p>
<p>Historical replay needs an explicit version map. Tests should cover blocks before, at, and after activation, including transactions that exercise changed behavior.</p>

<h2>Make observability part of security</h2>
<p>Operators need traces, metrics, and alerts that expose unusual execution without changing semantics. Useful signals include validation failures, opcode or host-call distribution, gas-to-wall-time outliers, memory growth, database latency, proof queue depth, root mismatches, and repeated reverts.</p>
<p>Tracing must be safe under adversarial load. An attacker should not be able to force unbounded debug output on every public node. Sampling, limits, redaction, and on-demand reproduction can preserve visibility without creating a second denial-of-service path.</p>
<p>Incident tooling should export a minimal reproducible package: pre-state reference, transaction or batch bytes, protocol version, client build, trace, and resulting mismatch. Speed of diagnosis matters when a network is halted or clients disagree.</p>

<h2>Security review checklist</h2>
<ul>
<li>Define consensus, asset, liveness, and availability failure outcomes.</li>
<li>Fuzz every decoder with strict size, depth, and canonical-encoding limits.</li>
<li>Validate bytecode or modules before expensive compilation or execution.</li>
<li>Audit host functions for memory safety, determinism, errors, and metering.</li>
<li>Benchmark worst-case gas-to-resource ratios across supported clients.</li>
<li>Test nested rollback, call context, reentrancy, and crash recovery.</li>
<li>Run differential vectors and stateful fuzz sequences across implementations.</li>
<li>Review upgrade authority, release integrity, historical versioning, and rollback.</li>
<li>Design bounded observability and a reproducible incident package.</li>
</ul>

<h2>Conclusion</h2>
<p>Blockchain VM security is the security of a deterministic, metered state-transition system under hostile input and long-lived upgrade pressure. The critical code is not limited to the instruction loop. Parsers, host functions, state journals, database layers, proof adapters, gas schedules, and governance controls can all change validity.</p>
<p>Apply this review alongside the <a href="/blog/gas-bytecode-and-determinism/">resource-accounting guide</a>, the <a href="/blog/local-evm-development-stack/">testing workflow</a>, and the <a href="/blog/smart-contract-upgrade-patterns/">upgrade-pattern checklist</a>.</p>
]]></content:encoded></item><item><title>Smart Contract Upgrade Patterns: Proxies, Storage Layout and Governance</title><link>https://blockvm.com/blog/smart-contract-upgrade-patterns/</link><guid isPermaLink="true">https://blockvm.com/blog/smart-contract-upgrade-patterns/</guid><pubDate>Thu, 16 Apr 2026 12:00:00 +0000</pubDate><category>EVM Development</category><description>Understand proxy upgrades, delegate-call context, implementation slots, initializers, storage compatibility, admin boundaries, timelocks, and rollback planning.</description><content:encoded><![CDATA[
<p>Smart contracts are often described as immutable, yet many production systems need a path to fix defects, change parameters, add features, or respond to external protocol upgrades. EVM proxy patterns create that path by keeping a stable user-facing address and storage context while routing calls to replaceable implementation code.</p>
<p>Upgradeability is not free mutability. It adds a privileged control plane, storage-compatibility rules, initialization hazards, selector boundaries, and governance decisions. A safe design treats the upgrade mechanism as part of the protocol and documents who can change code, how quickly, under which checks, and how users can observe or exit.</p>

<h2>The proxy separates address, storage, and code</h2>
<p>Users call the proxy address. The proxy's fallback logic forwards calldata to an implementation with <code>DELEGATECALL</code>. The implementation's code executes, but storage reads and writes apply to the proxy. The proxy remains the holder of balances, approvals, roles, and persistent variables while the implementation address can change.</p>
<p>The <a href="https://docs.openzeppelin.com/upgrades-plugins/proxies" rel="noopener">OpenZeppelin proxy pattern documentation</a> explains this separation and the unstructured-storage approach used to avoid collisions between proxy control variables and application storage. The underlying rule is simple: implementation code controls the proxy's state.</p>
<p>That rule is also the main risk. A malicious or buggy implementation can overwrite roles, transfer assets, destroy assumptions, or make future upgrades impossible. Protecting the upgrade key is necessary, but verifying implementation behavior is equally important.</p>

<h2>Implementation slots must not collide with application state</h2>
<p>A proxy needs to store the current implementation address and often an administrator or beacon reference. If those values occupy ordinary low-numbered slots, application variables in the implementation can overwrite them during delegated execution. Standardized slots derive locations from unique hashes and place control data away from compiler-assigned application slots.</p>
<p>Standards improve interoperability with explorers and tools, but they do not eliminate all storage risk. The implementation still needs a stable layout across versions. The proxy can safely store its own control fields while a new implementation corrupts old user data through a reordered variable list.</p>
<p>Review the actual deployed slot values after every upgrade. A UI that shows a new implementation address is helpful, but reading the standardized slot and comparing the code hash provides a stronger check.</p>

<h2>Storage layout is an append-only contract</h2>
<p>Compilers assign state variables to slots in declaration order, packing some smaller values into the same word. Mappings and dynamic arrays use hashed locations derived from a base slot. Changing declaration order, type width, inheritance order, or struct layout can reinterpret existing bytes as different variables.</p>
<p>A common safe rule is to preserve every existing variable in place and append new variables at the end. Reserved storage gaps can leave room in base contracts for future additions, though gaps must be maintained correctly. Namespaces are another strategy that groups variables under explicitly derived slots.</p>
<p>Automated layout comparison should be part of the release build. It should compare fully qualified contracts, inherited variables, type definitions, and slot offsets. A tool warning is not proof of safety, but ignoring a warning without a written explanation is a strong signal that the upgrade process is too informal.</p>

<h2>Constructors do not initialize proxy storage</h2>
<p>An implementation constructor runs when the implementation contract is deployed, using the implementation's own storage context. Calls through the proxy never execute that constructor against proxy storage. Upgradeable contracts therefore move initialization into an external or public initializer that runs through the proxy.</p>
<p>The initializer must be callable exactly as intended. If an attacker can initialize an unconfigured proxy first, they may acquire ownership or roles. If a reinitializer can be called twice, state can be reset. Deployment scripts should create the proxy and initialize it atomically when possible, then assert every critical field.</p>
<p>Implementation contracts themselves should usually be locked against initialization so nobody can take control of the standalone implementation and misuse functions that depend on its own state. The threat depends on code behavior, but leaving implementations openly initializable is unnecessary ambiguity.</p>

<h2>Transparent, UUPS, beacon, and minimal proxies</h2>
<h3>Transparent proxy</h3>
<p>A transparent proxy distinguishes admin calls from user calls. The admin can invoke upgrade functions on the proxy, while ordinary callers are delegated to the implementation. This avoids selector clashes at the cost of a separate admin path and contract.</p>
<h3>UUPS-style proxy</h3>
<p>A UUPS proxy keeps upgrade logic in the implementation. The proxy is smaller, but every new implementation must preserve a valid upgrade function and authorization check. An incorrect implementation can remove the upgrade path or make it callable by anyone.</p>
<h3>Beacon proxy</h3>
<p>Multiple proxies read their implementation from a shared beacon. Updating the beacon changes code for the entire fleet. This is efficient for many similar instances but increases blast radius and requires careful compatibility across all proxy states.</p>
<h3>Minimal clone</h3>
<p>A minimal proxy delegates to a fixed implementation and is typically not independently upgradeable. It is useful for cheap instance deployment. Upgrade behavior, if any, lives in an external registry, beacon-like layer, or migration process rather than the clone itself.</p>

<h2>Selector and call-context hazards</h2>
<p>A proxy has its own functions and also forwards arbitrary calldata. Function selectors are only four bytes, so different signatures can collide. Transparent proxy dispatch rules reduce one class of conflict, but interfaces and tooling should still identify which address and ABI are being used.</p>
<p><code>msg.sender</code> and <code>msg.value</code> are preserved under delegate call, while <code>address(this)</code> is the proxy. This is what makes implementation logic behave like the proxy, but it can surprise code that expects to know its own deployed address. Immutable variables are embedded in implementation bytecode and therefore shared by all proxies using that implementation.</p>
<p>Self-calls, reentrancy guards, domain separators, signature verification, and contract-address checks should be tested through the proxy path. A test that calls the implementation directly can pass while the real proxy context fails.</p>

<h2>Governance defines the real upgrade policy</h2>
<p>The technical ability to upgrade is only half the design. The governance path might be a single key, multisignature, timelock, on-chain vote, security council, or layered process. Document the signer threshold, delay, cancellation path, emergency authority, and conditions under which normal process can be bypassed.</p>
<p>Timelocks give users and monitors time to inspect a proposed implementation. They are less useful when the proposal contains an opaque address without verified source, reproducible bytecode, or release notes. Monitoring should decode the implementation change, code hash, initializer call, and any simultaneous role changes.</p>
<p>Emergency upgrades may reduce loss during an active exploit, but broad emergency power is also a trust assumption. Consider scoped pause functions, rate limits, and staged controls that can contain damage without immediately replacing arbitrary code.</p>

<h2>Test the upgrade as a state migration</h2>
<p>An upgrade test should begin from a realistic old deployment with nontrivial users, balances, roles, mappings, arrays, and edge-case values. Apply the exact governance and proxy calls planned for production. Then verify old behavior that must remain, new behavior, storage values, events, permissions, and rollback assumptions.</p>
<p>Fork tests are valuable when the target proxy already exists. Pin a recent block, impersonate the authorized governance path where appropriate, simulate the upgrade, and run application-level scenarios against real state. Do not broadcast from the same script configuration used for simulation without an explicit environment guard.</p>
<p>Save the pre-upgrade and post-upgrade implementation addresses, code hashes, layout reports, call data, and transaction simulation. Those artifacts are part of the change record.</p>

<h2>Rollback is not automatic</h2>
<p>Pointing the proxy back to an old implementation may not restore safety. The new code may have written state the old code cannot interpret, changed external dependencies, or executed irreversible transfers. A “rollback” should be tested as its own state transition from plausible partially upgraded conditions.</p>
<p>Safer releases can separate storage migration from feature activation, add a pause, cap exposure, or use a staged rollout across instances. For high-impact systems, define which state changes make rollback impossible and what replacement plan exists after that point.</p>

<h2>Upgrade review checklist</h2>
<ul>
<li>Identify the proxy pattern and the exact storage slots holding implementation and admin data.</li>
<li>Compare old and new storage layouts, inheritance order, structs, enums, and variable widths.</li>
<li>Verify initializer and reinitializer guards, arguments, and atomic deployment behavior.</li>
<li>Test every privileged function through the proxy, not only on the implementation.</li>
<li>Confirm the new implementation preserves upgrade authorization and future upgrade capability.</li>
<li>Simulate with realistic state and a pinned fork when an existing deployment is involved.</li>
<li>Document governance threshold, timelock, emergency powers, monitoring, and cancellation.</li>
<li>Define rollback limits and post-upgrade assertions before execution.</li>
</ul>

<h2>Conclusion</h2>
<p>Proxy upgrades work by separating stable address and storage from replaceable code. That architecture can support responsible maintenance, but it turns storage layout, initialization, delegate-call context, and governance into permanent security concerns. The upgrade mechanism should be easier to inspect than the application it controls.</p>
<p>Use the <a href="/blog/local-evm-development-stack/">local EVM stack guide</a> to simulate upgrades and the <a href="/blog/virtual-machine-security-review/">security review guide</a> to assess the wider trust boundary.</p>
]]></content:encoded></item><item><title>Building a Local EVM Development Stack: Compile, Test, Trace and Fork</title><link>https://blockvm.com/blog/local-evm-development-stack/</link><guid isPermaLink="true">https://blockvm.com/blog/local-evm-development-stack/</guid><pubDate>Thu, 22 Jan 2026 12:00:00 +0000</pubDate><category>EVM Development</category><description>Create a reproducible EVM workflow with pinned compilers, local nodes, deterministic accounts, unit and invariant tests, forked state, traces, and deployment checks.</description><content:encoded><![CDATA[
<p>A useful local EVM stack does more than make a contract compile. It gives developers a short, repeatable path from source code to bytecode, test transaction, execution trace, storage change, and deployable artifact. The environment should be deterministic enough for continuous integration and realistic enough to reproduce interactions with existing contracts.</p>
<p>The specific framework matters less than the workflow. Pin versions, make state disposable, separate unit tests from network integrations, expose traces on failure, and verify deployment inputs before broadcasting. A fast but opaque toolchain creates false confidence; a good one makes every important assumption inspectable.</p>

<h2>Pin the build before writing the first test</h2>
<p>Record the compiler version, optimizer settings, target VM version, dependency revisions, remappings, and build command. Small changes can alter bytecode, source maps, storage layout, and gas use. Lock files should be committed, and continuous integration should build from a clean checkout rather than relying on a developer's global cache.</p>
<p>Generated artifacts commonly include creation bytecode, runtime bytecode, ABI, metadata, source maps, and storage layout. Store them in a predictable directory and avoid hand-editing. A deployment script should consume the same artifacts the test suite used.</p>
<p>The <a href="https://getfoundry.sh/getting-started/first-steps/" rel="noopener">Foundry first-steps documentation</a> is one example of a modern command-line workflow that combines compilation, testing, scripting, and a local node. Comparable principles apply to other EVM frameworks.</p>

<h2>Use a disposable local node</h2>
<p>A local node should start quickly with deterministic funded accounts, controllable block production, predictable chain identifiers, and no dependence on a remote service. Tests can deploy fresh contracts, advance time, mine blocks, set balances, impersonate accounts, and reset state between cases.</p>
<p>Snapshots are especially useful. A suite can create an expensive baseline deployment once, take a snapshot, and revert to it before each scenario. This preserves speed while preventing order-dependent tests. The snapshot identifier belongs to the current node process and should not be treated as a portable artifact.</p>
<p>Configure the node explicitly. Chain ID, hardfork rules, base fee behavior, block gas limit, and automining can affect results. Defaults that silently change with a tool upgrade can make the same test behave differently months later.</p>

<h2>Separate test layers by purpose</h2>
<h3>Unit tests</h3>
<p>Unit tests isolate one contract or library and exercise expected success and failure paths. They should be small, deterministic, and fast enough to run after every edit. Test authorization, boundary values, zero values, repeated calls, and deliberate reverts—not only the main happy path.</p>
<h3>Integration tests</h3>
<p>Integration tests deploy multiple contracts and verify call sequences, asset movement, role handoff, proxy wiring, callbacks, and event data. They are where assumptions about external interfaces become visible. Use explicit mock behavior; an overly permissive mock can accept calls the real dependency would reject.</p>
<h3>Invariant and fuzz tests</h3>
<p>Fuzzing explores input space. Invariant testing explores sequences of actions and asserts properties that should always hold, such as conservation, authorization, solvency, or monotonic counters. A useful invariant harness constrains impossible actions while still allowing adversarial ordering and repeated calls.</p>

<h2>Make failures produce readable traces</h2>
<p>A failing test should print the call tree, decoded function selectors, sender, target, value, gas, and revert data. When source maps are available, the trace should connect the deepest failing frame to a source location. Labels for known addresses turn a wall of hex into an understandable interaction.</p>
<p>Start debugging at the first failing child frame, not the top-level revert. Inspect its input and state assumptions, then walk back to see how callers propagated or ignored the failure. If the failure is out of gas, inspect memory expansion, dynamic copies, storage transitions, and nested-call budgets.</p>
<p>Keep a raw trace option. Decoders can be wrong when an ABI is stale, a proxy changes implementation, or a custom error is unknown. The underlying bytes and opcode sequence remain the fallback source of truth.</p>

<h2>Use forked state deliberately</h2>
<p>Fork mode reads state from a remote network at a chosen block and applies local changes on top. It is valuable for reproducing integrations with live tokens, pools, registries, bridges, and proxies without deploying every dependency manually.</p>
<p>Always pin the block number. A test against “latest” can change when balances, implementations, prices, or governance settings move. Record the RPC endpoint class and chain ID, but do not commit private credentials. Cache behavior should not conceal a missing or rate-limited upstream dependency in continuous integration.</p>
<p>Fork tests are not a substitute for unit tests. They are slower and can inherit complex state. Use them to validate real interfaces and historical incidents, while keeping core logic covered by local deterministic tests.</p>

<h2>Inspect storage and proxy context</h2>
<p>When debugging state, distinguish contract address, implementation address, and storage context. A delegate call executes implementation code against proxy storage. Reading the implementation's own storage may show empty or irrelevant values even though user interactions succeed through the proxy.</p>
<p>Storage-layout artifacts help map slots back to variables. For mappings and dynamic arrays, tools can compute derived locations from keys and base slots. Compare layout between upgrade versions and fail the build when an unsafe reorder or type change appears.</p>
<p>Directly editing storage in a local node is useful for constructing edge cases, but tests that rely on it should explain the intended real-world path. Otherwise the suite may validate a state that the protocol can never reach.</p>

<h2>Track gas without optimizing the wrong thing</h2>
<p>Gas snapshots catch accidental regressions and reveal which operations dominate. Keep them near representative tests and review changes rather than automatically rejecting every increase. Security checks, clearer events, or safer storage can justify cost.</p>
<p>Measure both cold and warm access paths, first-time initialization, repeated use, worst-case array sizes, and revert paths. A benchmark against a fresh deployment can miss the cost pattern users see after state has grown.</p>
<p>On rollups, L2 gas is only part of the fee. Calldata or blob publication, compression, and cross-domain messages can dominate. A local EVM node may not reproduce those charges, so add network-specific estimation or integration checks.</p>

<h2>Make deployment a tested program</h2>
<p>Deployment scripts should be version-controlled, parameterized, and executable against a local node and test network before production. They should verify chain ID, deployer address, constructor or initializer arguments, linked libraries, expected bytecode, and resulting addresses.</p>
<p>For role-based systems, test the complete handoff. The temporary deployer may need to grant roles, transfer ownership, configure a timelock, and renounce privileges. A partial handoff can leave production controlled by the wrong key even when every contract deployed successfully.</p>
<p>After deployment, perform read-only assertions: implementation slot, owner, role members, configuration values, paused state, domain separators, and code hashes. Then run a minimal low-risk transaction path and archive the exact artifacts and command output.</p>

<h2>A compact local workflow</h2>
<pre><code># build from pinned inputs
vmtool build --locked

# run fast deterministic tests with traces on failure
vmtool test --unit --trace-failures

# run invariant and fuzz suites with recorded seeds
vmtool test --invariant --fuzz-runs 10000 --seed 424242

# reproduce an integration at a pinned block
vmtool test --fork-url $RPC_URL --fork-block 19876543

# simulate the deployment script locally
vmtool script deploy --rpc http://127.0.0.1:8545 --simulate
</code></pre>
<p>The command names are illustrative. The important properties are pinned inputs, visible traces, recorded random seeds, a fixed fork block, and deployment simulation.</p>

<h2>Continuous integration and artifact discipline</h2>
<ul>
<li>Build from a clean environment with locked dependencies.</li>
<li>Run formatting, static analysis, unit, fuzz, invariant, and integration stages separately.</li>
<li>Record compiler warnings and reject newly introduced high-severity findings.</li>
<li>Save failing fuzz seeds and traces as build artifacts.</li>
<li>Compare bytecode and storage layout for release candidates.</li>
<li>Run fork tests on a schedule if remote calls are too expensive for every commit.</li>
<li>Require deployment simulation and post-deploy assertions before approval.</li>
<li>Archive the final ABI, bytecode, metadata, source commit, and configuration.</li>
</ul>

<h2>Conclusion</h2>
<p>A local EVM stack is successful when a failure can be reproduced, traced, explained, fixed, and verified without hidden machine state. Pinned builds connect source to bytecode. Disposable nodes create reliable tests. Forks reproduce real integrations. Traces expose VM behavior. Tested deployment scripts carry the same assumptions into production.</p>
<p>Pair this workflow with the <a href="/blog/ethereum-virtual-machine-explained/">EVM execution guide</a>, then use the <a href="/blog/virtual-machine-security-review/">security review checklist</a> and <a href="/blog/smart-contract-upgrade-patterns/">upgrade-pattern guide</a> before release.</p>
]]></content:encoded></item><item><title>Gas, Bytecode and Determinism: How Blockchain VMs Bound Execution</title><link>https://blockvm.com/blog/gas-bytecode-and-determinism/</link><guid isPermaLink="true">https://blockvm.com/blog/gas-bytecode-and-determinism/</guid><pubDate>Fri, 03 Oct 2025 12:00:00 +0000</pubDate><category>EVM Development</category><description>See how bytecode semantics, gas schedules, memory expansion, storage costs, host functions, and protocol versions keep blockchain execution finite and reproducible.</description><content:encoded><![CDATA[
<p>Public blockchain execution has two non-negotiable requirements: every valid program must terminate within a bounded budget, and every honest verifier must compute the same result. Bytecode defines the operations a program can request. A gas or fuel model prices those operations and stops execution when the budget is exhausted. Deterministic semantics ensure that the same pre-state, input, and protocol version produce the same post-state.</p>
<p>These mechanisms are tightly coupled. Changing an opcode cost can change which transactions complete. Adding a host function can expose new state or nondeterminism. Altering compiler output can shift code size and resource use. A runtime's economic and consensus behavior therefore lives in the combination of instruction rules, dynamic charges, state access, and versioned environment.</p>

<h2>Bytecode is the protocol-facing program</h2>
<p>Developers write source code, but validators execute a lower-level representation. In the EVM that representation is a sequence of opcodes and immediate data. In a WebAssembly runtime it is a validated module. In a zkVM it may be a guest image for a RISC-like instruction set. Whatever the form, the protocol needs an unambiguous way to identify, decode, and execute it.</p>
<p>Source-level intent can disappear during compilation. Inlining, dead-code elimination, optimizer passes, ABI dispatch, bounds checks, and library linking all change the resulting instruction stream. Security review should preserve the connection between source, compiler settings, artifacts, and deployed code. Reproducible builds make that connection testable.</p>
<p>Bytecode validation is another defense layer. A runtime may reject malformed instruction sequences, unsupported features, excessive module structure, invalid jump targets, or imports outside an allowlist. Early rejection prevents ambiguous execution and limits expensive preparation work.</p>

<h2>Gas bounds the path, not just the program</h2>
<p>A gas limit does not estimate how long a source file “usually” runs. It bounds one concrete path through bytecode under one state and input. A branch that touches a new storage slot, allocates a larger memory region, performs a nested call, or verifies a signature can consume far more than a nearby path.</p>
<p>The <a href="https://ethereum.org/developers/docs/gas/" rel="noopener">Ethereum gas documentation</a> describes gas as the unit that measures computational effort and explains how transaction fees combine gas used with fee parameters. At the VM level, the critical property is that execution cannot continue after its allocated gas is depleted.</p>
<p>Nested calls complicate budgeting. A caller chooses how much gas to make available to a child, and protocol rules can cap forwarded amounts or reserve a stipend. The child may return unused gas, consume its allocation, or revert. Call structure therefore affects both correctness and denial-of-service resistance.</p>

<h2>Static costs are only the beginning</h2>
<p>Some opcodes have a fixed charge, but expensive operations often use dynamic formulas. Copying depends on byte length. Memory charge depends on the largest region reached. Storage cost depends on old and new values and whether the slot has already been accessed. Account access may be cheaper after the first touch in a transaction.</p>
<p>Dynamic pricing aims to approximate scarce node resources: CPU, memory bandwidth, database reads, database writes, state growth, and network data. No schedule is perfect. Implementations change, hardware evolves, and attackers search for operations whose real cost exceeds their gas price. Protocol upgrades sometimes reprice instructions after such imbalances are discovered.</p>
<p>A secure schedule should be monotonic enough to reason about and conservative around worst cases. If a database lookup is fast in a warm benchmark but slow for adversarial keys or cold storage, average timing is not a safe price. Measurements should include multiple clients, hardware classes, and pathological inputs.</p>

<h2>Memory expansion prevents unbounded address tricks</h2>
<p>Linear memory often starts empty and grows as code touches higher offsets. Charging only for individual load and store instructions would let a program reference an enormous address and force the runtime to allocate or manage a huge sparse region cheaply. Expansion formulas charge based on the highest word reached, usually with a component that grows faster than linearly.</p>
<p>This creates a recognizable failure mode: a corrupted pointer or decoded length jumps far beyond the intended buffer, and the next copy or store consumes nearly all remaining gas. Trace tools should show memory size and expansion cost near failures. Source-level debuggers that display only the final out-of-gas message can hide the actual pointer error.</p>
<p>WebAssembly embeddings have a related responsibility even when the core engine manages pages. The blockchain must limit maximum pages, meter growth, price host copies, and validate guest-provided pointer ranges before crossing the host boundary.</p>

<h2>Storage pricing protects shared state</h2>
<p>Persistent state is different from temporary computation. Every full node may need to store, index, snapshot, or prove it. A write can impose costs long after the originating transaction finishes. Storage pricing therefore accounts for access and for transitions such as zero to nonzero, nonzero to zero, and repeated modification.</p>
<p>Refund mechanisms can reward clearing state, but large refunds can also distort block capacity or create gas-token behavior. Protocol designers balance incentives against predictable execution limits. Developers should not assume a refund will make a complex path cheap or remain unchanged across network upgrades.</p>
<p>State models outside the EVM face the same issue. Object creation, key-value writes, code publication, and account growth need explicit bounds. Rent, deposits, storage staking, or size-based fees are alternative ways to make long-lived state visible to users.</p>

<h2>Host functions need independent metering</h2>
<p>A guest instruction can be cheap while the host operation it invokes is expensive. Cryptographic verification, hashing a large buffer, reading state, emitting logs, or calling another contract may execute substantial native code. A runtime that meters only guest instructions leaves an attack path through host imports.</p>
<p>Host charges should include validation and copying. For example, verifying a signature requires reading bytes from guest memory, checking lengths, performing curve arithmetic, and writing a result. Every step needs bounds. Error paths also need predictable cost so malformed inputs do not trigger expensive work for a small fee.</p>
<p>Precompiles and accelerators concentrate this concern. They can make important cryptography practical, but their implementation and price become consensus-sensitive. Differential tests should compare outputs and edge-case failures across clients.</p>

<h2>Determinism limits environmental access</h2>
<p>A node cannot let guest code read the local clock, operating-system randomness, network responses, locale, filesystem, or thread schedule. Those values differ between machines. Protocol facts such as block timestamp or randomness are supplied through defined context so every verifier observes the same input.</p>
<p>Integer arithmetic is generally easier to specify than floating point because floating-point modes, NaN payloads, fused operations, and implementation details can create variation. Runtimes that support floating point need canonical rules and extensive conformance testing. Many blockchain VMs avoid it or restrict it.</p>
<p>Serialization must also be deterministic. Field order, integer width, endianness, default values, and rejection of malformed encodings influence hashes and state. A flexible application format can become a consensus hazard if multiple byte sequences are treated inconsistently.</p>

<h2>Protocol versions freeze meaning at a block boundary</h2>
<p>Execution rules evolve. New opcodes appear, gas costs change, bugs are fixed, and host functions are added. A network upgrade defines the exact block or epoch at which new semantics become active. Nodes replaying history must select the old rules before that boundary and the new rules after it.</p>
<p>This makes “latest runtime” an unsafe replay strategy. A client should carry versioned semantics or a reliable mapping from historical height to rule set. Compilers and developer tools also need target settings so generated code does not assume instructions unavailable on the deployment network.</p>
<p>Upgrade tests should include transactions near the activation boundary, contracts using changed opcodes, and state created under old rules but read under new ones. The goal is not only that upgraded nodes agree with one another, but that they agree about the entire prior history.</p>

<h2>Out-of-gas and revert are different failures</h2>
<p>When execution exhausts gas, the current frame halts exceptionally and usually returns no structured explanation. State changes in that frame are rolled back, and the allocated gas is consumed. A deliberate revert also rolls back changes but can return error bytes and preserve unused gas for the caller.</p>
<p>Applications should distinguish these cases in monitoring. A revert may indicate an expected business-rule rejection, while out of gas often indicates a bad estimate, unexpected path, malicious input, or changed dependency. Retrying an out-of-gas transaction with a larger limit can succeed, but doing so without understanding the path can hide an unbounded loop or memory bug.</p>

<h2>A practical resource-review checklist</h2>
<ul>
<li>Pin the compiler, optimizer, runtime version, and target protocol rules.</li>
<li>Measure representative state transitions, not only empty-state unit tests.</li>
<li>Inspect memory growth, copy lengths, storage access, and nested calls in traces.</li>
<li>Include worst-case and malformed inputs for host functions and precompiles.</li>
<li>Track gas or cycle snapshots in continuous integration to catch regressions.</li>
<li>Test failure behavior with low gas, child-call failure, and revert data.</li>
<li>Review upgrade boundaries when opcode costs or semantics change.</li>
<li>Separate execution cost from data-publication, proof, and long-term state costs.</li>
</ul>

<h2>Conclusion</h2>
<p>Bytecode gives blockchain programs a protocol-defined form. Gas or fuel prevents one execution from consuming unbounded resources. Deterministic semantics make independent verification possible. These are not separate implementation details; together they define whether a transaction is valid, affordable, and reproducible.</p>
<p>See the <a href="/evm/">EVM guide</a> for machine structure, the <a href="/blog/ethereum-virtual-machine-explained/">EVM execution walkthrough</a> for call-level detail, and the <a href="/blog/virtual-machine-security-review/">VM security review guide</a> for a broader audit framework.</p>
]]></content:encoded></item><item><title>Rollup Execution Environments: Sequencers, State Roots and Settlement</title><link>https://blockvm.com/blog/rollup-execution-environments/</link><guid isPermaLink="true">https://blockvm.com/blog/rollup-execution-environments/</guid><pubDate>Fri, 11 Jul 2025 12:00:00 +0000</pubDate><category>Scaling &amp; Performance</category><description>Map a rollup transaction from sequencing through data publication, deterministic derivation, VM execution, state commitments, fault or validity proofs, and settlement.</description><content:encoded><![CDATA[
<p>A rollup execution environment is not only a virtual machine. It is a pipeline that accepts user transactions, orders them, publishes enough data for independent reconstruction, derives canonical execution inputs, computes state transitions, commits results, and convinces a settlement system that those results should be accepted. The VM is central, but its security and availability depend on every stage around it.</p>
<p>This broader view prevents common category errors. A fast sequencer response is not the same as settled finality. An EVM-compatible execution engine does not by itself guarantee Ethereum-equivalent security. A validity proof does not make missing transaction data available. Architecture review should follow one transaction end to end and record which actor or cryptographic mechanism is trusted at each boundary.</p>

<h2>The transaction enters through an ordering service</h2>
<p>Users usually submit transactions to an RPC endpoint connected to a sequencer. The sequencer checks basic validity, orders transactions, builds L2 blocks, and may return a rapid confirmation. Centralized sequencing can offer low latency and predictable block production, but it creates an operational and censorship dependency.</p>
<p>Important questions are what happens when the sequencer is offline, whether users can force transactions through the settlement chain, how ordering rules can change, and which party can replace the sequencer. A fallback path that exists only on paper but requires unavailable software or privileged cooperation is not a reliable escape mechanism.</p>
<p>The <a href="https://docs.optimism.io/op-stack/protocol/components" rel="noopener">OP Stack component documentation</a> usefully separates sequencing, data availability, derivation, execution, settlement, and governance. Other rollup stacks use different modules, but the layered taxonomy makes trust assumptions easier to inspect.</p>

<h2>Data publication makes reconstruction possible</h2>
<p>A rollup posts transaction data, compressed batches, state differences, or commitments to a data-availability system. Independent nodes need enough information to derive the same L2 chain and, in an optimistic system, to detect and challenge invalid execution. If critical data is withheld, a state root alone does not let ordinary participants reconstruct account history or produce a proof.</p>
<p>Using a base layer for data availability inherits that layer's publication and finality assumptions, while alternative systems can lower cost at the price of additional committees, sampling schemes, or recovery rules. Review where the bytes live, how long they remain retrievable, what a new node needs to sync, and what happens if the preferred data source is unavailable.</p>
<p>Compression is part of the protocol because every deriving node must decode batches identically. Limits, malformed input behavior, and version changes need deterministic rules. The sequencer's internal block database should not be the only place where canonical input can be found.</p>

<h2>Derivation turns published bytes into execution payloads</h2>
<p>Derivation reads the agreed data source and constructs the L2 inputs that the execution engine consumes. It may combine sequencer batches, deposits from the settlement chain, protocol-generated transactions, timestamps, and upgrade signals. The process must be reproducible so an independent node reaches the same L2 blocks without trusting the sequencer's private state.</p>
<p>Derivation rules are often overlooked because they sit between data and the familiar VM. Yet a parsing ambiguity, ordering difference, or timestamp rule can change execution. The derivation implementation, specification, test vectors, and upgrade schedule are consensus-critical for the rollup.</p>
<p>When a rollup uses an Ethereum-style engine API, the derivation node can send payloads to a modified execution client. That modularity is useful, but compatibility depends on the exact extensions, system transactions, fee fields, and protocol version.</p>

<h2>The execution engine computes L2 state</h2>
<p>The execution engine applies transactions to the L2 pre-state. It may use the EVM, a WASM runtime, a custom VM, or a zero-knowledge-friendly machine. Rollups often modify fee accounting because users pay for both L2 computation and data publication. They may add predeploys, special system calls, deposit transaction types, or altered block-context fields.</p>
<p>Developers should distinguish source-code compatibility from semantic equivalence. A Solidity contract can compile and deploy while observing different opcode support, precompiles, gas prices, block values, finality, or cross-domain messaging. Integration tests should run against the target rollup rather than assuming a local Ethereum node reproduces every behavior.</p>
<p>The engine produces receipts, logs, transaction outcomes, and a new state root. That root is a commitment to the result, not evidence by itself that the computation was correct. Settlement adds the mechanism that makes the commitment acceptable elsewhere.</p>

<h2>State commitments connect execution to settlement</h2>
<p>A proposer periodically submits an output root or equivalent commitment to a contract on the settlement chain. The commitment may include or derive from the L2 state root, message-passing state, and block identity. Withdrawals and cross-domain messages can depend on it.</p>
<p>The protocol must define who may propose, how proposals are ordered, how conflicting commitments are handled, and what delay exists before they are usable. Governance or privileged roles may be able to pause, replace, or override components. Those powers can be practical safety mechanisms, but they should be included explicitly in the trust model.</p>
<p>State commitments also create versioning requirements. If the execution engine, derivation rules, or commitment formula changes, provers and verifiers must agree on the activation boundary. A root without its protocol context can be ambiguous.</p>

<h2>Optimistic settlement relies on challenge capability</h2>
<p>In an optimistic design, a proposed result is accepted after a challenge period unless someone proves it invalid. Security requires at least one honest participant with access to the data, software, compute, and economic ability to monitor and challenge. “Anyone can challenge” is meaningful only if the process is operationally feasible.</p>
<p>Fault-proof systems narrow a disagreement until a settlement contract or on-chain VM can resolve a specific execution step. The off-chain VM and the dispute VM must represent the same state transition. Preimage access, instruction semantics, memory commitments, and step proofs become part of the security boundary.</p>
<p>Challenge periods affect withdrawal latency. Liquidity providers can advance funds sooner, but users then rely on those intermediaries and their pricing. Fast user experience and protocol-final settlement are distinct properties.</p>

<h2>Validity settlement relies on proof generation</h2>
<p>A validity rollup attaches a proof that the batch transition satisfied the encoded rules. The settlement contract verifies the proof and accepts the state commitment without waiting for a challenge window. This can shorten cryptographic finality, but proving adds latency, hardware, circuit or zkVM engineering, and verifier dependencies.</p>
<p>Proof generation may be centralized even when verification is permissionless. Review whether another prover can reproduce the proof, how failed batches are retried, whether proving keys or software are public, and what happens during a prover outage. A system that cannot produce proofs may stop finalizing even if transaction execution itself still works.</p>
<p>Validity proofs also need available data for users who want to reconstruct state or exit independently. Correctness and availability answer different questions.</p>

<h2>Bridges and cross-domain messages expose the full stack</h2>
<p>A deposit begins on one domain and becomes an input to the L2 derivation process. A withdrawal begins in L2 state and becomes claimable after the settlement rules accept the relevant commitment. The bridge must bind message identity, sender, target, value, replay protection, and finality status across both environments.</p>
<p>Application developers should account for asynchronous completion, reorganization windows, proof delays, and message failure on the destination. A successful source transaction does not guarantee that the destination call succeeded. Monitoring should track each lifecycle stage rather than displaying a single binary status.</p>
<p>Upgradeable bridges deserve special scrutiny because privileged changes can alter verification or asset control. Timelocks, multisignatures, security councils, and emergency pauses may reduce some risks while creating governance trust.</p>

<h2>Finality has several clocks</h2>
<ul>
<li><strong>Sequencer confirmation:</strong> the ordering service says the transaction is included.</li>
<li><strong>L2 block confirmation:</strong> an L2 node has executed the transaction in a local canonical view.</li>
<li><strong>Data finality:</strong> the published batch data is finalized in the chosen availability layer.</li>
<li><strong>Commitment publication:</strong> a state output has been posted to settlement.</li>
<li><strong>Proof or challenge completion:</strong> validity has been proven or the dispute window has passed.</li>
<li><strong>Withdrawal completion:</strong> the destination contract accepts and executes the claim.</li>
</ul>
<p>User interfaces and risk systems should name the stage they mean instead of using “final” without qualification.</p>

<h2>A rollup execution review checklist</h2>
<ul>
<li>Can an independent node reconstruct the L2 chain from public or protocol-approved data?</li>
<li>What can users do during a sequencer outage or censorship event?</li>
<li>Which derivation rules turn batches and deposits into execution inputs?</li>
<li>How does the L2 VM differ from the upstream runtime developers expect?</li>
<li>Who posts state commitments, and which privileged roles can change or pause the system?</li>
<li>How does an invalid result get rejected: fault proof, validity proof, or another mechanism?</li>
<li>What are the data, proof, and withdrawal latency assumptions?</li>
<li>Can the bridge lifecycle be monitored and recovered at every stage?</li>
</ul>

<h2>Conclusion</h2>
<p>A rollup VM is one link in a chain of ordered input, available data, deterministic derivation, execution, commitment, proof, and settlement. The security of the user-facing result is bounded by the weakest required stage and by the privileged controls around upgrades and emergencies.</p>
<p>Continue with the <a href="/rollups/">rollup architecture guide</a>, compare proof-based execution in <a href="/blog/zkvm-explained/">zkVMs explained</a>, or examine throughput claims through <a href="/blog/parallel-blockchain-execution/">parallel blockchain execution</a>.</p>
]]></content:encoded></item><item><title>zkVMs Explained: Proving Computation Instead of Re-executing It</title><link>https://blockvm.com/blog/zkvm-explained/</link><guid isPermaLink="true">https://blockvm.com/blog/zkvm-explained/</guid><pubDate>Wed, 19 Mar 2025 12:00:00 +0000</pubDate><category>Zero Knowledge</category><description>Learn how zero-knowledge virtual machines turn guest-program execution into proofs, including traces, public and private I/O, receipts, recursion, and performance trade-offs.</description><content:encoded><![CDATA[
<p>A zero-knowledge virtual machine executes a program and produces cryptographic evidence that the execution followed the machine's rules. Instead of every verifier repeating every instruction, a prover performs the work, constructs a proof or receipt, and publishes selected outputs. A verifier checks that compact artifact against a program identity and public values.</p>
<p>This changes the execution trade-off rather than making computation free. Proving can be much more expensive than ordinary native execution, and the VM must translate instruction steps, memory accesses, and I/O into algebraic constraints. The benefit is that verification can be dramatically cheaper and can cross trust boundaries: a smart contract, client, or remote service can accept a result without trusting the machine that computed it.</p>

<h2>What the “zero knowledge” label does and does not promise</h2>
<p>Zero-knowledge proof systems can hide private witnesses while proving a statement about them. A zkVM may use that property to keep inputs secret, but many applications use a zkVM primarily for succinctness or verifiable computation. The proof confirms correct execution; privacy depends on which inputs, outputs, commitments, and side channels the application exposes.</p>
<p>Ask four concrete questions. Which data enters the guest program privately? Which values are committed to the public journal? Does the proof reveal execution length or other metadata? Who receives the raw input before proving? Calling a system a zkVM does not answer those application-level questions.</p>
<p>The <a href="https://dev.risczero.com/api/zkvm/" rel="noopener">RISC Zero zkVM documentation</a> illustrates a common model in which a host launches a guest program and the resulting receipt authenticates public journal data. Other systems use different instruction sets and proof formats, but the host, guest, proof, and public-output separation is broadly useful.</p>

<h2>Host and guest programs have different responsibilities</h2>
<p>The host is ordinary software that gathers inputs, invokes the prover, stores artifacts, and communicates with external systems. It is not automatically proven correct. The guest is the program whose execution occurs inside the zkVM and is bound to the proof through a code or image identifier.</p>
<p>That boundary should be designed deliberately. If the host downloads a file, parses it, and sends only a claimed hash to the guest, the proof does not authenticate the parsing unless the guest recomputes or verifies the relevant transformation. Conversely, moving every operation into the guest can make proving unnecessarily expensive. Good designs keep trust-sensitive computation inside the proof while leaving orchestration outside.</p>
<p>Program identity is equally important. A verifier must know which guest code the proof represents. Systems may commit to an executable image, method identifier, circuit digest, or verification key. Upgrading the program changes that identity and requires an explicit policy for which versions are accepted.</p>

<h2>Execution traces become proof constraints</h2>
<p>During ordinary execution, a CPU changes registers and memory step by step. A zkVM represents an analogous trace in a form the proof system can check. Constraints enforce that each row follows the instruction semantics, memory is consistent, and the final public outputs match the committed values.</p>
<p>Different zkVMs choose different instruction sets and arithmetizations. Some emulate a RISC-style architecture so compiled languages can run with familiar toolchains. Others use a custom machine designed to reduce proof cost. A general-purpose instruction set improves portability, while a specialized design can make common operations cheaper to prove.</p>
<p>Memory is a major challenge because a proof must establish consistent reads and writes without trusting a conventional RAM device. Systems use sorted memory tables, permutation arguments, Merkle commitments, or other techniques. The chosen model affects trace size, random access cost, and optimization strategies.</p>

<h2>Public input, private input, and the journal</h2>
<p>A proof statement generally includes public values. In a zkVM application, those may include a program identifier, input commitment, output bytes, state root, or receipt digest. Private witness data is available to the prover and guest but omitted from the public statement. The verifier learns only what the statement and surrounding metadata reveal.</p>
<p>Applications should serialize public outputs canonically. If two encodings can represent the same logical value, downstream verification may become ambiguous. Domain-separated hashes help prevent a commitment intended for one purpose from being reused in another. Large outputs are often committed by hash, with the raw data distributed separately.</p>
<p>Input authenticity remains an external question. A proof can show that a program correctly processed a supplied price, document, or sensor reading; it cannot prove the source was truthful unless authenticity evidence is also checked inside the guest.</p>

<h2>Proving cost is not native runtime cost</h2>
<p>A program that runs quickly on a laptop may be expensive to prove. Each instruction contributes to a trace, and some operations create large constraint workloads. Hash functions, big-integer arithmetic, random memory access, dynamic allocation, and complex parsing can dominate. Optimization begins with measuring guest cycles or segments rather than assuming source-level speed maps directly to proof speed.</p>
<p>Proof-friendly cryptographic primitives can reduce cost, but applications often need compatibility with existing hashes and signatures. Precompiles or accelerators may implement those operations with dedicated constraints. This is useful only when the verifier's trust model and implementation make the accelerator's semantics as rigorous as ordinary instructions.</p>
<p>Memory footprint matters for the prover as well. Large traces may be segmented, streamed, or recursively composed. GPU acceleration and distributed proving can reduce latency, but they add operational complexity, scheduling, artifact storage, and failure recovery.</p>

<h2>Receipts, proofs, and verification</h2>
<p>The output artifact may be called a proof, receipt, seal, or certificate. It usually binds a program identity to public outputs and a claim of valid execution. A verifier checks cryptographic equations and rejects an artifact that does not match the expected program or data.</p>
<p>On-chain verification adds another cost model. A proof convenient for desktop verification may be too expensive for a smart contract. Systems can wrap or compress an inner proof into a format optimized for the settlement chain. That introduces additional keys, circuits, and version dependencies that must be monitored.</p>
<p>Verification code is a security boundary. Applications should pin accepted verifier versions, program identifiers, and proof formats. “Proof verified” is incomplete unless the verifier also checks the expected method and decodes public outputs safely.</p>

<h2>Recursion and proof composition</h2>
<p>Recursive proving allows a proof to verify other proofs. One use is aggregation: many independent executions are combined into a single artifact. Another is incremental computation: each step proves that it started from a previously proven state. Recursion can also create a stable outer proof while inner programs or proof systems evolve.</p>
<p>Composition needs a clear statement. The outer proof must bind every inner proof, relevant public output, ordering rule, and aggregation condition. Dropping an index, count, domain tag, or state-chain link can make a mathematically valid proof establish the wrong application claim.</p>
<p>Operationally, recursion creates queues and checkpoints. If one segment fails, the system should identify which guest execution or aggregation stage needs to be retried rather than restarting an entire batch.</p>

<h2>Where zkVMs fit in blockchain architecture</h2>
<ul>
<li><strong>Validity rollups:</strong> prove batches of state transitions for a settlement contract.</li>
<li><strong>Bridges and light clients:</strong> prove consensus or state-verification logic from another system.</li>
<li><strong>Coproduction:</strong> move expensive computation off-chain while keeping a verifiable result on-chain.</li>
<li><strong>Private applications:</strong> prove a policy or calculation without revealing all inputs.</li>
<li><strong>Verifiable data pipelines:</strong> authenticate transformations from a committed input to a public output.</li>
<li><strong>Auditable agents:</strong> prove that a deterministic program followed approved rules over committed data.</li>
</ul>
<p>In each case, the proof does not replace data availability, input authenticity, authorization, or application design. It proves a specific computation statement.</p>

<h2>A practical zkVM evaluation checklist</h2>
<ul>
<li>Which source languages and compiler versions produce supported guest code?</li>
<li>How is the program identity computed, published, and upgraded?</li>
<li>Which data is public, private, committed, or supplied outside the proof?</li>
<li>What are typical cycle counts, memory needs, and proving latency for representative workloads?</li>
<li>Can proofs be generated locally, delegated, distributed, or accelerated?</li>
<li>What setup assumptions, cryptographic curves, hashes, and security parameters are used?</li>
<li>How are receipts verified on the target platform, and what does verification cost?</li>
<li>Which debugging, profiling, recursion, and artifact-management tools are available?</li>
</ul>

<h2>Conclusion</h2>
<p>A zkVM turns deterministic execution into a portable claim that another party can verify. Its architecture spans the guest program, host orchestration, execution trace, proof system, public I/O, program identity, and verifier. The most important design work often happens at the boundaries: deciding what must be proven, what remains trusted, and which outputs are bound to the proof.</p>
<p>See the broader <a href="/zkvm/">zkVM guide</a>, then place proofs in context with the <a href="/blog/rollup-execution-environments/">rollup execution environment guide</a>.</p>
]]></content:encoded></item><item><title>EVM vs WebAssembly: Choosing a Smart Contract Runtime</title><link>https://blockvm.com/blog/evm-vs-wasm/</link><guid isPermaLink="true">https://blockvm.com/blog/evm-vs-wasm/</guid><pubDate>Fri, 08 Nov 2024 12:00:00 +0000</pubDate><category>Foundations</category><description>Compare EVM and WebAssembly blockchain runtimes across execution model, language support, metering, host functions, compatibility, determinism, and tooling.</description><content:encoded><![CDATA[
<p>EVM and WebAssembly are both bytecode targets used in blockchain execution environments, but they solve different parts of the design problem. The EVM arrives with Ethereum-style account semantics, gas conventions, transaction behavior, and a mature application ecosystem. WebAssembly provides a portable module format and execution semantics, while the blockchain must define its own host functions, state model, ABI, metering, and feature set.</p>
<p>Choosing between them is therefore not a contest between one instruction benchmark and another. The useful comparison asks which compatibility surface the project needs, which languages and tools developers will use, how deterministic execution is enforced, and how the runtime will be upgraded and operated for years.</p>

<h2>Start with the unit of compatibility</h2>
<p>“EVM compatible” usually means more than accepting bytecode. Applications may depend on Solidity compiler behavior, ABI encoding, address derivation, transaction types, JSON-RPC methods, event logs, precompiles, gas estimation, wallet signing, and deployed infrastructure. The value of EVM compatibility is the network effect around all of those layers.</p>
<p>“WASM compatible” is a weaker application claim because WebAssembly intentionally leaves embedding details to the host. Two chains can both execute valid modules while exposing entirely different imports, entry points, storage APIs, cryptography, and account models. Portable source-language tooling does not make a contract portable unless the chain-facing ABI and host interface also match.</p>
<p>The <a href="https://webassembly.org/specs/" rel="noopener">official WebAssembly specifications</a> define core module and instruction semantics independently from a concrete embedding. That separation is a strength, but it means a blockchain runtime review must include the embedding rather than stopping at the word “WASM.”</p>

<h2>Execution model: stack machine versus structured modules</h2>
<p>The EVM is a 256-bit stack machine. Instructions consume operands from the stack, access byte-addressed memory, and read or write persistent storage through protocol opcodes. Its word size and operations were designed around Ethereum's cryptographic and account environment. High-level compilers lower functions, variables, and control flow into this model.</p>
<p>WebAssembly modules have typed functions, locals, structured control flow, linear memories, tables, imports, and exports. Values use types such as 32-bit and 64-bit integers, with optional features extending the model. A blockchain validates modules against an allowed feature set, instantiates them with host imports, and calls designated exports.</p>
<p>Neither model automatically delivers better application performance. EVM implementations are heavily optimized and benefit from mature compiler assumptions. WASM engines can use established compilation techniques, but deterministic metering, sandbox checks, host calls, and state access can dominate. Measure complete workloads rather than isolated arithmetic loops.</p>

<h2>Language support and developer expectations</h2>
<p>The EVM ecosystem is centered on Solidity, with Vyper and other languages serving narrower audiences. That concentration can be an advantage: documentation, audits, libraries, and tools share conventions. It can also limit developers who want languages and package ecosystems from outside the Ethereum world.</p>
<p>WASM can be targeted by Rust, C, C++, AssemblyScript, and additional languages. Yet a language is practical only when its blockchain SDK handles serialization, entry points, storage, panics, allocation, deterministic libraries, code-size constraints, and testing. A general compiler that emits a module is not the same as a supported smart-contract toolchain.</p>
<p>Teams should examine generated artifacts and debugging quality. Can a developer map a runtime trap back to source? Are stack traces, coverage, gas or fuel profiles, and contract interfaces available? Does the standard library accidentally import unsupported operating-system functions? The developer experience is built above the bytecode format.</p>

<h2>State access and host boundaries</h2>
<p>EVM state access is built into the execution model through account context, storage opcodes, calls, logs, and environmental instructions. The interface is constrained but deeply standardized. Contracts can assume familiar behavior across networks that faithfully reproduce the semantics.</p>
<p>A WASM blockchain generally exposes state through imports. Guest code writes keys and values into linear memory, then calls a host function with pointers and lengths. The host validates those ranges, performs the state operation, charges resources, and returns a result code or writes output back into guest memory.</p>
<p>This flexibility lets a chain design object storage, capability systems, actor messages, or custom cryptographic APIs. It also expands the consensus-critical host surface. Every import needs precise rules for memory access, errors, reentrancy, versioning, and cost. Auditing the module engine without auditing host functions leaves the most chain-specific code unexamined.</p>

<h2>Metering strategies differ</h2>
<p>EVM gas is defined at opcode and protocol-operation level. Dynamic charges account for memory expansion, storage transitions, account access, calls, and transaction data. The schedule has changed through network upgrades as denial-of-service risks and resource costs became better understood.</p>
<p>WASM does not define blockchain gas. A runtime may instrument modules by inserting fuel checks, use an engine's fuel mechanism, count basic blocks, meter host calls separately, or combine approaches. Instrumentation must preserve semantics and cannot be bypassed by unusual control flow or future features.</p>
<p>The hardest costs often occur outside the guest instruction loop: database access, hashing, signature verification, large memory copies, compilation, and cross-contract calls. A credible WASM schedule prices those operations explicitly. It also addresses module validation and compilation so an attacker cannot submit cheap code that is expensive for nodes to prepare.</p>

<h2>Determinism and feature control</h2>
<p>The EVM avoids many host-level sources of variation by defining integer operations and a narrow environment. Differences can still arise from client bugs, underspecified edge cases, precompile implementations, or protocol-version handling, so conformance testing remains critical.</p>
<p>WebAssembly's core semantics are carefully specified, but an embedding must choose enabled proposals and constrain features that complicate determinism. Floating-point behavior, threads, SIMD, reference types, memory growth, and engine optimization bugs deserve explicit policy. Chains may reject certain instructions, canonicalize outcomes, or avoid features until implementations converge.</p>
<p>Engine upgrades create another decision. A node should not replay old blocks using whatever engine happens to be installed today if that engine changes observable semantics. Protocol versions, module validation rules, cost schedules, and host APIs need historical identity.</p>

<h2>Code size, validation, and startup cost</h2>
<p>EVM bytecode is compact and simple to decode, though analyzing valid control flow can be difficult in legacy code. Deployment cost discourages very large programs. Newer formats and compiler techniques can add structure, but the installed ecosystem includes substantial historical bytecode.</p>
<p>WASM modules carry structured sections, types, imports, exports, and validation information. That structure helps tooling and ahead-of-time compilation. Modules can also be larger, and compilation can consume meaningful CPU and memory. Runtimes typically cache compiled artifacts, but caches must not become consensus dependencies or unbounded attack surfaces.</p>
<p>Review limits for module bytes, function count, locals, tables, memory pages, nesting, and compilation time. Validation should reject malformed or unsupported modules before expensive work begins. A portable format still requires chain-specific admission controls.</p>

<h2>Tooling, composability, and existing liquidity</h2>
<p>The strongest EVM argument is often ecosystem compatibility rather than machine elegance. Existing wallets can sign transactions, libraries encode calls, block explorers decode events, auditors know common patterns, and applications can integrate deployed protocols. For teams that need immediate interoperability, reproducing that surface can outweigh theoretical runtime gains.</p>
<p>A WASM environment can build a compelling ecosystem around Rust or another language, especially when the chain's state model or proof system differs substantially from Ethereum. It may offer safer source-language patterns, richer type information, or a runtime tailored to domain-specific operations. The cost is building and maintaining SDKs, wallets, indexers, debuggers, standards, and educational material.</p>
<p>Composability also depends on execution and state rules. Synchronous calls, asynchronous messages, object ownership, shared state, and transaction atomicity shape application design more than the module format alone.</p>

<h2>A decision framework</h2>
<ul>
<li><strong>Choose EVM first</strong> when Solidity talent, wallet compatibility, existing contracts, shared standards, and Ethereum-style tooling are central requirements.</li>
<li><strong>Consider WASM first</strong> when the chain needs a custom state or capability model, multiple supported languages, a portable module ecosystem, or close integration with Rust-oriented infrastructure.</li>
<li><strong>Prototype both</strong> when performance claims drive the decision. Implement representative storage, cryptography, nested calls, and error paths—not only arithmetic.</li>
<li><strong>Model upgrades</strong> before launch. Determine how old code, old blocks, engine versions, host functions, and gas schedules remain reproducible.</li>
<li><strong>Count ecosystem work</strong> as engineering work. ABI standards, debuggers, explorers, testing frameworks, and security guidance are part of the runtime product.</li>
</ul>

<h2>Conclusion</h2>
<p>EVM offers a tightly coupled execution and application compatibility surface. WebAssembly offers a portable, structured module format that a blockchain can embed behind its own state and host APIs. The former reduces ecosystem bootstrapping when Ethereum compatibility is the goal; the latter provides architectural freedom at the cost of specifying and supporting more of the surrounding runtime.</p>
<p>Use the <a href="/evm/">EVM guide</a> and <a href="/wasm-runtimes/">WASM runtime guide</a> to inspect each model in detail, then apply the broader <a href="/blockchain-virtual-machines/">blockchain VM evaluation checklist</a>.</p>
]]></content:encoded></item><item><title>Ethereum Virtual Machine Explained: Stack, Memory, Storage and Gas</title><link>https://blockvm.com/blog/ethereum-virtual-machine-explained/</link><guid isPermaLink="true">https://blockvm.com/blog/ethereum-virtual-machine-explained/</guid><pubDate>Thu, 27 Jun 2024 12:00:00 +0000</pubDate><category>EVM Development</category><description>Follow an EVM message call through bytecode, the 256-bit stack, temporary memory, persistent storage, gas accounting, nested calls, logs, and reverts.</description><content:encoded><![CDATA[
<p>The Ethereum Virtual Machine is a deterministic, stack-based computer embedded in Ethereum's state-transition rules. A transaction or message call selects an account, supplies calldata and gas, and causes bytecode to execute one opcode at a time. The machine manipulates 256-bit words, expands temporary memory, reads or writes persistent storage, creates logs, calls other accounts, and either returns successfully or halts with a defined failure.</p>
<p>That compact description hides the compatibility surface developers actually use. EVM behavior includes account context, transaction envelopes, contract creation, precompiles, call variants, revert data, event logs, gas rules, and application binary interface conventions. Understanding those layers makes debugging and security review much easier than treating Solidity source as if it ran directly.</p>

<h2>From source code to runtime bytecode</h2>
<p>Solidity and other high-level languages compile into EVM bytecode. Deployment uses a creation transaction whose initialization code runs once. That code can validate constructor arguments, initialize state, and return a second byte sequence: the runtime bytecode stored at the new contract address. Future calls execute the runtime portion, not the constructor.</p>
<p>Compilers also emit an ABI, source maps, metadata, and sometimes storage-layout information. The ABI describes how external function selectors and arguments are encoded; it is a tooling convention rather than an opcode feature. Source maps connect instruction offsets back to source files and lines. When reviewing a deployed contract, bytecode is the authority, while compiler artifacts make it intelligible.</p>
<p>The official <a href="https://ethereum.org/developers/docs/evm/" rel="noopener">Ethereum EVM documentation</a> describes the machine as a stack architecture with volatile memory and persistent storage. Those three data areas behave differently enough that confusing them is a common source of performance and security mistakes.</p>

<h2>The 256-bit operand stack</h2>
<p>Most opcodes take operands from the top of the stack and push results back. Arithmetic, comparisons, bitwise operations, hashing setup, memory access, storage access, control flow, and calls all use this pattern. The word size is 256 bits, which aligns with common cryptographic values and addresses padded into words, but it also means small integers occupy full stack slots during execution.</p>
<p>The stack is not random-access general memory. Duplication and swap instructions reach only a limited range near the top, so compilers schedule values carefully. Deep expressions can create “stack too deep” pressure at the source level because too many live values must remain accessible. Optimizing compilers may move values through memory or restructure code to manage this constraint.</p>
<p>Raw traces list program counter, opcode, gas, and sometimes stack values. Reading them is easier when you treat each call frame separately. The same bytecode can run under different address or storage context depending on the call type, so opcode rows alone do not tell the full story.</p>

<h2>Memory is temporary and byte-addressed</h2>
<p>EVM memory begins empty for each message call. It is a linear byte array used for ABI decoding, hashing input, dynamic arrays, return data, and temporary structures. Instructions read and write 32-byte words, single bytes, or copied regions. Memory disappears when the current call frame ends.</p>
<p>Memory expansion costs gas. The charge depends on the highest region touched and grows nonlinearly, so an accidental pointer to a huge offset can consume the remaining budget. Compilers maintain a free-memory pointer by convention, but the VM itself does not understand high-level object lifetimes. Assembly code must avoid overwriting reserved regions or aliasing data unexpectedly.</p>
<p>Calldata is separate. It is a read-only byte sequence supplied to the call and is often cheaper to use directly for external-function parameters than copying everything into memory. Return data is another distinct buffer populated after an external call. Keeping these regions conceptually separate helps explain the source-language location keywords <code>calldata</code>, <code>memory</code>, and <code>storage</code>.</p>

<h2>Storage is persistent contract state</h2>
<p>Each contract account has a persistent mapping from 256-bit slots to 256-bit values. High-level variables are assigned to slots according to compiler layout rules. Fixed-size values may be packed together, while mappings and dynamic arrays derive locations through hashing. A source-level structure can therefore generate several storage reads, hashes, and writes.</p>
<p>Storage is expensive because every node must maintain the resulting shared state. Gas cost depends on whether a slot is cold or already accessed, whether the old value is zero, whether the new value clears or sets data, and which protocol rules are active. The transaction maintains an access context, so repeated reads in one execution can cost less than the first.</p>
<p>Writes are journaled. If a call reverts, changes made in that frame and its successful descendants are discarded according to call semantics. Logs are reverted too. This makes storage look transactional, but external interactions can still create reentrancy: another contract may call back before the original function has completed its intended state sequence.</p>

<h2>Gas is both a limit and a semantic input</h2>
<p>A transaction provides a gas limit, and opcodes deduct from the remaining amount. If execution reaches zero before a successful halt, the current call fails with an out-of-gas exception. Because gas affects whether later instructions execute, the budget is part of program behavior. Forwarded gas to nested calls, stipends, memory expansion, and dynamic opcode charges can change the observed path.</p>
<p>A deliberate <code>REVERT</code> differs from running out of gas. It rolls back state in the frame but can return structured bytes and leaves unused gas available to the caller. High-level custom errors and revert strings are encoded into that data. A caller may bubble the failure up, decode it, or handle it and continue.</p>
<p>Gas estimates are simulations against a particular state and transaction context. They can become inaccurate when storage values, access lists, base fees, block context, or called contracts change. Production systems should include reasonable headroom and monitor actual consumption rather than treating one estimate as a permanent constant.</p>

<h2>Calls change code and context in different ways</h2>
<p>A regular call executes the target account's code with the target's storage and address context. The caller can send value and choose how much gas to forward. A static call adds a read-only restriction that prevents state-changing operations. A delegate call executes another account's code while retaining the caller's address, storage, original sender, and value context.</p>
<p>Delegate call enables reusable libraries and proxy upgrade patterns, but it also couples code to the caller's storage layout. If implementation versions assign variables to different slots, new logic can overwrite existing state. Authorization is equally important because implementation code can modify the proxy's assets and roles as if it were the proxy.</p>
<p>Calls can fail without automatically reverting the caller. Low-level call instructions return a success flag and bytes. High-level interfaces usually turn failure into an exception, but assembly and low-level wrappers must check the flag explicitly. Ignoring it can convert a failed transfer or authorization step into an apparently successful transaction.</p>

<h2>Control flow, halting, and invalid execution</h2>
<p>The program counter identifies the next opcode. Jumps are allowed only to valid jump destinations in legacy EVM bytecode, and compilers build branches and loops from those primitives. Execution can halt successfully with no data, return a byte range, revert with data, or encounter an exceptional condition such as invalid opcode, stack underflow, bad jump, or out of gas.</p>
<p>Nested execution creates a tree of frames. A child can succeed and return data, fail and return revert data, or consume its gas. The parent decides what to do next unless its own language wrapper automatically propagates the exception. Debuggers that render the call tree and decoded inputs usually reveal the cause faster than a flat opcode list.</p>

<h2>Logs are receipts, not contract-readable storage</h2>
<p>Contracts emit logs with topics and data. Nodes include them in transaction receipts, and external indexers use them to build searchable event histories. Logs are cheaper than persistent storage for information that contracts do not need to read later, but they are not available to future EVM execution as state.</p>
<p>Applications should not confuse an emitted event with enforcement. A contract must still update the storage that future calls rely on. Indexers must also handle chain reorganizations and removed logs. An event interface is an off-chain consumption surface, not an independent source of protocol truth.</p>

<h2>A practical trace-reading workflow</h2>
<ol>
<li>Confirm transaction sender, target, value, calldata, gas limit, and chain context.</li>
<li>Decode the top-level selector and arguments using the correct ABI.</li>
<li>Open the call tree and find the deepest frame that first failed.</li>
<li>Inspect its revert bytes, source location, and nearby storage or memory operations.</li>
<li>Walk upward to see whether callers propagated, wrapped, or ignored the failure.</li>
<li>Compare state reads with the assumptions made by the test or user interface.</li>
<li>Reproduce on a pinned local fork when the failure depends on live contracts.</li>
</ol>

<h2>Conclusion</h2>
<p>The EVM is small enough to describe precisely but rich enough to support a large software ecosystem. The stack carries immediate operands, memory holds temporary bytes, storage persists contract state, calldata supplies read-only input, and gas bounds every path. Calls add context, logs expose receipt data, and reverts define transactional failure boundaries.</p>
<p>For a focused resource model, read <a href="/blog/gas-bytecode-and-determinism/">gas, bytecode, and determinism</a>. For hands-on workflow, continue to the <a href="/blog/local-evm-development-stack/">local EVM development stack</a> and the <a href="/blog/virtual-machine-security-review/">virtual-machine security review</a>.</p>
]]></content:encoded></item><item><title>What Is a Blockchain Virtual Machine? A Practical Architecture Guide</title><link>https://blockvm.com/blog/what-is-a-blockchain-virtual-machine/</link><guid isPermaLink="true">https://blockvm.com/blog/what-is-a-blockchain-virtual-machine/</guid><pubDate>Wed, 14 Feb 2024 12:00:00 +0000</pubDate><category>Foundations</category><description>A blockchain virtual machine turns ordered transactions into deterministic state changes. Learn the roles of bytecode, state, host functions, gas, and verification.</description><content:encoded><![CDATA[
<p>A blockchain virtual machine is the execution environment that applies program rules to ordered inputs. It receives a transaction, message, or batch together with an agreed pre-state; executes code under a constrained instruction set and host interface; meters resource use; and produces outputs plus a candidate post-state. Every honest participant must be able to reproduce that result. The machine is therefore not merely a developer convenience. It is part of the protocol's definition of validity.</p>
<p>The phrase “virtual machine” can suggest a miniature desktop computer, but blockchain VMs are usually narrower. They do not offer arbitrary files, sockets, clocks, devices, or operating-system calls. They expose only capabilities the protocol can reproduce. That restriction is what lets independent nodes on different hardware reach the same state after processing the same chain history.</p>

<h2>The VM sits between consensus and state</h2>
<p>Consensus and execution solve different problems. Consensus chooses an ordered history: which block or batch is accepted, which transactions it contains, and where it sits relative to earlier blocks. Execution interprets that history. Given the same pre-state and the same ordered inputs, the runtime computes balances, storage writes, events, return values, fees, and a new state commitment.</p>
<p>This separation is useful when reviewing architecture. A fast consensus protocol cannot compensate for ambiguous execution semantics. Likewise, a carefully specified VM does not decide who may propose blocks or how competing histories are resolved. Rollups make the distinction even clearer because sequencing, data publication, execution, and settlement may be separate modules operated under different assumptions.</p>
<p>One early description of this model appears in the <a href="https://ethereum.org/en/whitepaper/" rel="noopener">Ethereum whitepaper</a>, which presents EVM code as low-level bytecode executed by a protocol-defined machine. Modern execution environments vary widely, but the central idea remains: program behavior must be reduced to rules that every verifier can apply.</p>

<h2>The core inputs and outputs</h2>
<h3>Pre-state and program identity</h3>
<p>The pre-state represents everything execution is allowed to observe before the current input: account data, contract storage, object ownership, code hashes, protocol parameters, or other chain-specific records. The runtime also needs a stable program identity. That may be deployed bytecode at an address, a module hash, a package identifier, or a verified guest image in a zkVM.</p>
<h3>Transaction or message input</h3>
<p>The input commonly includes a caller, destination, value, method selector, encoded arguments, authorization data, and resource limit. A block environment may add the chain identifier, block height, timestamp, base fee, or randomness value. The protocol supplies these facts explicitly because reading the local machine's clock or random device would make execution diverge.</p>
<h3>Outputs and state effects</h3>
<p>Execution can return bytes, emit logs, create new programs, transfer assets, invoke other programs, consume metered resources, or fail. Persistent writes are usually staged until the relevant call or transaction succeeds. A revert discards those writes according to defined call-frame rules. The resulting state is summarized by a commitment, often called a state root, that later blocks can reference.</p>

<h2>Determinism is broader than the instruction set</h2>
<p>A deterministic opcode table is necessary but not sufficient. The runtime must control serialization, integer behavior, memory bounds, map iteration, error codes, cryptographic libraries, floating-point corner cases, thread scheduling, and host-call results. Two implementations that interpret an underspecified edge case differently can produce different state roots and split the network.</p>
<p>For that reason, a mature VM is surrounded by specifications, test vectors, conformance suites, reference implementations, and differential tests. Client teams run identical inputs through multiple implementations and compare traces or outputs. Historical replay is another important test: a new client should reproduce old blocks using the semantics and protocol parameters that were active at the time.</p>
<blockquote>Determinism is a property of the complete execution environment: bytecode rules, host functions, state engine, cost schedule, version gates, and error handling.</blockquote>

<h2>Program representation shapes the ecosystem</h2>
<p>Some chains use a purpose-built bytecode such as the EVM's stack-oriented instruction set. Others embed WebAssembly modules, use a register machine, execute a constrained native format, or prove a general-purpose instruction set inside a zero-knowledge VM. The representation influences compiler complexity, code size, static validation, language support, and the ease of building debuggers.</p>
<p>Compatibility claims should be read carefully. Supporting Solidity syntax is not identical to reproducing the full EVM. Wallets and applications may depend on transaction envelopes, address derivation, precompiles, event logs, revert data, RPC methods, fee behavior, and subtle call semantics. Conversely, a non-EVM runtime can offer excellent tooling if its ABI, SDKs, testing environment, and indexers are coherent.</p>

<h2>Host functions define the sandbox boundary</h2>
<p>Guest code needs controlled ways to interact with shared state and protocol services. Host functions may load and store values, compute hashes, verify signatures, emit events, call another program, inspect the caller, or read block context. They are often more security-sensitive than ordinary arithmetic instructions because they cross from sandboxed code into the node implementation.</p>
<p>A host interface should specify valid input ranges, failure modes, memory-copy behavior, reentrancy, resource charges, and versioning. The runtime must validate pointers and lengths supplied by untrusted guest code. It must also prevent a host function from exposing nondeterministic operating-system behavior. A small instruction set with an enormous, loosely specified host surface is not necessarily a small consensus risk.</p>

<h2>Metering keeps execution finite and prices pressure</h2>
<p>Public execution environments must stop programs from consuming unlimited CPU, memory, storage, or bandwidth. Gas is the familiar term, but runtimes may also use instruction fuel, weighted host calls, cycle limits, memory pages, proof segments, or data-byte charges. A transaction supplies a budget, and execution halts when that budget is exhausted.</p>
<p>A cost schedule is also a denial-of-service defense. If an operation is cheap in the VM but expensive for nodes to perform, an attacker can fill blocks with pathological work. Schedules therefore evolve as implementations and hardware change. Upgrading them is delicate because cost affects which transactions succeed, how applications estimate fees, and whether historical blocks remain replayable.</p>
<p>Metering should reflect more than average CPU time. Persistent writes can increase state size, cryptographic functions can dominate compute, memory expansion can amplify copying, and data publication can be the largest rollup cost. The useful question is not “does this VM have gas?” but “which scarce resources are bounded, and how accurately does the model track them?”</p>

<h2>Verification determines who must trust execution</h2>
<p>On a conventional layer-one network, validators commonly re-execute every transaction. In an optimistic rollup, nodes reconstruct execution while a dispute system allows invalid commitments to be challenged. In a validity system, a prover produces cryptographic evidence that a state transition followed the rules. A zkVM can make a general program's execution verifiable without every verifier repeating all work.</p>
<p>These paths impose different requirements. Re-execution favors efficient deterministic clients. Fault proofs need a trace or step function that can isolate disagreement. Validity proofs need an arithmetization, prover infrastructure, and a verifier compatible with the settlement environment. A runtime cannot be evaluated independently from the way its outputs become accepted facts.</p>

<h2>How to evaluate a blockchain VM</h2>
<ul>
<li><strong>Semantics:</strong> Are instructions, host calls, failures, and version changes specified precisely?</li>
<li><strong>State:</strong> What can programs read and write, and how are updates committed, rolled back, and proven?</li>
<li><strong>Isolation:</strong> Which capabilities are available to guest code, and how is untrusted memory validated?</li>
<li><strong>Metering:</strong> Which resources are bounded, and can the cost schedule resist pathological workloads?</li>
<li><strong>Compatibility:</strong> Which compilers, libraries, wallets, RPC methods, indexers, and debuggers work in practice?</li>
<li><strong>Verification:</strong> Who re-executes, who can challenge, or which proof establishes correctness?</li>
<li><strong>Operations:</strong> Can node operators trace, profile, replay, snapshot, and diagnose divergent execution?</li>
<li><strong>Governance:</strong> Who can change semantics, and how are upgrades activated without ambiguous history?</li>
</ul>

<h2>Conclusion</h2>
<p>A blockchain virtual machine is best understood as a deterministic state-transition system, not as an isolated bytecode interpreter. Program format, host functions, state storage, resource accounting, error behavior, versioning, and verification all contribute to the protocol's meaning. Once those layers are separated, comparisons become more useful: the question shifts from which VM has the loudest ecosystem to which execution model fits the application's trust, performance, and tooling constraints.</p>
<p>Continue with the <a href="/evm/">EVM architecture guide</a>, compare <a href="/wasm-runtimes/">WebAssembly blockchain runtimes</a>, or see how <a href="/zkvm/">zero-knowledge VMs prove execution</a>.</p>
]]></content:encoded></item></channel></rss>