EEVM.Interpreter.Instructions.Helpers (eevm v0.1.0)

Copy Markdown View Source

Shared utilities for opcode implementations.

Provides:

  • Stack-pop / push helpers for the common pop-pop-compute-push shape used by arithmetic, comparison, and bitwise opcodes.
  • Two's-complement conversion (to_signed/1, to_unsigned/1) bridging the uint256 stack with signed opcodes (SLT, SGT, SDIV, SMOD, SAR).
  • mod_pow/3, square-and-multiply modular exponentiation used by EXP.
  • pop_address/1 for opcodes that take an address operand.
  • touch_account/3, which before EIP-161 creates the accounts a message call or fee credit touches. The transaction handler shares it.

Summary

Functions

Pops two values, applies a bitwise function, and pushes the result.

Pops two values from the stack, applies a comparison, and pushes 1 or 0.

Computes base^exp mod m using square-and-multiply (binary exponentiation).

Pops an address operand: the top stack word truncated to its low 160 bits, as BALANCE, EXTCODESIZE, EXTCODECOPY, EXTCODEHASH, the CALL family, and SELFDESTRUCT read it (Yellow Paper Appendix H, μs[0] mod 2^160).

Pushes a value onto the stack and advances the program counter by 1.

Same as comparison_op/2 but interprets both values as signed 256-bit integers.

Converts a uint256 value to a signed 256-bit integer (two's complement).

Converts a signed integer back to its uint256 (two's complement) representation.

Before EIP-161 (Spurious Dragon), touching an account that doesn't exist creates it empty, even when no value moves: the target of every message call, and the coinbase credited a zero fee (execution-specs Frontier process_message calls touch_account; process_transaction sets the coinbase balance). From EIP-161 the account is not created, and touched empty accounts are deleted when the transaction ends.

Functions

bitwise_op(state, fun)

Pops two values, applies a bitwise function, and pushes the result.

Used by AND (0x16), OR (0x17), and XOR (0x18). The function fun receives a and b as uint256 values and must return a uint256.

comparison_op(state, fun)

Pops two values from the stack, applies a comparison, and pushes 1 or 0.

Used by LT (0x10), GT (0x11), and EQ (0x14). The comparison function fun receives a (first pop) and b (second pop) as unsigned integers.

mod_pow(base, exp, m)

Computes base^exp mod m using square-and-multiply (binary exponentiation).

Used by EXP (0x0A). The naive approach of multiplying base by itself exp times would be O(exp) — unusable for 256-bit exponents. Square-and-multiply is O(log exp), making it practical even for large values.

pop_address(stack)

@spec pop_address(EEVM.Interpreter.Stack.t()) ::
  {:ok, non_neg_integer(), EEVM.Interpreter.Stack.t()} | {:error, atom()}

Pops an address operand: the top stack word truncated to its low 160 bits, as BALANCE, EXTCODESIZE, EXTCODECOPY, EXTCODEHASH, the CALL family, and SELFDESTRUCT read it (Yellow Paper Appendix H, μs[0] mod 2^160).

iex> {:ok, stack} = EEVM.Interpreter.Stack.push(EEVM.Interpreter.Stack.new(), 2 ** 200 + 0xAB)
iex> {:ok, address, _} = EEVM.Interpreter.Instructions.Helpers.pop_address(stack)
iex> address
0xAB

push_value(state, value)

Pushes a value onto the stack and advances the program counter by 1.

This is the final step in almost every opcode: after computing a result, push it and move to the next instruction.

signed_comparison_op(state, fun)

@spec signed_comparison_op(
  EEVM.Interpreter.MachineState.t(),
  (integer(), integer() -> boolean())
) ::
  {:ok, EEVM.Interpreter.MachineState.t()}
  | {:error, atom(), EEVM.Interpreter.MachineState.t()}

Same as comparison_op/2 but interprets both values as signed 256-bit integers.

Used by SLT (0x12) and SGT (0x13). Converts each uint256 operand to two's complement before applying the comparison, then pushes 1 or 0.

to_signed(value)

@spec to_signed(non_neg_integer()) :: integer()

Converts a uint256 value to a signed 256-bit integer (two's complement).

Any value with the 255th bit set (>= 2^255) is treated as negative. For example, the uint256 representation of -1 is 2^256 - 1 (all bits set).

to_unsigned(value)

@spec to_unsigned(integer()) :: non_neg_integer()

Converts a signed integer back to its uint256 (two's complement) representation.

Negative values are mapped to the upper half of the uint256 range. Positive values (and zero) pass through unchanged.

touch_account(db, address, hardfork)

Before EIP-161 (Spurious Dragon), touching an account that doesn't exist creates it empty, even when no value moves: the target of every message call, and the coinbase credited a zero fee (execution-specs Frontier process_message calls touch_account; process_transaction sets the coinbase balance). From EIP-161 the account is not created, and touched empty accounts are deleted when the transaction ends.

Example

iex> alias EEVM.{Database, HardforkConfig}
iex> alias EEVM.Interpreter.Instructions.Helpers
iex> db = EEVM.Database.InMemory.new()
iex> Database.account_exists?(Helpers.touch_account(db, 0xAB, HardforkConfig.new(:frontier)), 0xAB)
true
iex> Database.account_exists?(Helpers.touch_account(db, 0xAB, HardforkConfig.new(:spurious_dragon)), 0xAB)
false