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.
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.
Bytecode is the protocol-facing program
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.
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.
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.
Gas bounds the path, not just the program
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.
The Ethereum gas documentation 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.
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.
Static costs are only the beginning
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.
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.
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.
Memory expansion prevents unbounded address tricks
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.
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.
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.
Storage pricing protects shared state
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.
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.
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.
Host functions need independent metering
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.
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.
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.
Determinism limits environmental access
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.
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.
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.
Protocol versions freeze meaning at a block boundary
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.
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.
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.
Out-of-gas and revert are different failures
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.
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.
A practical resource-review checklist
- Pin the compiler, optimizer, runtime version, and target protocol rules.
- Measure representative state transitions, not only empty-state unit tests.
- Inspect memory growth, copy lengths, storage access, and nested calls in traces.
- Include worst-case and malformed inputs for host functions and precompiles.
- Track gas or cycle snapshots in continuous integration to catch regressions.
- Test failure behavior with low gas, child-call failure, and revert data.
- Review upgrade boundaries when opcode costs or semantics change.
- Separate execution cost from data-publication, proof, and long-term state costs.
Conclusion
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.
See the EVM guide for machine structure, the EVM execution walkthrough for call-level detail, and the VM security review guide for a broader audit framework.



