py-evm Documentation

repository·main·Indexed 25 days ago

https://github.com/ethereum/py-evm

A Python implementation of the Ethereum Virtual Machine (EVM) designed for readability, research flexibility, and testing. The library provides tools for incremental block building, custom chain implementations via BaseChain, and a comprehensive database layer including AtomicDB and MemoryDB. It includes RLP encoding/decoding for accounts, headers, logs, and receipts, as well as chain builder utilities and state test fillers for creating JSON consensus tests.

Tokens
9.3K
Snippets
17
Records
57
Agent score
81%

What's inside py-evm

  1. Use the eth.abc abstract base classes for interface implementation

    main

    The eth.abc module provides a collection of Abstract Base Classes (ABCs) that define the documented interfaces for the py-evm ecosystem. Developers implementing custom components (such as new database backends, custom transaction types, or specialized execution environments) should inherit from these classes to ensure compatibility with the rest of the library.

    Key interface groups include:

    • Blockchain Data: BlockAPI, BlockHeaderAPI, LogAPI, ReceiptAPI, ChainAPI.
    • Transactions: BaseTransactionAPI, UnsignedTransactionAPI, SignedTransactionAPI, TransactionFieldsAPI.
    • State & Storage: StateAPI, AccountDatabaseAPI, AccountStorageDatabaseAPI, DatabaseAPI, AtomicDatabaseAPI.
    • Execution Engine: VirtualMachineAPI, ExecutionContextAPI, TransactionExecutorAPI, ComputationAPI, GasMeterAPI.
    • EVM Internals: StackAPI, MemoryAPI, OpcodeAPI, CodeStreamAPI, StackManipulationAPI.
  2. Use Chain Builder utilities to construct and initialize chains

    main

    The eth.tools.builder.chain module provides utilities to reduce boilerplate when constructing chain classes, initializing chains to a genesis state, and building out chains of blocks. These tools are designed to work effectively with cytoolz.pipe for functional-style chain transformations.

    Constructing Chain Classes

    Use these utilities to configure the properties of a chain class:

    • fork_at: Creates a fork at a specific block.
    • dao_fork_at: Specifically handles DAO fork configurations.
    • disable_dao_fork: Disables the DAO fork logic.
    • enable_pow_mining: Enables Proof-of-Work mining.
    • disable_pow_check: Disables Proof-of-Work checks.
    • name: Sets the chain name.
    • chain_id: Sets the chain ID.

    Initializing Chains

    • genesis: Initializes a chain into its genesis state.

    Building Chains (Block Management)

    Use these utilities to manipulate the chain state and block history:

    • copy: Creates a copy of the chain.
    • import_block: Imports a single block into the chain.
    • import_blocks: Imports a sequence of blocks.
    • mine_block: Mines a single block.
    • mine_blocks: Mines multiple blocks.
    • chain_split: Splits a chain into multiple branches.
    • at_block_number: Navigates or retrieves the chain at a specific block height.
  3. How Messages and Computations work during execution

    main

    In Py-EVM, transaction execution is modeled through the relationship between a Message and a Computation:

    1. Message: This is the input to the VM execution layer. It encapsulates the transaction data required to initiate execution, specifically parameters like sender, value, and to.
    2. Computation: This is the interface used to implement opcode logic. It tracks the state during execution (such as the stack, memory, and gas metering) and produces the final results (such as return data, gas consumption, refunds, or execution errors).
  4. Access Ethereum protocol forks in py-evm

    main

    The eth.vm.forks package provides specialized implementations of the Ethereum Virtual Machine for different protocol hard forks. Each fork typically exposes three core components to manage the execution environment:

    1. VM Class: The main execution engine (e.g., FrontierVM, LondonVM).
    2. State Class: Manages the account and storage state for that specific fork (e.g., FrontierState, LondonState).
    3. Computation Class: Handles the logic for executing transactions and state transitions (e.g., FrontierComputation, LondonComputation).

    Available forks include:

    • frontier
    • homestead
    • tangerine_whistle
    • spurious_dragon
    • byzantium
    • constantinople
    • petersburg
    • istanbul
    • muir_glacier
    • berlin
    • london
    • arrow_glacier
    • gray_glacier
  5. How State Test Fillers work

    main

    State Test Fillers in eth.tools.fixtures are tools used to create standard JSON consensus tests (compatible with the ethereum/tests repository). Currently, only VM and state tests are supported.

    The process follows two distinct stages:

    1. Writing a Filler: You write a high-level description of the test case using a functional approach. This is represented as a nested dictionary.
    2. Filling (Compilation): The filler is compiled into an actual test. This process involves calculating the resulting state root and other necessary consensus data.

    To compile a filler, use the eth.tools.fixtures.fillers.fill_test function, which handles the compilation and accepts additional parameters that cannot be inferred from the filler dictionary itself.

  6. How to create simple opcodes

    main

    An opcode in Py-EVM is a function that takes a eth.vm.computation.BaseComputation instance as its sole argument. If the function returns a value, it is discarded during normal VM execution. You can interact with the computation state, such as consuming gas, using methods on the computation object.

    def noop(computation):
        """
        An opcode which does nothing (not even consume gas)
        """
        pass
    
    def burn_5_gas(computation):
        """
        An opcode which simply burns 5 gas
        """
        computation.consume_gas(5, reason='why not?')
  7. Understand the Py-EVM architectural abstractions

    main

    Py-EVM is designed for configurability and extensibility, allowing it to support the public Ethereum blockchain or alternate use cases like private or consortium chains. The architecture is built on several key abstractions that represent the consensus rules and execution logic:

    • Chain: The high-level orchestration layer for interacting with the blockchain.
    • VM (Virtual Machine): Encapsulates the state transition function for a specific fork ruleset and handles transaction execution orchestration.
    • VMState: Represents the execution context (e.g., coinbase, gas_limit) and the state root.
    • Message: The VM's internal representation of a transaction, containing parameters like sender, value, and to.
    • Computation: Encapsulates the computational state (memory, stack, gas metering) and the results (return data, gas consumption, errors).
    • Opcode: The logic for a single instruction (e.g., ADD, MUL).
  8. Understand the Proof-of-Work (PoW) mining process in Py-EVM

    main

    In Py-EVM, the MiningChain.mine_block API is used to create, validate, and set a block as the new canonical head of the chain. While modern Ethereum uses Proof-of-Stake (PoS), this guide explains the legacy PoW mechanism for educational purposes.

    To successfully mine a block using PoW, the block header must contain a valid nonce and mix_hash that satisfy the PoW algorithm. If these are missing or incorrect, chain.mine_block() will raise an eth.exceptions.ValidationError: mix hash mismatch because the block fails the eth.consensus.pow.check_pow validation.

  9. How the Chain and VM abstractions relate

    main

    The Chain object acts as an interface and orchestration layer that wraps various blockchain components. It manages:

    • Protocol rules (block rewards, difficulty, etc.)
    • Chain data (Headers, Blocks, Transactions, Receipts)
    • State data (balance, nonce, code, storage)
    • Chain state (tracking the head and canonical blocks)

    A Chain can contain one or more underlying Virtual Machines (VMs). The Chain maintains a mapping to determine which VM is active for specific blocks. For example, a mainnet chain would map different VMs to different forks (e.g., Frontier, Homestead, Byzantium).

    While the Chain provides high-level APIs, many of these are passthroughs to the appropriate VM. The VM itself is responsible for:

    • The state transition function for a single fork ruleset.
    • Orchestration of transaction execution.
    • Block construction and validation.
    • APIs for chain data storage and retrieval.
  10. Implement opcodes as classes for reusable logic

    main

    When you need to share common logic between similar opcodes or across different fork rules, you can implement an opcode as a class. To do this, implement a __call__ method that accepts a single eth.vm.computation.Computation instance as its sole argument. This pattern allows you to structure an opcode into stages (e.g., initial, main, and cleanup) and override specific sections while reusing the overall structure.

    class MyOpcode:
        def initial_logic(self, computation):
            ...
    
        def main_logic(self, computation):
            ...
    
        def cleanup_logic(self, computation):
            ...
    
        def __call__(self, computation):
            self.initial_logic(computation)
            self.main_logic(computation)
            self.cleanup_logic(computation)