CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/pypi-pyevmasm

Ethereum Virtual Machine (EVM) assembler and disassembler library for working with EVM bytecode and assembly instructions

Pending
Overview
Eval results
Files

fork-management.mddocs/

Fork Management

Comprehensive support for all Ethereum hard forks with proper opcode evolution, gas cost updates, and utilities for fork selection and block number conversion. PyEVMAsm accurately handles the historical development of the EVM instruction set across all major Ethereum upgrades.

Capabilities

Fork Constants and Configuration

Default fork configuration and comprehensive fork support information.

DEFAULT_FORK: str  # Current default fork name ("istanbul")

Usage Examples:

from pyevmasm import DEFAULT_FORK

print(f"Default fork: {DEFAULT_FORK}")  # istanbul

# Supported forks can be accessed via instruction_tables.keys()
from pyevmasm import instruction_tables
supported_forks = tuple(instruction_tables.keys())
print(f"Supported forks: {supported_forks}")
# ('frontier', 'homestead', 'tangerine_whistle', 'spurious_dragon',
#  'byzantium', 'constantinople', 'petersburg', 'serenity', 'istanbul')

# Check if a fork is supported
fork_name = "byzantium"
if fork_name in instruction_tables:
    print(f"{fork_name} is supported")

Instruction Tables

Fork-specific instruction lookup tables providing accurate opcode definitions for each Ethereum hard fork.

instruction_tables: dict  # Mapping of fork names to InstructionTable instances

# Individual fork instruction tables
frontier_instruction_table: InstructionTable
homestead_instruction_table: InstructionTable  
tangerine_whistle_instruction_table: InstructionTable
spurious_dragon_instruction_table: InstructionTable
byzantium_instruction_table: InstructionTable
constantinople_instruction_table: InstructionTable
serenity_instruction_table: InstructionTable
istanbul_instruction_table: InstructionTable

Usage Examples:

from pyevmasm import instruction_tables

# Access fork-specific instruction table
byzantium_table = instruction_tables["byzantium"]
frontier_table = instruction_tables["frontier"]

# Look up instructions by opcode
returndatasize = byzantium_table[0x3d]  # RETURNDATASIZE
print(f"Byzantium 0x3d: {returndatasize.name}")  # RETURNDATASIZE

# Same opcode in frontier (before RETURNDATASIZE existed)
try:
    frontier_returndatasize = frontier_table[0x3d] 
    print(f"Frontier 0x3d: {frontier_returndatasize.name}")  # INVALID
except KeyError:
    print("0x3d not defined in frontier")

# Look up by mnemonic
push1 = byzantium_table["PUSH1"]
print(f"PUSH1 opcode: 0x{push1.opcode:02x}")  # 0x60

InstructionTable Class

Fork-specific instruction lookup and iteration capabilities.

class InstructionTable:
    """
    EVM Instruction factory providing fork-specific instruction definitions.
    Implements an immutable, iterable instruction lookup table indexed by 
    both mnemonic and opcode.
    """
    
    def __getitem__(self, key) -> Instruction:
        """
        Get instruction by opcode (int) or mnemonic (str).
        
        Parameters:
        - key (int | str): Opcode value (0x00-0xFF) or mnemonic name
        
        Returns:
        Instruction: New Instruction instance for the specified opcode/mnemonic
        
        Raises:
        KeyError: If opcode/mnemonic not found in this fork
        """
    
    def get(self, key, default=None) -> Instruction:
        """
        Get instruction with default fallback.
        
        Parameters:
        - key (int | str): Opcode value or mnemonic name
        - default: Default value if key not found
        
        Returns:
        Instruction | default: Instruction instance or default value
        """
    
    def __contains__(self, key) -> bool:
        """Check if opcode or mnemonic exists in this fork."""
    
    def __iter__(self):
        """Iterate over all instructions in opcode order."""
    
    def keys(self):
        """Get sorted list of all opcodes in this fork."""

Usage Examples:

from pyevmasm import instruction_tables

# Get instruction table for specific fork
istanbul_table = instruction_tables["istanbul"]

# Multiple ways to access instructions
push1_by_opcode = istanbul_table[0x60]        # By opcode
push1_by_name = istanbul_table["PUSH1"]       # By mnemonic
print(f"Same instruction: {push1_by_opcode.name == push1_by_name.name}")  # True

# Safe access with default
unknown = istanbul_table.get(0xff, None)  # SELFDESTRUCT or None depending on context
valid_instr = istanbul_table.get("ADD")    # Returns ADD instruction

# Check existence
has_returndatasize = "RETURNDATASIZE" in istanbul_table  # True
has_returndatasize_frontier = "RETURNDATASIZE" in instruction_tables["frontier"]  # False

# Iterate all instructions
for instruction in istanbul_table:
    if instruction.is_arithmetic:
        print(f"Arithmetic: {instruction.name}")

# Get all opcodes
all_opcodes = istanbul_table.keys()
print(f"Total opcodes in Istanbul: {len(all_opcodes)}")

Block Number to Fork Conversion

Utility function to determine the appropriate fork for a given Ethereum block number.

def block_to_fork(block_number: int) -> str:
    """
    Convert Ethereum block number to appropriate fork name.
    
    Parameters:
    - block_number (int): Ethereum block number
    
    Returns:
    str: Fork name appropriate for the given block number
    
    Example:
    >>> block_to_fork(0)
    'frontier'
    >>> block_to_fork(4370000)
    'byzantium'
    >>> block_to_fork(7280000)
    'petersburg'
    """

Usage Examples:

from pyevmasm import block_to_fork

# Historical block analysis
print(f"Block 0: {block_to_fork(0)}")           # frontier
print(f"Block 1150000: {block_to_fork(1150000)}")  # homestead  
print(f"Block 4370000: {block_to_fork(4370000)}")  # byzantium
print(f"Block 7280000: {block_to_fork(7280000)}")  # petersburg
print(f"Recent block: {block_to_fork(15000000)}")  # petersburg (latest)

# Use in analysis
def analyze_transaction_bytecode(bytecode, block_number):
    fork = block_to_fork(block_number)
    return disassemble_hex(bytecode, fork=fork)

Fork Evolution and Differences

Frontier (Block 0)

  • Original EVM instruction set
  • Basic arithmetic, comparison, bitwise, and system operations
  • All core opcodes: STOP, ADD, MUL, PUSH, POP, MLOAD, SSTORE, CALL, etc.

Homestead (Block 1,150,000)

  • Added DELEGATECALL (0xf4)
  • Enhanced contract creation security

Tangerine Whistle (Block 2,463,000)

  • Gas cost increases for several opcodes
  • EXTCODESIZE, EXTCODECOPY, BALANCE, SLOAD, CALL operations became more expensive
  • SELFDESTRUCT gas cost increased

Spurious Dragon (Block 2,675,000)

  • No new opcodes
  • Additional gas cost and state tree adjustments

Byzantium (Block 4,370,000)

  • Added RETURNDATASIZE (0x3d), RETURNDATACOPY (0x3e)
  • Added STATICCALL (0xfa) for state-reading calls
  • Added REVERT (0xfd) for better error handling

Constantinople/Petersburg (Block 7,280,000)

  • Added shift operations: SHL (0x1b), SHR (0x1c), SAR (0x1d)
  • Added EXTCODEHASH (0x3f) for code hash retrieval
  • Added CREATE2 (0xf5) for deterministic contract addresses

Istanbul (Latest Default)

  • Updated gas costs for BALANCE, EXTCODEHASH, SLOAD
  • Added CHAINID (0x46) and SELFBALANCE (0x47)

Fork Compatibility Examples:

from pyevmasm import assemble_hex, disassemble_one

# Instructions available in different forks
def test_fork_compatibility():
    # This works in all forks
    basic_code = assemble_hex("PUSH1 0x01\nADD", fork="frontier")
    
    # DELEGATECALL introduced in homestead
    try:
        homestead_code = assemble_hex("DELEGATECALL", fork="homestead")  # Works
        frontier_code = assemble_hex("DELEGATECALL", fork="frontier")    # Fails
    except AssembleError:
        print("DELEGATECALL not available in frontier")
    
    # RETURNDATASIZE introduced in byzantium
    byzantium_instr = disassemble_one(b'\x3d', fork="byzantium")  # RETURNDATASIZE
    frontier_instr = disassemble_one(b'\x3d', fork="frontier")    # INVALID
    
    # Gas costs change between forks
    balance_frontier = disassemble_one(b'\x31', fork="frontier")   # fee: 20
    balance_istanbul = disassemble_one(b'\x31', fork="istanbul")   # fee: 700
    
    print(f"BALANCE gas: frontier={balance_frontier.fee}, istanbul={balance_istanbul.fee}")

# Choose appropriate fork for analysis
def analyze_historical_transaction(bytecode, block_number):
    fork = block_to_fork(block_number)
    instructions = list(disassemble_all(bytecode, fork=fork))
    
    # Analysis with correct historical opcodes
    for instr in instructions:
        if instr.name == "INVALID":
            print(f"Invalid opcode 0x{instr.opcode:02x} at block {block_number} (fork: {fork})")

Best Practices

Fork Selection

  • Use DEFAULT_FORK for current/modern bytecode analysis
  • Use block_to_fork() for historical transaction analysis
  • Specify explicit fork when analyzing specific deployment contexts
  • Test compatibility when working with cross-fork smart contracts

Instruction Table Caching

  • Instruction tables are pre-computed and immutable
  • Safe to cache table references for performance
  • Each table lookup creates new Instruction instances

Version-Aware Analysis

from pyevmasm import instruction_tables, block_to_fork

def version_aware_analysis(bytecode, context_block=None):
    if context_block:
        fork = block_to_fork(context_block)
    else:
        fork = DEFAULT_FORK
    
    table = instruction_tables[fork]
    # Proceed with fork-appropriate analysis
    return disassemble_all(bytecode, fork=fork)

Install with Tessl CLI

npx tessl i tessl/pypi-pyevmasm

docs

assembly.md

disassembly.md

fork-management.md

index.md

instruction-analysis.md

tile.json