From decd4e5367052b51c7dc3451fc99e6fb0977761f Mon Sep 17 00:00:00 2001 From: Peter Davies Date: Sat, 5 Aug 2023 01:03:59 +0100 Subject: [PATCH] Unify optimized implementations (#811) * Unify optimized implementations * Update spec tools * Use a decorator to explicitly declare which items are to be patched * Use `Hardfork.discover()` to get forks * Use `Hardfork.consensus` to test consensus type --- setup.cfg | 15 - src/ethereum_optimized/__init__.py | 88 +++-- .../arrow_glacier/__init__.py | 72 ---- src/ethereum_optimized/arrow_glacier/fork.py | 47 --- .../arrow_glacier/state_db.py | 315 ---------------- src/ethereum_optimized/berlin/__init__.py | 72 ---- src/ethereum_optimized/berlin/fork.py | 47 --- src/ethereum_optimized/berlin/state_db.py | 315 ---------------- src/ethereum_optimized/byzantium/__init__.py | 72 ---- src/ethereum_optimized/byzantium/fork.py | 47 --- src/ethereum_optimized/byzantium/state_db.py | 315 ---------------- .../constantinople/__init__.py | 72 ---- src/ethereum_optimized/constantinople/fork.py | 47 --- .../constantinople/state_db.py | 315 ---------------- src/ethereum_optimized/dao_fork/__init__.py | 71 ---- src/ethereum_optimized/dao_fork/fork.py | 47 --- src/ethereum_optimized/dao_fork/state_db.py | 294 --------------- src/ethereum_optimized/fork.py | 71 ++++ src/ethereum_optimized/frontier/__init__.py | 72 ---- src/ethereum_optimized/frontier/fork.py | 47 --- src/ethereum_optimized/frontier/state_db.py | 315 ---------------- .../gray_glacier/__init__.py | 72 ---- src/ethereum_optimized/gray_glacier/fork.py | 47 --- .../gray_glacier/state_db.py | 315 ---------------- src/ethereum_optimized/homestead/__init__.py | 72 ---- src/ethereum_optimized/homestead/fork.py | 47 --- src/ethereum_optimized/homestead/state_db.py | 315 ---------------- src/ethereum_optimized/istanbul/__init__.py | 72 ---- src/ethereum_optimized/istanbul/fork.py | 47 --- src/ethereum_optimized/istanbul/state_db.py | 315 ---------------- src/ethereum_optimized/london/__init__.py | 72 ---- src/ethereum_optimized/london/fork.py | 47 --- src/ethereum_optimized/london/state_db.py | 315 ---------------- .../muir_glacier/__init__.py | 72 ---- src/ethereum_optimized/muir_glacier/fork.py | 47 --- .../muir_glacier/state_db.py | 315 ---------------- src/ethereum_optimized/paris/__init__.py | 56 --- src/ethereum_optimized/paris/state_db.py | 315 ---------------- src/ethereum_optimized/shanghai/__init__.py | 56 --- src/ethereum_optimized/shanghai/state_db.py | 315 ---------------- .../spurious_dragon/__init__.py | 72 ---- .../spurious_dragon/fork.py | 47 --- .../spurious_dragon/state_db.py | 315 ---------------- src/ethereum_optimized/state_db.py | 346 ++++++++++++++++++ .../tangerine_whistle/__init__.py | 72 ---- .../tangerine_whistle/fork.py | 47 --- .../tangerine_whistle/state_db.py | 315 ---------------- src/ethereum_optimized/utils.py | 27 ++ src/ethereum_spec_tools/new_fork.py | 21 +- src/ethereum_spec_tools/patch_tool.py | 8 - tests/berlin/optimized/__init__.py | 0 tests/berlin/optimized/test_state_db.py | 66 ---- tests/byzantium/optimized/__init__.py | 0 tests/byzantium/optimized/test_state_db.py | 66 ---- tests/constantinople/optimized/__init__.py | 0 .../constantinople/optimized/test_state_db.py | 66 ---- tests/frontier/optimized/__init__.py | 0 tests/homestead/optimized/__init__.py | 0 tests/homestead/optimized/test_state_db.py | 66 ---- tests/istanbul/optimized/__init__.py | 0 tests/istanbul/optimized/test_state_db.py | 66 ---- tests/london/optimized/__init__.py | 0 tests/london/optimized/test_state_db.py | 66 ---- tests/paris/optimized/__init__.py | 0 tests/paris/optimized/test_state_db.py | 66 ---- tests/shanghai/optimized/__init__.py | 0 tests/shanghai/optimized/test_state_db.py | 66 ---- tests/spurious_dragon/optimized/__init__.py | 0 .../optimized/test_state_db.py | 66 ---- tests/tangerine_whistle/optimized/__init__.py | 0 .../optimized/test_state_db.py | 66 ---- ...st_state_db.py => test_optimized_state.py} | 35 +- 72 files changed, 521 insertions(+), 7112 deletions(-) delete mode 100644 src/ethereum_optimized/arrow_glacier/__init__.py delete mode 100644 src/ethereum_optimized/arrow_glacier/fork.py delete mode 100644 src/ethereum_optimized/arrow_glacier/state_db.py delete mode 100644 src/ethereum_optimized/berlin/__init__.py delete mode 100644 src/ethereum_optimized/berlin/fork.py delete mode 100644 src/ethereum_optimized/berlin/state_db.py delete mode 100644 src/ethereum_optimized/byzantium/__init__.py delete mode 100644 src/ethereum_optimized/byzantium/fork.py delete mode 100644 src/ethereum_optimized/byzantium/state_db.py delete mode 100644 src/ethereum_optimized/constantinople/__init__.py delete mode 100644 src/ethereum_optimized/constantinople/fork.py delete mode 100644 src/ethereum_optimized/constantinople/state_db.py delete mode 100644 src/ethereum_optimized/dao_fork/__init__.py delete mode 100644 src/ethereum_optimized/dao_fork/fork.py delete mode 100644 src/ethereum_optimized/dao_fork/state_db.py create mode 100644 src/ethereum_optimized/fork.py delete mode 100644 src/ethereum_optimized/frontier/__init__.py delete mode 100644 src/ethereum_optimized/frontier/fork.py delete mode 100644 src/ethereum_optimized/frontier/state_db.py delete mode 100644 src/ethereum_optimized/gray_glacier/__init__.py delete mode 100644 src/ethereum_optimized/gray_glacier/fork.py delete mode 100644 src/ethereum_optimized/gray_glacier/state_db.py delete mode 100644 src/ethereum_optimized/homestead/__init__.py delete mode 100644 src/ethereum_optimized/homestead/fork.py delete mode 100644 src/ethereum_optimized/homestead/state_db.py delete mode 100644 src/ethereum_optimized/istanbul/__init__.py delete mode 100644 src/ethereum_optimized/istanbul/fork.py delete mode 100644 src/ethereum_optimized/istanbul/state_db.py delete mode 100644 src/ethereum_optimized/london/__init__.py delete mode 100644 src/ethereum_optimized/london/fork.py delete mode 100644 src/ethereum_optimized/london/state_db.py delete mode 100644 src/ethereum_optimized/muir_glacier/__init__.py delete mode 100644 src/ethereum_optimized/muir_glacier/fork.py delete mode 100644 src/ethereum_optimized/muir_glacier/state_db.py delete mode 100644 src/ethereum_optimized/paris/__init__.py delete mode 100644 src/ethereum_optimized/paris/state_db.py delete mode 100644 src/ethereum_optimized/shanghai/__init__.py delete mode 100644 src/ethereum_optimized/shanghai/state_db.py delete mode 100644 src/ethereum_optimized/spurious_dragon/__init__.py delete mode 100644 src/ethereum_optimized/spurious_dragon/fork.py delete mode 100644 src/ethereum_optimized/spurious_dragon/state_db.py create mode 100644 src/ethereum_optimized/state_db.py delete mode 100644 src/ethereum_optimized/tangerine_whistle/__init__.py delete mode 100644 src/ethereum_optimized/tangerine_whistle/fork.py delete mode 100644 src/ethereum_optimized/tangerine_whistle/state_db.py create mode 100644 src/ethereum_optimized/utils.py delete mode 100644 tests/berlin/optimized/__init__.py delete mode 100644 tests/berlin/optimized/test_state_db.py delete mode 100644 tests/byzantium/optimized/__init__.py delete mode 100644 tests/byzantium/optimized/test_state_db.py delete mode 100644 tests/constantinople/optimized/__init__.py delete mode 100644 tests/constantinople/optimized/test_state_db.py delete mode 100644 tests/frontier/optimized/__init__.py delete mode 100644 tests/homestead/optimized/__init__.py delete mode 100644 tests/homestead/optimized/test_state_db.py delete mode 100644 tests/istanbul/optimized/__init__.py delete mode 100644 tests/istanbul/optimized/test_state_db.py delete mode 100644 tests/london/optimized/__init__.py delete mode 100644 tests/london/optimized/test_state_db.py delete mode 100644 tests/paris/optimized/__init__.py delete mode 100644 tests/paris/optimized/test_state_db.py delete mode 100644 tests/shanghai/optimized/__init__.py delete mode 100644 tests/shanghai/optimized/test_state_db.py delete mode 100644 tests/spurious_dragon/optimized/__init__.py delete mode 100644 tests/spurious_dragon/optimized/test_state_db.py delete mode 100644 tests/tangerine_whistle/optimized/__init__.py delete mode 100644 tests/tangerine_whistle/optimized/test_state_db.py rename tests/{frontier/optimized/test_state_db.py => test_optimized_state.py} (60%) diff --git a/setup.cfg b/setup.cfg index 9d48a9ebb6..aee6caa3ed 100644 --- a/setup.cfg +++ b/setup.cfg @@ -27,91 +27,76 @@ packages = ethereum/utils ethereum/crypto ethereum_optimized/ - ethereum_optimized/frontier ethereum/homestead ethereum/homestead/utils ethereum/homestead/vm ethereum/homestead/vm/instructions ethereum/homestead/vm/precompiled_contracts - ethereum_optimized/homestead ethereum/dao_fork ethereum/dao_fork/utils ethereum/dao_fork/vm ethereum/dao_fork/vm/instructions ethereum/dao_fork/vm/precompiled_contracts - ethereum_optimized/dao_fork ethereum/tangerine_whistle ethereum/tangerine_whistle/utils ethereum/tangerine_whistle/vm ethereum/tangerine_whistle/vm/instructions ethereum/tangerine_whistle/vm/precompiled_contracts - ethereum_optimized/tangerine_whistle ethereum/spurious_dragon ethereum/spurious_dragon/utils ethereum/spurious_dragon/vm ethereum/spurious_dragon/vm/instructions ethereum/spurious_dragon/vm/precompiled_contracts - ethereum_optimized/spurious_dragon ethereum/byzantium ethereum/byzantium/utils ethereum/byzantium/vm ethereum/byzantium/vm/instructions ethereum/byzantium/vm/precompiled_contracts - ethereum_optimized/byzantium ethereum/constantinople ethereum/constantinople/utils ethereum/constantinople/vm ethereum/constantinople/vm/instructions ethereum/constantinople/vm/precompiled_contracts - ethereum_optimized/constantinople ethereum/istanbul ethereum/istanbul/utils ethereum/istanbul/vm ethereum/istanbul/vm/instructions ethereum/istanbul/vm/precompiled_contracts - ethereum_optimized/istanbul ethereum/muir_glacier ethereum/muir_glacier/utils ethereum/muir_glacier/vm ethereum/muir_glacier/vm/instructions ethereum/muir_glacier/vm/precompiled_contracts - ethereum_optimized/muir_glacier ethereum/berlin ethereum/berlin/utils ethereum/berlin/vm ethereum/berlin/vm/instructions ethereum/berlin/vm/precompiled_contracts - ethereum_optimized/berlin ethereum/london ethereum/london/utils ethereum/london/vm ethereum/london/vm/instructions ethereum/london/vm/precompiled_contracts - ethereum_optimized/london ethereum/arrow_glacier ethereum/arrow_glacier/utils ethereum/arrow_glacier/vm ethereum/arrow_glacier/vm/instructions ethereum/arrow_glacier/vm/precompiled_contracts - ethereum_optimized/arrow_glacier ethereum/gray_glacier ethereum/gray_glacier/utils ethereum/gray_glacier/vm ethereum/gray_glacier/vm/instructions ethereum/gray_glacier/vm/precompiled_contracts - ethereum_optimized/gray_glacier ethereum/paris ethereum/paris/utils ethereum/paris/vm ethereum/paris/vm/instructions ethereum/paris/vm/precompiled_contracts - ethereum_optimized/paris ethereum/shanghai ethereum/shanghai/utils ethereum/shanghai/vm ethereum/shanghai/vm/instructions ethereum/shanghai/vm/precompiled_contracts - ethereum_optimized/shanghai package_dir = diff --git a/src/ethereum_optimized/__init__.py b/src/ethereum_optimized/__init__.py index 0de5b723b8..f93f339c07 100644 --- a/src/ethereum_optimized/__init__.py +++ b/src/ethereum_optimized/__init__.py @@ -13,46 +13,64 @@ have been optimized for speed rather than clarity. They can be monkey patched in during start up by calling the `monkey_patch()` -function. +function. This must be done before those modules are imported anywhere. """ -from typing import Optional +from importlib import import_module +from typing import Any, Optional, cast + +from ethereum_spec_tools.forks import Hardfork + +from .fork import get_optimized_pow_patches +from .state_db import get_optimized_state_patches + + +def monkey_patch_optimized_state_db( + fork_name: str, state_path: Optional[str] +) -> None: + """ + Replace the state interface with one that supports high performance + updates and storing state in a database. + + This function must be called before the state interface is imported + anywhere. + """ + slow_state = cast(Any, import_module("ethereum." + fork_name + ".state")) + + optimized_state_db_patches = get_optimized_state_patches(fork_name) + + for (name, value) in optimized_state_db_patches.items(): + setattr(slow_state, name, value) + + if state_path is not None: + slow_state.State.default_path = state_path + + +def monkey_patch_optimized_spec(fork_name: str) -> None: + """ + Replace the ethash implementation with one that supports higher + performance. + + This function must be called before the spec interface is imported + anywhere. + """ + slow_spec = import_module("ethereum." + fork_name + ".fork") + + optimized_pow_patches = get_optimized_pow_patches(fork_name) + + for (name, value) in optimized_pow_patches.items(): + setattr(slow_spec, name, value) def monkey_patch(state_path: Optional[str]) -> None: """ Apply all monkey patches to the specification. """ - from . import ( - arrow_glacier, - berlin, - byzantium, - constantinople, - dao_fork, - frontier, - gray_glacier, - homestead, - istanbul, - london, - muir_glacier, - paris, - shanghai, - spurious_dragon, - tangerine_whistle, - ) - - frontier.monkey_patch(state_path) - homestead.monkey_patch(state_path) - dao_fork.monkey_patch(state_path) - tangerine_whistle.monkey_patch(state_path) - spurious_dragon.monkey_patch(state_path) - byzantium.monkey_patch(state_path) - constantinople.monkey_patch(state_path) - istanbul.monkey_patch(state_path) - muir_glacier.monkey_patch(state_path) - berlin.monkey_patch(state_path) - london.monkey_patch(state_path) - arrow_glacier.monkey_patch(state_path) - gray_glacier.monkey_patch(state_path) - paris.monkey_patch(state_path) - shanghai.monkey_patch(state_path) + forks = Hardfork.discover() + + for fork in forks: + monkey_patch_optimized_state_db(fork.short_name, state_path) + + # Only patch the POW code on POW forks + if fork.consensus.is_pow(): + monkey_patch_optimized_spec(fork.short_name) diff --git a/src/ethereum_optimized/arrow_glacier/__init__.py b/src/ethereum_optimized/arrow_glacier/__init__.py deleted file mode 100644 index 840d1cac99..0000000000 --- a/src/ethereum_optimized/arrow_glacier/__init__.py +++ /dev/null @@ -1,72 +0,0 @@ -""" -Optimized Implementations (Arrow Glacier) -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -.. contents:: Table of Contents - :backlinks: none - :local: -""" - -from typing import Optional - - -def monkey_patch_optimized_state_db(state_path: Optional[str]) -> None: - """ - Replace the state interface with one that supports high performance - updates and storing state in a database. - - This function must be called before the state interface is imported - anywhere. - """ - import ethereum.arrow_glacier.state as slow_state - - from . import state_db as fast_state - - optimized_state_db_patches = { - "State": fast_state.State, - "get_account_optional": fast_state.get_account_optional, - "set_account": fast_state.set_account, - "destroy_storage": fast_state.destroy_storage, - "mark_account_created": fast_state.mark_account_created, - "get_storage": fast_state.get_storage, - "get_storage_original": fast_state.get_storage_original, - "set_storage": fast_state.set_storage, - "state_root": fast_state.state_root, - "storage_root": fast_state.storage_root, - "begin_transaction": fast_state.begin_transaction, - "rollback_transaction": fast_state.rollback_transaction, - "commit_transaction": fast_state.commit_transaction, - "close_state": fast_state.close_state, - } - - for (name, value) in optimized_state_db_patches.items(): - setattr(slow_state, name, value) - - if state_path is not None: - fast_state.State.default_path = state_path - - -def monkey_patch_optimized_spec() -> None: - """ - Replace the ethash implementation with one that supports higher - performance. - - This function must be called before the spec interface is imported - anywhere. - """ - import ethereum.arrow_glacier.fork as slow_spec - - from . import fork as fast_spec - - slow_spec.validate_proof_of_work = fast_spec.validate_proof_of_work - - -def monkey_patch(state_path: Optional[str]) -> None: - """ - Apply all monkey patches to swap in high performance implementations. - - This function must be called before any of the ethereum modules are - imported anywhere. - """ - monkey_patch_optimized_state_db(state_path) - monkey_patch_optimized_spec() diff --git a/src/ethereum_optimized/arrow_glacier/fork.py b/src/ethereum_optimized/arrow_glacier/fork.py deleted file mode 100644 index 30205846e5..0000000000 --- a/src/ethereum_optimized/arrow_glacier/fork.py +++ /dev/null @@ -1,47 +0,0 @@ -""" -Optimized Spec -^^^^^^^^^^^^^^ - -.. contents:: Table of Contents - :backlinks: none - :local: - -Introduction ------------- - -This module contains functions can be monkey patched into -`ethereum.arrow_glacier.fork` to use alternate optimized implementations. -""" -from ethereum.arrow_glacier.fork import generate_header_hash_for_pow -from ethereum.arrow_glacier.fork_types import Header -from ethereum.base_types import U256_CEIL_VALUE -from ethereum.ethash import epoch -from ethereum.exceptions import InvalidBlock -from ethereum.utils.ensure import ensure - -try: - import ethash -except ImportError as e: - # Add a message, but keep it an ImportError. - raise e from Exception( - "Install with `pip install 'ethereum[optimized]'` to enable this " - "package" - ) - - -def validate_proof_of_work(header: Header) -> None: - """ - See `ethereum.arrow_glacier.fork.validate_proof_of_work`. - """ - epoch_number = epoch(header.number) - header_hash = generate_header_hash_for_pow(header) - - result = ethash.verify( - int(epoch_number), - header_hash, - header.mix_digest, - int.from_bytes(header.nonce, "big"), - (U256_CEIL_VALUE // header.difficulty).to_be_bytes32(), - ) - - ensure(result, InvalidBlock) diff --git a/src/ethereum_optimized/arrow_glacier/state_db.py b/src/ethereum_optimized/arrow_glacier/state_db.py deleted file mode 100644 index 1dd1869e82..0000000000 --- a/src/ethereum_optimized/arrow_glacier/state_db.py +++ /dev/null @@ -1,315 +0,0 @@ -""" -Optimized State -^^^^^^^^^^^^^^^ - -.. contents:: Table of Contents - :backlinks: none - :local: - -Introduction ------------- - -This module contains functions can be monkey patched into -`ethereum.arrow_glacier.state` to use an optimized database backed state. -""" -import logging -from dataclasses import dataclass -from typing import Any, ClassVar, Dict, List, Optional, Set - -try: - import rust_pyspec_glue -except ImportError as e: - # Add a message, but keep it an ImportError. - raise e from Exception( - "Install with `pip install 'ethereum[optimized]'` to enable this " - "package" - ) - -from ethereum.arrow_glacier.fork_types import Account, Address, Root -from ethereum.base_types import U256, Bytes, Uint - - -class UnmodifiedType: - """ - Sentinal type to represent a value that hasn't been modified. - """ - - pass - - -Unmodified = UnmodifiedType() - - -@dataclass -class State: - """ - The State, backed by a LMDB database. - - When created with `State()` store the db in a temporary directory. When - created with `State(path)` open or create the db located at `path`. - """ - - default_path: ClassVar[Optional[str]] = None - - db: Any - dirty_accounts: Dict[Address, Optional[Account]] - dirty_storage: Dict[Address, Dict[Bytes, U256]] - destroyed_accounts: Set[Address] - tx_restore_points: List[int] - journal: List[Any] - created_accounts: Set[Address] - - def __init__(self, path: Optional[str] = None) -> None: - logging.info("using optimized state db at %s", path) - - if path is None: - path = State.default_path - - self.db = rust_pyspec_glue.DB(path) - self.dirty_accounts = {} - self.dirty_storage = {} - self.destroyed_accounts = set() - self.tx_restore_points = [] - self.journal = [] - self.created_accounts = set() - self.db.begin_mutable() - - def __eq__(self, other: object) -> bool: - """ - Test for equality by comparing state roots. - """ - if not isinstance(other, State): - return NotImplemented - return state_root(self) == state_root(other) - - def __enter__(self) -> "State": - """Support with statements""" - return self - - def __exit__(self, *args: Any) -> None: - """Support with statements""" - close_state(self) - - -def close_state(state: State) -> None: - """Close a state, releasing all resources it holds""" - state.db.close() - state.db = None - del state.dirty_accounts - del state.dirty_storage - del state.destroyed_accounts - del state.journal - del state.created_accounts - - -def get_metadata(state: State, key: Bytes) -> Optional[Bytes]: - """Get a piece of metadata""" - return state.db.get_metadata(key) - - -def set_metadata(state: State, key: Bytes, value: Bytes) -> None: - """Set a piece of metadata""" - return state.db.set_metadata(key, value) - - -def begin_db_transaction(state: State) -> None: - """ - Start a database transaction. A transaction is automatically started when a - `State` is created. Nesting of DB transactions is not supported (unlike - non-db transactions). - - No operations are supported when not in a transaction. - """ - state.db.begin_mutable() - state.tx_restore_points = [] - state.journal = [] - - -def commit_db_transaction(state: State) -> None: - """ - Commit the current database transaction. - """ - if state.tx_restore_points: - raise Exception("In a non-db transaction") - flush(state) - state.db.commit_mutable() - - -def state_root(state: State) -> Root: - """ - See `ethereum.arrow_glacier.state`. - """ - if state.tx_restore_points: - raise Exception("In a non-db transaction") - flush(state) - return state.db.state_root() - - -def storage_root(state: State, address: Address) -> Root: - """ - See `ethereum.arrow_glacier.state`. - """ - if state.tx_restore_points: - raise Exception("In a non-db transaction") - flush(state) - return state.db.storage_root(address) - - -def flush(state: State) -> None: - """ - Send everything in the internal caches to the Rust layer. - """ - if state.tx_restore_points: - raise Exception("In a non-db transaction") - for address in state.destroyed_accounts: - state.db.destroy_storage(address) - for address, account in state.dirty_accounts.items(): - state.db.set_account(address, account) - for address, storage in state.dirty_storage.items(): - for key, value in storage.items(): - state.db.set_storage(address, key, value) - state.destroyed_accounts = set() - state.dirty_accounts.clear() - state.dirty_storage.clear() - - -def rollback_db_transaction(state: State) -> None: - """ - Rollback the current database transaction. - """ - if state.tx_restore_points: - raise Exception("In a non-db transaction") - state.db.rollback_mutable() - state.dirty_accounts.clear() - state.dirty_storage.clear() - state.destroyed_accounts = set() - - -def begin_transaction(state: State) -> None: - """ - See `ethereum.arrow_glacier.state`. - """ - if not state.tx_restore_points: - flush(state) - state.tx_restore_points.append(len(state.journal)) - - -def commit_transaction(state: State) -> None: - """ - See `ethereum.arrow_glacier.state`. - """ - state.tx_restore_points.pop() - if not state.tx_restore_points: - state.journal.clear() - state.created_accounts.clear() - flush(state) - - -def rollback_transaction(state: State) -> None: - """ - See `ethereum.arrow_glacier.state`. - """ - restore_point = state.tx_restore_points.pop() - while len(state.journal) > restore_point: - item = state.journal.pop() - if len(item) == 3: - # Revert a storage key write - if item[2] is Unmodified: - del state.dirty_storage[item[0]][item[1]] - else: - state.dirty_storage[item[0]][item[1]] = item[2] - elif type(item[1]) is dict: - # Restore storage that was destroyed by `destroy_storage()` - state.destroyed_accounts.remove(item[0]) - state.dirty_storage[item[0]] = item[1] - else: - # Revert a change to an account - if item[1] is Unmodified: - del state.dirty_accounts[item[0]] - else: - state.dirty_accounts[item[0]] = item[1] - - if not state.tx_restore_points: - state.created_accounts.clear() - - -def get_storage(state: State, address: Address, key: Bytes) -> U256: - """ - See `ethereum.arrow_glacier.state`. - """ - if address in state.dirty_storage and key in state.dirty_storage[address]: - return state.dirty_storage[address][key] - - if address in state.destroyed_accounts: - return U256(0) - else: - return U256(state.db.get_storage(address, key)) - - -def get_storage_original(state: State, address: Address, key: Bytes) -> U256: - """ - See `ethereum.istanbul.state`. - """ - if address in state.created_accounts: - return U256(0) - else: - return U256(state.db.get_storage(address, key)) - - -def set_storage( - state: State, address: Address, key: Bytes, value: U256 -) -> None: - """ - See `ethereum.arrow_glacier.state`. - """ - if address not in state.dirty_accounts: - state.dirty_accounts[address] = get_account_optional(state, address) - if address not in state.dirty_storage: - state.dirty_storage[address] = {} - if key not in state.dirty_storage[address]: - state.journal.append((address, key, Unmodified)) - else: - state.journal.append((address, key, state.dirty_storage[address][key])) - state.dirty_storage[address][key] = value - - -def get_account_optional(state: State, address: Address) -> Optional[Account]: - """ - See `ethereum.arrow_glacier.state`. - """ - if address in state.dirty_accounts: - return state.dirty_accounts[address] - account = state.db.get_account_optional(address) - if account is not None: - return Account(Uint(account[0]), U256(account[1]), account[2]) - else: - return None - - -def set_account( - state: State, address: Address, account: Optional[Account] -) -> None: - """ - See `ethereum.arrow_glacier.state`. - """ - if address not in state.dirty_accounts: - state.journal.append((address, Unmodified)) - if address in state.dirty_accounts: - state.journal.append((address, state.dirty_accounts[address])) - state.dirty_accounts[address] = account - - -def destroy_storage(state: State, address: Address) -> None: - """ - See `ethereum.arrow_glacier.state`. - """ - state.journal.append((address, state.dirty_storage.pop(address, {}))) - state.destroyed_accounts.add(address) - set_account(state, address, get_account_optional(state, address)) - - -def mark_account_created(state: State, address: Address) -> None: - """ - See `ethereum.frontier.state`. - """ - state.created_accounts.add(address) diff --git a/src/ethereum_optimized/berlin/__init__.py b/src/ethereum_optimized/berlin/__init__.py deleted file mode 100644 index 54cefd1963..0000000000 --- a/src/ethereum_optimized/berlin/__init__.py +++ /dev/null @@ -1,72 +0,0 @@ -""" -Optimized Implementations (Berlin) -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -.. contents:: Table of Contents - :backlinks: none - :local: -""" - -from typing import Optional - - -def monkey_patch_optimized_state_db(state_path: Optional[str]) -> None: - """ - Replace the state interface with one that supports high performance - updates and storing state in a database. - - This function must be called before the state interface is imported - anywhere. - """ - import ethereum.berlin.state as slow_state - - from . import state_db as fast_state - - optimized_state_db_patches = { - "State": fast_state.State, - "get_account_optional": fast_state.get_account_optional, - "set_account": fast_state.set_account, - "destroy_storage": fast_state.destroy_storage, - "mark_account_created": fast_state.mark_account_created, - "get_storage": fast_state.get_storage, - "get_storage_original": fast_state.get_storage_original, - "set_storage": fast_state.set_storage, - "state_root": fast_state.state_root, - "storage_root": fast_state.storage_root, - "begin_transaction": fast_state.begin_transaction, - "rollback_transaction": fast_state.rollback_transaction, - "commit_transaction": fast_state.commit_transaction, - "close_state": fast_state.close_state, - } - - for (name, value) in optimized_state_db_patches.items(): - setattr(slow_state, name, value) - - if state_path is not None: - fast_state.State.default_path = state_path - - -def monkey_patch_optimized_spec() -> None: - """ - Replace the ethash implementation with one that supports higher - performance. - - This function must be called before the spec interface is imported - anywhere. - """ - import ethereum.berlin.fork as slow_spec - - from . import fork as fast_spec - - slow_spec.validate_proof_of_work = fast_spec.validate_proof_of_work - - -def monkey_patch(state_path: Optional[str]) -> None: - """ - Apply all monkey patches to swap in high performance implementations. - - This function must be called before any of the ethereum modules are - imported anywhere. - """ - monkey_patch_optimized_state_db(state_path) - monkey_patch_optimized_spec() diff --git a/src/ethereum_optimized/berlin/fork.py b/src/ethereum_optimized/berlin/fork.py deleted file mode 100644 index 34c1131687..0000000000 --- a/src/ethereum_optimized/berlin/fork.py +++ /dev/null @@ -1,47 +0,0 @@ -""" -Optimized Spec -^^^^^^^^^^^^^^ - -.. contents:: Table of Contents - :backlinks: none - :local: - -Introduction ------------- - -This module contains functions can be monkey patched into -`ethereum.berlin.fork` to use alternate optimized implementations. -""" -from ethereum.base_types import U256_CEIL_VALUE -from ethereum.berlin.fork import generate_header_hash_for_pow -from ethereum.berlin.fork_types import Header -from ethereum.ethash import epoch -from ethereum.exceptions import InvalidBlock -from ethereum.utils.ensure import ensure - -try: - import ethash -except ImportError as e: - # Add a message, but keep it an ImportError. - raise e from Exception( - "Install with `pip install 'ethereum[optimized]'` to enable this " - "package" - ) - - -def validate_proof_of_work(header: Header) -> None: - """ - See `ethereum.berlin.fork.validate_proof_of_work`. - """ - epoch_number = epoch(header.number) - header_hash = generate_header_hash_for_pow(header) - - result = ethash.verify( - int(epoch_number), - header_hash, - header.mix_digest, - int.from_bytes(header.nonce, "big"), - (U256_CEIL_VALUE // header.difficulty).to_be_bytes32(), - ) - - ensure(result, InvalidBlock) diff --git a/src/ethereum_optimized/berlin/state_db.py b/src/ethereum_optimized/berlin/state_db.py deleted file mode 100644 index 2efe3b2ff1..0000000000 --- a/src/ethereum_optimized/berlin/state_db.py +++ /dev/null @@ -1,315 +0,0 @@ -""" -Optimized State -^^^^^^^^^^^^^^^ - -.. contents:: Table of Contents - :backlinks: none - :local: - -Introduction ------------- - -This module contains functions can be monkey patched into -`ethereum.berlin.state` to use an optimized database backed state. -""" -import logging -from dataclasses import dataclass -from typing import Any, ClassVar, Dict, List, Optional, Set - -try: - import rust_pyspec_glue -except ImportError as e: - # Add a message, but keep it an ImportError. - raise e from Exception( - "Install with `pip install 'ethereum[optimized]'` to enable this " - "package" - ) - -from ethereum.base_types import U256, Bytes, Uint -from ethereum.berlin.fork_types import Account, Address, Root - - -class UnmodifiedType: - """ - Sentinal type to represent a value that hasn't been modified. - """ - - pass - - -Unmodified = UnmodifiedType() - - -@dataclass -class State: - """ - The State, backed by a LMDB database. - - When created with `State()` store the db in a temporary directory. When - created with `State(path)` open or create the db located at `path`. - """ - - default_path: ClassVar[Optional[str]] = None - - db: Any - dirty_accounts: Dict[Address, Optional[Account]] - dirty_storage: Dict[Address, Dict[Bytes, U256]] - destroyed_accounts: Set[Address] - tx_restore_points: List[int] - journal: List[Any] - created_accounts: Set[Address] - - def __init__(self, path: Optional[str] = None) -> None: - logging.info("using optimized state db at %s", path) - - if path is None: - path = State.default_path - - self.db = rust_pyspec_glue.DB(path) - self.dirty_accounts = {} - self.dirty_storage = {} - self.destroyed_accounts = set() - self.tx_restore_points = [] - self.journal = [] - self.created_accounts = set() - self.db.begin_mutable() - - def __eq__(self, other: object) -> bool: - """ - Test for equality by comparing state roots. - """ - if not isinstance(other, State): - return NotImplemented - return state_root(self) == state_root(other) - - def __enter__(self) -> "State": - """Support with statements""" - return self - - def __exit__(self, *args: Any) -> None: - """Support with statements""" - close_state(self) - - -def close_state(state: State) -> None: - """Close a state, releasing all resources it holds""" - state.db.close() - state.db = None - del state.dirty_accounts - del state.dirty_storage - del state.destroyed_accounts - del state.journal - del state.created_accounts - - -def get_metadata(state: State, key: Bytes) -> Optional[Bytes]: - """Get a piece of metadata""" - return state.db.get_metadata(key) - - -def set_metadata(state: State, key: Bytes, value: Bytes) -> None: - """Set a piece of metadata""" - return state.db.set_metadata(key, value) - - -def begin_db_transaction(state: State) -> None: - """ - Start a database transaction. A transaction is automatically started when a - `State` is created. Nesting of DB transactions is not supported (unlike - non-db transactions). - - No operations are supported when not in a transaction. - """ - state.db.begin_mutable() - state.tx_restore_points = [] - state.journal = [] - - -def commit_db_transaction(state: State) -> None: - """ - Commit the current database transaction. - """ - if state.tx_restore_points: - raise Exception("In a non-db transaction") - flush(state) - state.db.commit_mutable() - - -def state_root(state: State) -> Root: - """ - See `ethereum.berlin.state`. - """ - if state.tx_restore_points: - raise Exception("In a non-db transaction") - flush(state) - return state.db.state_root() - - -def storage_root(state: State, address: Address) -> Root: - """ - See `ethereum.berlin.state`. - """ - if state.tx_restore_points: - raise Exception("In a non-db transaction") - flush(state) - return state.db.storage_root(address) - - -def flush(state: State) -> None: - """ - Send everything in the internal caches to the Rust layer. - """ - if state.tx_restore_points: - raise Exception("In a non-db transaction") - for address in state.destroyed_accounts: - state.db.destroy_storage(address) - for address, account in state.dirty_accounts.items(): - state.db.set_account(address, account) - for address, storage in state.dirty_storage.items(): - for key, value in storage.items(): - state.db.set_storage(address, key, value) - state.destroyed_accounts = set() - state.dirty_accounts.clear() - state.dirty_storage.clear() - - -def rollback_db_transaction(state: State) -> None: - """ - Rollback the current database transaction. - """ - if state.tx_restore_points: - raise Exception("In a non-db transaction") - state.db.rollback_mutable() - state.dirty_accounts.clear() - state.dirty_storage.clear() - state.destroyed_accounts = set() - - -def begin_transaction(state: State) -> None: - """ - See `ethereum.berlin.state`. - """ - if not state.tx_restore_points: - flush(state) - state.tx_restore_points.append(len(state.journal)) - - -def commit_transaction(state: State) -> None: - """ - See `ethereum.berlin.state`. - """ - state.tx_restore_points.pop() - if not state.tx_restore_points: - state.journal.clear() - state.created_accounts.clear() - flush(state) - - -def rollback_transaction(state: State) -> None: - """ - See `ethereum.berlin.state`. - """ - restore_point = state.tx_restore_points.pop() - while len(state.journal) > restore_point: - item = state.journal.pop() - if len(item) == 3: - # Revert a storage key write - if item[2] is Unmodified: - del state.dirty_storage[item[0]][item[1]] - else: - state.dirty_storage[item[0]][item[1]] = item[2] - elif type(item[1]) is dict: - # Restore storage that was destroyed by `destroy_storage()` - state.destroyed_accounts.remove(item[0]) - state.dirty_storage[item[0]] = item[1] - else: - # Revert a change to an account - if item[1] is Unmodified: - del state.dirty_accounts[item[0]] - else: - state.dirty_accounts[item[0]] = item[1] - - if not state.tx_restore_points: - state.created_accounts.clear() - - -def get_storage(state: State, address: Address, key: Bytes) -> U256: - """ - See `ethereum.berlin.state`. - """ - if address in state.dirty_storage and key in state.dirty_storage[address]: - return state.dirty_storage[address][key] - - if address in state.destroyed_accounts: - return U256(0) - else: - return U256(state.db.get_storage(address, key)) - - -def get_storage_original(state: State, address: Address, key: Bytes) -> U256: - """ - See `ethereum.istanbul.state`. - """ - if address in state.created_accounts: - return U256(0) - else: - return U256(state.db.get_storage(address, key)) - - -def set_storage( - state: State, address: Address, key: Bytes, value: U256 -) -> None: - """ - See `ethereum.berlin.state`. - """ - if address not in state.dirty_accounts: - state.dirty_accounts[address] = get_account_optional(state, address) - if address not in state.dirty_storage: - state.dirty_storage[address] = {} - if key not in state.dirty_storage[address]: - state.journal.append((address, key, Unmodified)) - else: - state.journal.append((address, key, state.dirty_storage[address][key])) - state.dirty_storage[address][key] = value - - -def get_account_optional(state: State, address: Address) -> Optional[Account]: - """ - See `ethereum.berlin.state`. - """ - if address in state.dirty_accounts: - return state.dirty_accounts[address] - account = state.db.get_account_optional(address) - if account is not None: - return Account(Uint(account[0]), U256(account[1]), account[2]) - else: - return None - - -def set_account( - state: State, address: Address, account: Optional[Account] -) -> None: - """ - See `ethereum.berlin.state`. - """ - if address not in state.dirty_accounts: - state.journal.append((address, Unmodified)) - if address in state.dirty_accounts: - state.journal.append((address, state.dirty_accounts[address])) - state.dirty_accounts[address] = account - - -def destroy_storage(state: State, address: Address) -> None: - """ - See `ethereum.berlin.state`. - """ - state.journal.append((address, state.dirty_storage.pop(address, {}))) - state.destroyed_accounts.add(address) - set_account(state, address, get_account_optional(state, address)) - - -def mark_account_created(state: State, address: Address) -> None: - """ - See `ethereum.frontier.state`. - """ - state.created_accounts.add(address) diff --git a/src/ethereum_optimized/byzantium/__init__.py b/src/ethereum_optimized/byzantium/__init__.py deleted file mode 100644 index fe5c894d21..0000000000 --- a/src/ethereum_optimized/byzantium/__init__.py +++ /dev/null @@ -1,72 +0,0 @@ -""" -Optimized Implementations (Byzantium) -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -.. contents:: Table of Contents - :backlinks: none - :local: -""" - -from typing import Optional - - -def monkey_patch_optimized_state_db(state_path: Optional[str]) -> None: - """ - Replace the state interface with one that supports high performance - updates and storing state in a database. - - This function must be called before the state interface is imported - anywhere. - """ - import ethereum.byzantium.state as slow_state - - from . import state_db as fast_state - - optimized_state_db_patches = { - "State": fast_state.State, - "get_account_optional": fast_state.get_account_optional, - "set_account": fast_state.set_account, - "destroy_storage": fast_state.destroy_storage, - "mark_account_created": fast_state.mark_account_created, - "get_storage": fast_state.get_storage, - "get_storage_original": fast_state.get_storage_original, - "set_storage": fast_state.set_storage, - "state_root": fast_state.state_root, - "storage_root": fast_state.storage_root, - "begin_transaction": fast_state.begin_transaction, - "rollback_transaction": fast_state.rollback_transaction, - "commit_transaction": fast_state.commit_transaction, - "close_state": fast_state.close_state, - } - - for (name, value) in optimized_state_db_patches.items(): - setattr(slow_state, name, value) - - if state_path is not None: - fast_state.State.default_path = state_path - - -def monkey_patch_optimized_spec() -> None: - """ - Replace the ethash implementation with one that supports higher - performance. - - This function must be called before the spec interface is imported - anywhere. - """ - import ethereum.byzantium.fork as slow_spec - - from . import fork as fast_spec - - slow_spec.validate_proof_of_work = fast_spec.validate_proof_of_work - - -def monkey_patch(state_path: Optional[str]) -> None: - """ - Apply all monkey patches to swap in high performance implementations. - - This function must be called before any of the ethereum modules are - imported anywhere. - """ - monkey_patch_optimized_state_db(state_path) - monkey_patch_optimized_spec() diff --git a/src/ethereum_optimized/byzantium/fork.py b/src/ethereum_optimized/byzantium/fork.py deleted file mode 100644 index e3cd574d0d..0000000000 --- a/src/ethereum_optimized/byzantium/fork.py +++ /dev/null @@ -1,47 +0,0 @@ -""" -Optimized Spec -^^^^^^^^^^^^^^ - -.. contents:: Table of Contents - :backlinks: none - :local: - -Introduction ------------- - -This module contains functions can be monkey patched into -`ethereum.byzantium.fork` to use alternate optimized implementations. -""" -from ethereum.base_types import U256_CEIL_VALUE -from ethereum.byzantium.fork import generate_header_hash_for_pow -from ethereum.byzantium.fork_types import Header -from ethereum.ethash import epoch -from ethereum.exceptions import InvalidBlock -from ethereum.utils.ensure import ensure - -try: - import ethash -except ImportError as e: - # Add a message, but keep it an ImportError. - raise e from Exception( - "Install with `pip install 'ethereum[optimized]'` to enable this " - "package" - ) - - -def validate_proof_of_work(header: Header) -> None: - """ - See `ethereum.byzantium.fork.validate_proof_of_work`. - """ - epoch_number = epoch(header.number) - header_hash = generate_header_hash_for_pow(header) - - result = ethash.verify( - int(epoch_number), - header_hash, - header.mix_digest, - int.from_bytes(header.nonce, "big"), - (U256_CEIL_VALUE // header.difficulty).to_be_bytes32(), - ) - - ensure(result, InvalidBlock) diff --git a/src/ethereum_optimized/byzantium/state_db.py b/src/ethereum_optimized/byzantium/state_db.py deleted file mode 100644 index 04d8cc104b..0000000000 --- a/src/ethereum_optimized/byzantium/state_db.py +++ /dev/null @@ -1,315 +0,0 @@ -""" -Optimized State -^^^^^^^^^^^^^^^ - -.. contents:: Table of Contents - :backlinks: none - :local: - -Introduction ------------- - -This module contains functions can be monkey patched into -`ethereum.byzantium.state` to use an optimized database backed state. -""" -import logging -from dataclasses import dataclass -from typing import Any, ClassVar, Dict, List, Optional, Set - -try: - import rust_pyspec_glue -except ImportError as e: - # Add a message, but keep it an ImportError. - raise e from Exception( - "Install with `pip install 'ethereum[optimized]'` to enable this " - "package" - ) - -from ethereum.base_types import U256, Bytes, Uint -from ethereum.byzantium.fork_types import Account, Address, Root - - -class UnmodifiedType: - """ - Sentinal type to represent a value that hasn't been modified. - """ - - pass - - -Unmodified = UnmodifiedType() - - -@dataclass -class State: - """ - The State, backed by a LMDB database. - - When created with `State()` store the db in a temporary directory. When - created with `State(path)` open or create the db located at `path`. - """ - - default_path: ClassVar[Optional[str]] = None - - db: Any - dirty_accounts: Dict[Address, Optional[Account]] - dirty_storage: Dict[Address, Dict[Bytes, U256]] - destroyed_accounts: Set[Address] - tx_restore_points: List[int] - journal: List[Any] - created_accounts: Set[Address] - - def __init__(self, path: Optional[str] = None) -> None: - logging.info("using optimized state db at %s", path) - - if path is None: - path = State.default_path - - self.db = rust_pyspec_glue.DB(path) - self.dirty_accounts = {} - self.dirty_storage = {} - self.destroyed_accounts = set() - self.tx_restore_points = [] - self.journal = [] - self.created_accounts = set() - self.db.begin_mutable() - - def __eq__(self, other: object) -> bool: - """ - Test for equality by comparing state roots. - """ - if not isinstance(other, State): - return NotImplemented - return state_root(self) == state_root(other) - - def __enter__(self) -> "State": - """Support with statements""" - return self - - def __exit__(self, *args: Any) -> None: - """Support with statements""" - close_state(self) - - -def close_state(state: State) -> None: - """Close a state, releasing all resources it holds""" - state.db.close() - state.db = None - del state.dirty_accounts - del state.dirty_storage - del state.destroyed_accounts - del state.journal - del state.created_accounts - - -def get_metadata(state: State, key: Bytes) -> Optional[Bytes]: - """Get a piece of metadata""" - return state.db.get_metadata(key) - - -def set_metadata(state: State, key: Bytes, value: Bytes) -> None: - """Set a piece of metadata""" - return state.db.set_metadata(key, value) - - -def begin_db_transaction(state: State) -> None: - """ - Start a database transaction. A transaction is automatically started when a - `State` is created. Nesting of DB transactions is not supported (unlike - non-db transactions). - - No operations are supported when not in a transaction. - """ - state.db.begin_mutable() - state.tx_restore_points = [] - state.journal = [] - - -def commit_db_transaction(state: State) -> None: - """ - Commit the current database transaction. - """ - if state.tx_restore_points: - raise Exception("In a non-db transaction") - flush(state) - state.db.commit_mutable() - - -def state_root(state: State) -> Root: - """ - See `ethereum.byzantium.state`. - """ - if state.tx_restore_points: - raise Exception("In a non-db transaction") - flush(state) - return state.db.state_root() - - -def storage_root(state: State, address: Address) -> Root: - """ - See `ethereum.byzantium.state`. - """ - if state.tx_restore_points: - raise Exception("In a non-db transaction") - flush(state) - return state.db.storage_root(address) - - -def flush(state: State) -> None: - """ - Send everything in the internal caches to the Rust layer. - """ - if state.tx_restore_points: - raise Exception("In a non-db transaction") - for address in state.destroyed_accounts: - state.db.destroy_storage(address) - for address, account in state.dirty_accounts.items(): - state.db.set_account(address, account) - for address, storage in state.dirty_storage.items(): - for key, value in storage.items(): - state.db.set_storage(address, key, value) - state.destroyed_accounts = set() - state.dirty_accounts.clear() - state.dirty_storage.clear() - - -def rollback_db_transaction(state: State) -> None: - """ - Rollback the current database transaction. - """ - if state.tx_restore_points: - raise Exception("In a non-db transaction") - state.db.rollback_mutable() - state.dirty_accounts.clear() - state.dirty_storage.clear() - state.destroyed_accounts = set() - - -def begin_transaction(state: State) -> None: - """ - See `ethereum.byzantium.state`. - """ - if not state.tx_restore_points: - flush(state) - state.tx_restore_points.append(len(state.journal)) - - -def commit_transaction(state: State) -> None: - """ - See `ethereum.byzantium.state`. - """ - state.tx_restore_points.pop() - if not state.tx_restore_points: - state.journal.clear() - state.created_accounts.clear() - flush(state) - - -def rollback_transaction(state: State) -> None: - """ - See `ethereum.byzantium.state`. - """ - restore_point = state.tx_restore_points.pop() - while len(state.journal) > restore_point: - item = state.journal.pop() - if len(item) == 3: - # Revert a storage key write - if item[2] is Unmodified: - del state.dirty_storage[item[0]][item[1]] - else: - state.dirty_storage[item[0]][item[1]] = item[2] - elif type(item[1]) is dict: - # Restore storage that was destroyed by `destroy_storage()` - state.destroyed_accounts.remove(item[0]) - state.dirty_storage[item[0]] = item[1] - else: - # Revert a change to an account - if item[1] is Unmodified: - del state.dirty_accounts[item[0]] - else: - state.dirty_accounts[item[0]] = item[1] - - if not state.tx_restore_points: - state.created_accounts.clear() - - -def get_storage(state: State, address: Address, key: Bytes) -> U256: - """ - See `ethereum.byzantium.state`. - """ - if address in state.dirty_storage and key in state.dirty_storage[address]: - return state.dirty_storage[address][key] - - if address in state.destroyed_accounts: - return U256(0) - else: - return U256(state.db.get_storage(address, key)) - - -def get_storage_original(state: State, address: Address, key: Bytes) -> U256: - """ - See `ethereum.istanbul.state`. - """ - if address in state.created_accounts: - return U256(0) - else: - return U256(state.db.get_storage(address, key)) - - -def set_storage( - state: State, address: Address, key: Bytes, value: U256 -) -> None: - """ - See `ethereum.byzantium.state`. - """ - if address not in state.dirty_accounts: - state.dirty_accounts[address] = get_account_optional(state, address) - if address not in state.dirty_storage: - state.dirty_storage[address] = {} - if key not in state.dirty_storage[address]: - state.journal.append((address, key, Unmodified)) - else: - state.journal.append((address, key, state.dirty_storage[address][key])) - state.dirty_storage[address][key] = value - - -def get_account_optional(state: State, address: Address) -> Optional[Account]: - """ - See `ethereum.byzantium.state`. - """ - if address in state.dirty_accounts: - return state.dirty_accounts[address] - account = state.db.get_account_optional(address) - if account is not None: - return Account(Uint(account[0]), U256(account[1]), account[2]) - else: - return None - - -def set_account( - state: State, address: Address, account: Optional[Account] -) -> None: - """ - See `ethereum.byzantium.state`. - """ - if address not in state.dirty_accounts: - state.journal.append((address, Unmodified)) - if address in state.dirty_accounts: - state.journal.append((address, state.dirty_accounts[address])) - state.dirty_accounts[address] = account - - -def destroy_storage(state: State, address: Address) -> None: - """ - See `ethereum.byzantium.state`. - """ - state.journal.append((address, state.dirty_storage.pop(address, {}))) - state.destroyed_accounts.add(address) - set_account(state, address, get_account_optional(state, address)) - - -def mark_account_created(state: State, address: Address) -> None: - """ - See `ethereum.frontier.state`. - """ - state.created_accounts.add(address) diff --git a/src/ethereum_optimized/constantinople/__init__.py b/src/ethereum_optimized/constantinople/__init__.py deleted file mode 100644 index 10afbe0d1e..0000000000 --- a/src/ethereum_optimized/constantinople/__init__.py +++ /dev/null @@ -1,72 +0,0 @@ -""" -Optimized Implementations (Constantinople) -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -.. contents:: Table of Contents - :backlinks: none - :local: -""" - -from typing import Optional - - -def monkey_patch_optimized_state_db(state_path: Optional[str]) -> None: - """ - Replace the state interface with one that supports high performance - updates and storing state in a database. - - This function must be called before the state interface is imported - anywhere. - """ - import ethereum.constantinople.state as slow_state - - from . import state_db as fast_state - - optimized_state_db_patches = { - "State": fast_state.State, - "get_account_optional": fast_state.get_account_optional, - "set_account": fast_state.set_account, - "destroy_storage": fast_state.destroy_storage, - "mark_account_created": fast_state.mark_account_created, - "get_storage": fast_state.get_storage, - "get_storage_original": fast_state.get_storage_original, - "set_storage": fast_state.set_storage, - "state_root": fast_state.state_root, - "storage_root": fast_state.storage_root, - "begin_transaction": fast_state.begin_transaction, - "rollback_transaction": fast_state.rollback_transaction, - "commit_transaction": fast_state.commit_transaction, - "close_state": fast_state.close_state, - } - - for (name, value) in optimized_state_db_patches.items(): - setattr(slow_state, name, value) - - if state_path is not None: - fast_state.State.default_path = state_path - - -def monkey_patch_optimized_spec() -> None: - """ - Replace the ethash implementation with one that supports higher - performance. - - This function must be called before the spec interface is imported - anywhere. - """ - import ethereum.constantinople.fork as slow_spec - - from . import fork as fast_spec - - slow_spec.validate_proof_of_work = fast_spec.validate_proof_of_work - - -def monkey_patch(state_path: Optional[str]) -> None: - """ - Apply all monkey patches to swap in high performance implementations. - - This function must be called before any of the ethereum modules are - imported anywhere. - """ - monkey_patch_optimized_state_db(state_path) - monkey_patch_optimized_spec() diff --git a/src/ethereum_optimized/constantinople/fork.py b/src/ethereum_optimized/constantinople/fork.py deleted file mode 100644 index 8fbc484e80..0000000000 --- a/src/ethereum_optimized/constantinople/fork.py +++ /dev/null @@ -1,47 +0,0 @@ -""" -Optimized Spec -^^^^^^^^^^^^^^ - -.. contents:: Table of Contents - :backlinks: none - :local: - -Introduction ------------- - -This module contains functions can be monkey patched into -`ethereum.constantinople.fork` to use alternate optimized implementations. -""" -from ethereum.base_types import U256_CEIL_VALUE -from ethereum.constantinople.fork import generate_header_hash_for_pow -from ethereum.constantinople.fork_types import Header -from ethereum.ethash import epoch -from ethereum.exceptions import InvalidBlock -from ethereum.utils.ensure import ensure - -try: - import ethash -except ImportError as e: - # Add a message, but keep it an ImportError. - raise e from Exception( - "Install with `pip install 'ethereum[optimized]'` to enable this " - "package" - ) - - -def validate_proof_of_work(header: Header) -> None: - """ - See `ethereum.constantinople.fork.validate_proof_of_work`. - """ - epoch_number = epoch(header.number) - header_hash = generate_header_hash_for_pow(header) - - result = ethash.verify( - int(epoch_number), - header_hash, - header.mix_digest, - int.from_bytes(header.nonce, "big"), - (U256_CEIL_VALUE // header.difficulty).to_be_bytes32(), - ) - - ensure(result, InvalidBlock) diff --git a/src/ethereum_optimized/constantinople/state_db.py b/src/ethereum_optimized/constantinople/state_db.py deleted file mode 100644 index 14a1ccf622..0000000000 --- a/src/ethereum_optimized/constantinople/state_db.py +++ /dev/null @@ -1,315 +0,0 @@ -""" -Optimized State -^^^^^^^^^^^^^^^ - -.. contents:: Table of Contents - :backlinks: none - :local: - -Introduction ------------- - -This module contains functions can be monkey patched into -`ethereum.constantinople.state` to use an optimized database backed state. -""" -import logging -from dataclasses import dataclass -from typing import Any, ClassVar, Dict, List, Optional, Set - -try: - import rust_pyspec_glue -except ImportError as e: - # Add a message, but keep it an ImportError. - raise e from Exception( - "Install with `pip install 'ethereum[optimized]'` to enable this " - "package" - ) - -from ethereum.base_types import U256, Bytes, Uint -from ethereum.constantinople.fork_types import Account, Address, Root - - -class UnmodifiedType: - """ - Sentinal type to represent a value that hasn't been modified. - """ - - pass - - -Unmodified = UnmodifiedType() - - -@dataclass -class State: - """ - The State, backed by a LMDB database. - - When created with `State()` store the db in a temporary directory. When - created with `State(path)` open or create the db located at `path`. - """ - - default_path: ClassVar[Optional[str]] = None - - db: Any - dirty_accounts: Dict[Address, Optional[Account]] - dirty_storage: Dict[Address, Dict[Bytes, U256]] - destroyed_accounts: Set[Address] - tx_restore_points: List[int] - journal: List[Any] - created_accounts: Set[Address] - - def __init__(self, path: Optional[str] = None) -> None: - logging.info("using optimized state db at %s", path) - - if path is None: - path = State.default_path - - self.db = rust_pyspec_glue.DB(path) - self.dirty_accounts = {} - self.dirty_storage = {} - self.destroyed_accounts = set() - self.tx_restore_points = [] - self.journal = [] - self.created_accounts = set() - self.db.begin_mutable() - - def __eq__(self, other: object) -> bool: - """ - Test for equality by comparing state roots. - """ - if not isinstance(other, State): - return NotImplemented - return state_root(self) == state_root(other) - - def __enter__(self) -> "State": - """Support with statements""" - return self - - def __exit__(self, *args: Any) -> None: - """Support with statements""" - close_state(self) - - -def close_state(state: State) -> None: - """Close a state, releasing all resources it holds""" - state.db.close() - state.db = None - del state.dirty_accounts - del state.dirty_storage - del state.destroyed_accounts - del state.journal - del state.created_accounts - - -def get_metadata(state: State, key: Bytes) -> Optional[Bytes]: - """Get a piece of metadata""" - return state.db.get_metadata(key) - - -def set_metadata(state: State, key: Bytes, value: Bytes) -> None: - """Set a piece of metadata""" - return state.db.set_metadata(key, value) - - -def begin_db_transaction(state: State) -> None: - """ - Start a database transaction. A transaction is automatically started when a - `State` is created. Nesting of DB transactions is not supported (unlike - non-db transactions). - - No operations are supported when not in a transaction. - """ - state.db.begin_mutable() - state.tx_restore_points = [] - state.journal = [] - - -def commit_db_transaction(state: State) -> None: - """ - Commit the current database transaction. - """ - if state.tx_restore_points: - raise Exception("In a non-db transaction") - flush(state) - state.db.commit_mutable() - - -def state_root(state: State) -> Root: - """ - See `ethereum.constantinople.state`. - """ - if state.tx_restore_points: - raise Exception("In a non-db transaction") - flush(state) - return state.db.state_root() - - -def storage_root(state: State, address: Address) -> Root: - """ - See `ethereum.constantinople.state`. - """ - if state.tx_restore_points: - raise Exception("In a non-db transaction") - flush(state) - return state.db.storage_root(address) - - -def flush(state: State) -> None: - """ - Send everything in the internal caches to the Rust layer. - """ - if state.tx_restore_points: - raise Exception("In a non-db transaction") - for address in state.destroyed_accounts: - state.db.destroy_storage(address) - for address, account in state.dirty_accounts.items(): - state.db.set_account(address, account) - for address, storage in state.dirty_storage.items(): - for key, value in storage.items(): - state.db.set_storage(address, key, value) - state.destroyed_accounts = set() - state.dirty_accounts.clear() - state.dirty_storage.clear() - - -def rollback_db_transaction(state: State) -> None: - """ - Rollback the current database transaction. - """ - if state.tx_restore_points: - raise Exception("In a non-db transaction") - state.db.rollback_mutable() - state.dirty_accounts.clear() - state.dirty_storage.clear() - state.destroyed_accounts = set() - - -def begin_transaction(state: State) -> None: - """ - See `ethereum.constantinople.state`. - """ - if not state.tx_restore_points: - flush(state) - state.tx_restore_points.append(len(state.journal)) - - -def commit_transaction(state: State) -> None: - """ - See `ethereum.constantinople.state`. - """ - state.tx_restore_points.pop() - if not state.tx_restore_points: - state.journal.clear() - state.created_accounts.clear() - flush(state) - - -def rollback_transaction(state: State) -> None: - """ - See `ethereum.constantinople.state`. - """ - restore_point = state.tx_restore_points.pop() - while len(state.journal) > restore_point: - item = state.journal.pop() - if len(item) == 3: - # Revert a storage key write - if item[2] is Unmodified: - del state.dirty_storage[item[0]][item[1]] - else: - state.dirty_storage[item[0]][item[1]] = item[2] - elif type(item[1]) is dict: - # Restore storage that was destroyed by `destroy_storage()` - state.destroyed_accounts.remove(item[0]) - state.dirty_storage[item[0]] = item[1] - else: - # Revert a change to an account - if item[1] is Unmodified: - del state.dirty_accounts[item[0]] - else: - state.dirty_accounts[item[0]] = item[1] - - if not state.tx_restore_points: - state.created_accounts.clear() - - -def get_storage(state: State, address: Address, key: Bytes) -> U256: - """ - See `ethereum.constantinople.state`. - """ - if address in state.dirty_storage and key in state.dirty_storage[address]: - return state.dirty_storage[address][key] - - if address in state.destroyed_accounts: - return U256(0) - else: - return U256(state.db.get_storage(address, key)) - - -def get_storage_original(state: State, address: Address, key: Bytes) -> U256: - """ - See `ethereum.istanbul.state`. - """ - if address in state.created_accounts: - return U256(0) - else: - return U256(state.db.get_storage(address, key)) - - -def set_storage( - state: State, address: Address, key: Bytes, value: U256 -) -> None: - """ - See `ethereum.constantinople.state`. - """ - if address not in state.dirty_accounts: - state.dirty_accounts[address] = get_account_optional(state, address) - if address not in state.dirty_storage: - state.dirty_storage[address] = {} - if key not in state.dirty_storage[address]: - state.journal.append((address, key, Unmodified)) - else: - state.journal.append((address, key, state.dirty_storage[address][key])) - state.dirty_storage[address][key] = value - - -def get_account_optional(state: State, address: Address) -> Optional[Account]: - """ - See `ethereum.constantinople.state`. - """ - if address in state.dirty_accounts: - return state.dirty_accounts[address] - account = state.db.get_account_optional(address) - if account is not None: - return Account(Uint(account[0]), U256(account[1]), account[2]) - else: - return None - - -def set_account( - state: State, address: Address, account: Optional[Account] -) -> None: - """ - See `ethereum.constantinople.state`. - """ - if address not in state.dirty_accounts: - state.journal.append((address, Unmodified)) - if address in state.dirty_accounts: - state.journal.append((address, state.dirty_accounts[address])) - state.dirty_accounts[address] = account - - -def destroy_storage(state: State, address: Address) -> None: - """ - See `ethereum.constantinople.state`. - """ - state.journal.append((address, state.dirty_storage.pop(address, {}))) - state.destroyed_accounts.add(address) - set_account(state, address, get_account_optional(state, address)) - - -def mark_account_created(state: State, address: Address) -> None: - """ - See `ethereum.frontier.state`. - """ - state.created_accounts.add(address) diff --git a/src/ethereum_optimized/dao_fork/__init__.py b/src/ethereum_optimized/dao_fork/__init__.py deleted file mode 100644 index 8fb68bcee7..0000000000 --- a/src/ethereum_optimized/dao_fork/__init__.py +++ /dev/null @@ -1,71 +0,0 @@ -""" -Optimized Implementations (Dao_fork) -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -.. contents:: Table of Contents - :backlinks: none - :local: -""" - -from typing import Optional - - -def monkey_patch_optimized_state_db(state_path: Optional[str]) -> None: - """ - Replace the state interface with one that supports high performance - updates and storing state in a database. - - This function must be called before the state interface is imported - anywhere. - """ - import ethereum.dao_fork.state as slow_state - - from . import state_db as fast_state - - optimized_state_db_patches = { - "State": fast_state.State, - "get_account_optional": fast_state.get_account_optional, - "set_account": fast_state.set_account, - "destroy_storage": fast_state.destroy_storage, - "get_storage": fast_state.get_storage, - "get_storage_original": fast_state.get_storage_original, - "set_storage": fast_state.set_storage, - "state_root": fast_state.state_root, - "storage_root": fast_state.storage_root, - "begin_transaction": fast_state.begin_transaction, - "rollback_transaction": fast_state.rollback_transaction, - "commit_transaction": fast_state.commit_transaction, - "close_state": fast_state.close_state, - } - - for (name, value) in optimized_state_db_patches.items(): - setattr(slow_state, name, value) - - if state_path is not None: - fast_state.State.default_path = state_path - - -def monkey_patch_optimized_spec() -> None: - """ - Replace the ethash implementation with one that supports higher - performance. - - This function must be called before the spec interface is imported - anywhere. - """ - import ethereum.dao_fork.fork as slow_spec - - from . import fork as fast_spec - - slow_spec.validate_proof_of_work = fast_spec.validate_proof_of_work - - -def monkey_patch(state_path: Optional[str]) -> None: - """ - Apply all monkey patches to swap in high performance implementations. - - This function must be called before any of the ethereum modules are - imported anywhere. - """ - monkey_patch_optimized_state_db(state_path) - monkey_patch_optimized_spec() diff --git a/src/ethereum_optimized/dao_fork/fork.py b/src/ethereum_optimized/dao_fork/fork.py deleted file mode 100644 index ffd39701d4..0000000000 --- a/src/ethereum_optimized/dao_fork/fork.py +++ /dev/null @@ -1,47 +0,0 @@ -""" -Optimized Spec -^^^^^^^^^^^^^^ - -.. contents:: Table of Contents - :backlinks: none - :local: - -Introduction ------------- - -This module contains functions can be monkey patched into -`ethereum.dao_fork.fork` to use alternate optimized implementations. -""" -from ethereum.base_types import U256_CEIL_VALUE -from ethereum.dao_fork.fork import generate_header_hash_for_pow -from ethereum.dao_fork.fork_types import Header -from ethereum.ethash import epoch -from ethereum.exceptions import InvalidBlock -from ethereum.utils.ensure import ensure - -try: - import ethash -except ImportError as e: - # Add a message, but keep it an ImportError. - raise e from Exception( - "Install with `pip install 'ethereum[optimized]'` to enable this " - "package" - ) - - -def validate_proof_of_work(header: Header) -> None: - """ - See `ethereum.dao_fork.fork.validate_proof_of_work`. - """ - epoch_number = epoch(header.number) - header_hash = generate_header_hash_for_pow(header) - - result = ethash.verify( - int(epoch_number), - header_hash, - header.mix_digest, - int.from_bytes(header.nonce, "big"), - (U256_CEIL_VALUE // header.difficulty).to_be_bytes32(), - ) - - ensure(result, InvalidBlock) diff --git a/src/ethereum_optimized/dao_fork/state_db.py b/src/ethereum_optimized/dao_fork/state_db.py deleted file mode 100644 index 2943589bc7..0000000000 --- a/src/ethereum_optimized/dao_fork/state_db.py +++ /dev/null @@ -1,294 +0,0 @@ -""" -Optimized State -^^^^^^^^^^^^^^^ - -.. contents:: Table of Contents - :backlinks: none - :local: - -Introduction ------------- - -This module contains functions can be monkey patched into -`ethereum.dao_fork.state` to use an optimized database backed state. -""" -import logging -from dataclasses import dataclass -from typing import Any, ClassVar, Dict, List, Optional, Set - -try: - import rust_pyspec_glue -except ImportError as e: - # Add a message, but keep it an ImportError. - raise e from Exception( - "Install with `pip install 'ethereum[optimized]'` to enable this " - "package" - ) - -from ethereum.base_types import U256, Bytes, Uint -from ethereum.dao_fork.fork_types import Account, Address, Root - - -class UnmodifiedType: - """ - Sentinal type to represent a value that hasn't been modified. - """ - - pass - - -Unmodified = UnmodifiedType() - - -@dataclass -class State: - """ - The State, backed by a LMDB database. - - When created with `State()` store the db in a temporary directory. When - created with `State(path)` open or create the db located at `path`. - """ - - default_path: ClassVar[Optional[str]] = None - - db: Any - dirty_accounts: Dict[Address, Optional[Account]] - dirty_storage: Dict[Address, Dict[Bytes, U256]] - destroyed_accounts: Set[Address] - tx_restore_points: List[int] - journal: List[Any] - - def __init__(self, path: Optional[str] = None) -> None: - logging.info("using optimized state db at %s", path) - - if path is None: - path = State.default_path - - self.db = rust_pyspec_glue.DB(path) - self.dirty_accounts = {} - self.dirty_storage = {} - self.destroyed_accounts = set() - self.tx_restore_points = [] - self.journal = [] - self.db.begin_mutable() - - def __eq__(self, other: object) -> bool: - """ - Test for equality by comparing state roots. - """ - if not isinstance(other, State): - return NotImplemented - return state_root(self) == state_root(other) - - def __enter__(self) -> "State": - """Support with statements""" - return self - - def __exit__(self, *args: Any) -> None: - """Support with statements""" - close_state(self) - - -def close_state(state: State) -> None: - """Close a state, releasing all resources it holds""" - state.db.close() - state.db = None - del state.dirty_accounts - del state.dirty_storage - del state.destroyed_accounts - del state.journal - - -def get_metadata(state: State, key: Bytes) -> Optional[Bytes]: - """Get a piece of metadata""" - return state.db.get_metadata(key) - - -def set_metadata(state: State, key: Bytes, value: Bytes) -> None: - """Set a piece of metadata""" - return state.db.set_metadata(key, value) - - -def begin_db_transaction(state: State) -> None: - """ - Start a database transaction. A transaction is automatically started when a - `State` is created. Nesting of DB transactions is not supported (unlike - non-db transactions). - - No operations are supported when not in a transaction. - """ - state.db.begin_mutable() - state.tx_restore_points = [] - state.journal = [] - - -def commit_db_transaction(state: State) -> None: - """ - Commit the current database transaction. - """ - if state.tx_restore_points: - raise Exception("In a non-db transaction") - flush(state) - state.db.commit_mutable() - - -def state_root(state: State) -> Root: - """ - See `ethereum.dao_fork.state`. - """ - if state.tx_restore_points: - raise Exception("In a non-db transaction") - flush(state) - return state.db.state_root() - - -def storage_root(state: State, address: Address) -> Root: - """ - See `ethereum.dao_fork.state`. - """ - if state.tx_restore_points: - raise Exception("In a non-db transaction") - flush(state) - return state.db.storage_root(address) - - -def flush(state: State) -> None: - """ - Send everything in the internal caches to the Rust layer. - """ - if state.tx_restore_points: - raise Exception("In a non-db transaction") - for address in state.destroyed_accounts: - state.db.destroy_storage(address) - for address, account in state.dirty_accounts.items(): - state.db.set_account(address, account) - for address, storage in state.dirty_storage.items(): - for key, value in storage.items(): - state.db.set_storage(address, key, value) - state.destroyed_accounts = set() - state.dirty_accounts.clear() - state.dirty_storage.clear() - - -def rollback_db_transaction(state: State) -> None: - """ - Rollback the current database transaction. - """ - if state.tx_restore_points: - raise Exception("In a non-db transaction") - state.db.rollback_mutable() - state.dirty_accounts.clear() - state.dirty_storage.clear() - state.destroyed_accounts = set() - - -def begin_transaction(state: State) -> None: - """ - See `ethereum.dao_fork.state`. - """ - if not state.tx_restore_points: - flush(state) - state.tx_restore_points.append(len(state.journal)) - - -def commit_transaction(state: State) -> None: - """ - See `ethereum.dao_fork.state`. - """ - state.tx_restore_points.pop() - if not state.tx_restore_points: - state.journal.clear() - flush(state) - - -def rollback_transaction(state: State) -> None: - """ - See `ethereum.dao_fork.state`. - """ - restore_point = state.tx_restore_points.pop() - while len(state.journal) > restore_point: - item = state.journal.pop() - if len(item) == 3: - # Revert a storage key write - if item[2] is Unmodified: - del state.dirty_storage[item[0]][item[1]] - else: - state.dirty_storage[item[0]][item[1]] = item[2] - elif type(item[1]) is dict: - # Restore storage that was destroyed by `destroy_storage()` - state.destroyed_accounts.remove(item[0]) - state.dirty_storage[item[0]] = item[1] - else: - # Revert a change to an account - if item[1] is Unmodified: - del state.dirty_accounts[item[0]] - else: - state.dirty_accounts[item[0]] = item[1] - - -def get_storage(state: State, address: Address, key: Bytes) -> U256: - """ - See `ethereum.dao_fork.state`. - """ - if address in state.dirty_storage and key in state.dirty_storage[address]: - return state.dirty_storage[address][key] - return U256(state.db.get_storage(address, key)) - - -def get_storage_original(state: State, address: Address, key: Bytes) -> U256: - """ - See `ethereum.istanbul.state`. - """ - return U256(state.db.get_storage(address, key)) - - -def set_storage( - state: State, address: Address, key: Bytes, value: U256 -) -> None: - """ - See `ethereum.dao_fork.state`. - """ - if address not in state.dirty_accounts: - state.dirty_accounts[address] = get_account_optional(state, address) - if address not in state.dirty_storage: - state.dirty_storage[address] = {} - if key not in state.dirty_storage[address]: - state.journal.append((address, key, Unmodified)) - else: - state.journal.append((address, key, state.dirty_storage[address][key])) - state.dirty_storage[address][key] = value - - -def get_account_optional(state: State, address: Address) -> Optional[Account]: - """ - See `ethereum.dao_fork.state`. - """ - if address in state.dirty_accounts: - return state.dirty_accounts[address] - account = state.db.get_account_optional(address) - if account is not None: - return Account(Uint(account[0]), U256(account[1]), account[2]) - else: - return None - - -def set_account( - state: State, address: Address, account: Optional[Account] -) -> None: - """ - See `ethereum.dao_fork.state`. - """ - if address not in state.dirty_accounts: - state.journal.append((address, Unmodified)) - if address in state.dirty_accounts: - state.journal.append((address, state.dirty_accounts[address])) - state.dirty_accounts[address] = account - - -def destroy_storage(state: State, address: Address) -> None: - """ - See `ethereum.dao_fork.state`. - """ - state.journal.append((address, state.dirty_storage.pop(address, {}))) - state.destroyed_accounts.add(address) - set_account(state, address, get_account_optional(state, address)) diff --git a/src/ethereum_optimized/fork.py b/src/ethereum_optimized/fork.py new file mode 100644 index 0000000000..ae3decba00 --- /dev/null +++ b/src/ethereum_optimized/fork.py @@ -0,0 +1,71 @@ +""" +Optimized Spec +^^^^^^^^^^^^^^ + +.. contents:: Table of Contents + :backlinks: none + :local: + +Introduction +------------ + +This module contains optimized POW functions can be monkey patched into the +`fork` module of a fork. +""" +from importlib import import_module +from typing import Any, Dict, cast + +from ethereum.base_types import U256_CEIL_VALUE +from ethereum.ethash import epoch +from ethereum.exceptions import InvalidBlock +from ethereum.utils.ensure import ensure + +from .utils import add_item + +try: + import ethash +except ImportError as e: + # Add a message, but keep it an ImportError. + raise e from Exception( + "Install with `pip install 'ethereum[optimized]'` to enable this " + "package" + ) + +Header_ = Any + + +def get_optimized_pow_patches(_fork_name: str) -> Dict[str, Any]: + """ + Get a dictionary of patches to be patched into the fork to make it + optimized. + """ + patches: Dict[str, Any] = {} + + mod = cast(Any, import_module("ethereum." + _fork_name + ".fork")) + + if not hasattr(mod, "validate_proof_of_work"): + raise Exception( + "Attempted to get optimized pow patches for non-pow fork" + ) + + generate_header_hash_for_pow = mod.generate_header_hash_for_pow + + @add_item(patches) + def validate_proof_of_work(header: Header_) -> None: + """ + See `validate_proof_of_work`. + """ + epoch_number = epoch(header.number) + header_hash = generate_header_hash_for_pow(header) + + result = ethash.verify( + int(epoch_number), + header_hash, + header.mix_digest, + int.from_bytes(header.nonce, "big"), + (U256_CEIL_VALUE // header.difficulty).to_be_bytes32(), + ) + + ensure(result, InvalidBlock) + + return patches diff --git a/src/ethereum_optimized/frontier/__init__.py b/src/ethereum_optimized/frontier/__init__.py deleted file mode 100644 index 909aed98ea..0000000000 --- a/src/ethereum_optimized/frontier/__init__.py +++ /dev/null @@ -1,72 +0,0 @@ -""" -Optimized Implementations (Frontier) -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -.. contents:: Table of Contents - :backlinks: none - :local: -""" - -from typing import Optional - - -def monkey_patch_optimized_state_db(state_path: Optional[str]) -> None: - """ - Replace the state interface with one that supports high performance - updates and storing state in a database. - - This function must be called before the state interface is imported - anywhere. - """ - import ethereum.frontier.state as slow_state - - from . import state_db as fast_state - - optimized_state_db_patches = { - "State": fast_state.State, - "get_account_optional": fast_state.get_account_optional, - "set_account": fast_state.set_account, - "destroy_storage": fast_state.destroy_storage, - "mark_account_created": fast_state.mark_account_created, - "get_storage": fast_state.get_storage, - "get_storage_original": fast_state.get_storage_original, - "set_storage": fast_state.set_storage, - "state_root": fast_state.state_root, - "storage_root": fast_state.storage_root, - "begin_transaction": fast_state.begin_transaction, - "rollback_transaction": fast_state.rollback_transaction, - "commit_transaction": fast_state.commit_transaction, - "close_state": fast_state.close_state, - } - - for (name, value) in optimized_state_db_patches.items(): - setattr(slow_state, name, value) - - if state_path is not None: - fast_state.State.default_path = state_path - - -def monkey_patch_optimized_spec() -> None: - """ - Replace the ethash implementation with one that supports higher - performance. - - This function must be called before the spec interface is imported - anywhere. - """ - import ethereum.frontier.fork as slow_spec - - from . import fork as fast_spec - - slow_spec.validate_proof_of_work = fast_spec.validate_proof_of_work - - -def monkey_patch(state_path: Optional[str]) -> None: - """ - Apply all monkey patches to swap in high performance implementations. - - This function must be called before any of the ethereum modules are - imported anywhere. - """ - monkey_patch_optimized_state_db(state_path) - monkey_patch_optimized_spec() diff --git a/src/ethereum_optimized/frontier/fork.py b/src/ethereum_optimized/frontier/fork.py deleted file mode 100644 index 1d3591523d..0000000000 --- a/src/ethereum_optimized/frontier/fork.py +++ /dev/null @@ -1,47 +0,0 @@ -""" -Optimized Spec -^^^^^^^^^^^^^^ - -.. contents:: Table of Contents - :backlinks: none - :local: - -Introduction ------------- - -This module contains functions can be monkey patched into -`ethereum.frontier.fork` to use alternate optimized implementations. -""" -from ethereum.base_types import U256_CEIL_VALUE -from ethereum.ethash import epoch -from ethereum.exceptions import InvalidBlock -from ethereum.frontier.fork import generate_header_hash_for_pow -from ethereum.frontier.fork_types import Header -from ethereum.utils.ensure import ensure - -try: - import ethash -except ImportError as e: - # Add a message, but keep it an ImportError. - raise e from Exception( - "Install with `pip install 'ethereum[optimized]'` to enable this " - "package" - ) - - -def validate_proof_of_work(header: Header) -> None: - """ - See `ethereum.frontier.fork.validate_proof_of_work`. - """ - epoch_number = epoch(header.number) - header_hash = generate_header_hash_for_pow(header) - - result = ethash.verify( - int(epoch_number), - header_hash, - header.mix_digest, - int.from_bytes(header.nonce, "big"), - (U256_CEIL_VALUE // header.difficulty).to_be_bytes32(), - ) - - ensure(result, InvalidBlock) diff --git a/src/ethereum_optimized/frontier/state_db.py b/src/ethereum_optimized/frontier/state_db.py deleted file mode 100644 index 013bb5195e..0000000000 --- a/src/ethereum_optimized/frontier/state_db.py +++ /dev/null @@ -1,315 +0,0 @@ -""" -Optimized State -^^^^^^^^^^^^^^^ - -.. contents:: Table of Contents - :backlinks: none - :local: - -Introduction ------------- - -This module contains functions can be monkey patched into -`ethereum.frontier.state` to use an optimized database backed state. -""" -import logging -from dataclasses import dataclass -from typing import Any, ClassVar, Dict, List, Optional, Set - -try: - import rust_pyspec_glue -except ImportError as e: - # Add a message, but keep it an ImportError. - raise e from Exception( - "Install with `pip install 'ethereum[optimized]'` to enable this " - "package" - ) - -from ethereum.base_types import U256, Bytes, Uint -from ethereum.frontier.fork_types import Account, Address, Root - - -class UnmodifiedType: - """ - Sentinal type to represent a value that hasn't been modified. - """ - - pass - - -Unmodified = UnmodifiedType() - - -@dataclass -class State: - """ - The State, backed by a LMDB database. - - When created with `State()` store the db in a temporary directory. When - created with `State(path)` open or create the db located at `path`. - """ - - default_path: ClassVar[Optional[str]] = None - - db: Any - dirty_accounts: Dict[Address, Optional[Account]] - dirty_storage: Dict[Address, Dict[Bytes, U256]] - destroyed_accounts: Set[Address] - tx_restore_points: List[int] - journal: List[Any] - created_accounts: Set[Address] - - def __init__(self, path: Optional[str] = None) -> None: - logging.info("using optimized state db at %s", path) - - if path is None: - path = State.default_path - - self.db = rust_pyspec_glue.DB(path) - self.dirty_accounts = {} - self.dirty_storage = {} - self.destroyed_accounts = set() - self.tx_restore_points = [] - self.journal = [] - self.created_accounts = set() - self.db.begin_mutable() - - def __eq__(self, other: object) -> bool: - """ - Test for equality by comparing state roots. - """ - if not isinstance(other, State): - return NotImplemented - return state_root(self) == state_root(other) - - def __enter__(self) -> "State": - """Support with statements""" - return self - - def __exit__(self, *args: Any) -> None: - """Support with statements""" - close_state(self) - - -def close_state(state: State) -> None: - """Close a state, releasing all resources it holds""" - state.db.close() - state.db = None - del state.dirty_accounts - del state.dirty_storage - del state.destroyed_accounts - del state.journal - del state.created_accounts - - -def get_metadata(state: State, key: Bytes) -> Optional[Bytes]: - """Get a piece of metadata""" - return state.db.get_metadata(key) - - -def set_metadata(state: State, key: Bytes, value: Bytes) -> None: - """Set a piece of metadata""" - return state.db.set_metadata(key, value) - - -def begin_db_transaction(state: State) -> None: - """ - Start a database transaction. A transaction is automatically started when a - `State` is created. Nesting of DB transactions is not supported (unlike - non-db transactions). - - No operations are supported when not in a transaction. - """ - state.db.begin_mutable() - state.tx_restore_points = [] - state.journal = [] - - -def commit_db_transaction(state: State) -> None: - """ - Commit the current database transaction. - """ - if state.tx_restore_points: - raise Exception("In a non-db transaction") - flush(state) - state.db.commit_mutable() - - -def state_root(state: State) -> Root: - """ - See `ethereum.frontier.state`. - """ - if state.tx_restore_points: - raise Exception("In a non-db transaction") - flush(state) - return state.db.state_root() - - -def storage_root(state: State, address: Address) -> Root: - """ - See `ethereum.frontier.state`. - """ - if state.tx_restore_points: - raise Exception("In a non-db transaction") - flush(state) - return state.db.storage_root(address) - - -def flush(state: State) -> None: - """ - Send everything in the internal caches to the Rust layer. - """ - if state.tx_restore_points: - raise Exception("In a non-db transaction") - for address in state.destroyed_accounts: - state.db.destroy_storage(address) - for address, account in state.dirty_accounts.items(): - state.db.set_account(address, account) - for address, storage in state.dirty_storage.items(): - for key, value in storage.items(): - state.db.set_storage(address, key, value) - state.destroyed_accounts = set() - state.dirty_accounts.clear() - state.dirty_storage.clear() - - -def rollback_db_transaction(state: State) -> None: - """ - Rollback the current database transaction. - """ - if state.tx_restore_points: - raise Exception("In a non-db transaction") - state.db.rollback_mutable() - state.dirty_accounts.clear() - state.dirty_storage.clear() - state.destroyed_accounts = set() - - -def begin_transaction(state: State) -> None: - """ - See `ethereum.frontier.state`. - """ - if not state.tx_restore_points: - flush(state) - state.tx_restore_points.append(len(state.journal)) - - -def commit_transaction(state: State) -> None: - """ - See `ethereum.frontier.state`. - """ - state.tx_restore_points.pop() - if not state.tx_restore_points: - state.journal.clear() - state.created_accounts.clear() - flush(state) - - -def rollback_transaction(state: State) -> None: - """ - See `ethereum.frontier.state`. - """ - restore_point = state.tx_restore_points.pop() - while len(state.journal) > restore_point: - item = state.journal.pop() - if len(item) == 3: - # Revert a storage key write - if item[2] is Unmodified: - del state.dirty_storage[item[0]][item[1]] - else: - state.dirty_storage[item[0]][item[1]] = item[2] - elif type(item[1]) is dict: - # Restore storage that was destroyed by `destroy_storage()` - state.destroyed_accounts.remove(item[0]) - state.dirty_storage[item[0]] = item[1] - else: - # Revert a change to an account - if item[1] is Unmodified: - del state.dirty_accounts[item[0]] - else: - state.dirty_accounts[item[0]] = item[1] - - if not state.tx_restore_points: - state.created_accounts.clear() - - -def get_storage(state: State, address: Address, key: Bytes) -> U256: - """ - See `ethereum.frontier.state`. - """ - if address in state.dirty_storage and key in state.dirty_storage[address]: - return state.dirty_storage[address][key] - - if address in state.destroyed_accounts: - return U256(0) - else: - return U256(state.db.get_storage(address, key)) - - -def get_storage_original(state: State, address: Address, key: Bytes) -> U256: - """ - See `ethereum.istanbul.state`. - """ - if address in state.created_accounts: - return U256(0) - else: - return U256(state.db.get_storage(address, key)) - - -def set_storage( - state: State, address: Address, key: Bytes, value: U256 -) -> None: - """ - See `ethereum.frontier.state`. - """ - if address not in state.dirty_accounts: - state.dirty_accounts[address] = get_account_optional(state, address) - if address not in state.dirty_storage: - state.dirty_storage[address] = {} - if key not in state.dirty_storage[address]: - state.journal.append((address, key, Unmodified)) - else: - state.journal.append((address, key, state.dirty_storage[address][key])) - state.dirty_storage[address][key] = value - - -def get_account_optional(state: State, address: Address) -> Optional[Account]: - """ - See `ethereum.frontier.state`. - """ - if address in state.dirty_accounts: - return state.dirty_accounts[address] - account = state.db.get_account_optional(address) - if account is not None: - return Account(Uint(account[0]), U256(account[1]), account[2]) - else: - return None - - -def set_account( - state: State, address: Address, account: Optional[Account] -) -> None: - """ - See `ethereum.frontier.state`. - """ - if address not in state.dirty_accounts: - state.journal.append((address, Unmodified)) - if address in state.dirty_accounts: - state.journal.append((address, state.dirty_accounts[address])) - state.dirty_accounts[address] = account - - -def destroy_storage(state: State, address: Address) -> None: - """ - See `ethereum.frontier.state`. - """ - state.journal.append((address, state.dirty_storage.pop(address, {}))) - state.destroyed_accounts.add(address) - set_account(state, address, get_account_optional(state, address)) - - -def mark_account_created(state: State, address: Address) -> None: - """ - See `ethereum.frontier.state`. - """ - state.created_accounts.add(address) diff --git a/src/ethereum_optimized/gray_glacier/__init__.py b/src/ethereum_optimized/gray_glacier/__init__.py deleted file mode 100644 index 70be123efa..0000000000 --- a/src/ethereum_optimized/gray_glacier/__init__.py +++ /dev/null @@ -1,72 +0,0 @@ -""" -Optimized Implementations (Gray Glacier) -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -.. contents:: Table of Contents - :backlinks: none - :local: -""" - -from typing import Optional - - -def monkey_patch_optimized_state_db(state_path: Optional[str]) -> None: - """ - Replace the state interface with one that supports high performance - updates and storing state in a database. - - This function must be called before the state interface is imported - anywhere. - """ - import ethereum.gray_glacier.state as slow_state - - from . import state_db as fast_state - - optimized_state_db_patches = { - "State": fast_state.State, - "get_account_optional": fast_state.get_account_optional, - "set_account": fast_state.set_account, - "destroy_storage": fast_state.destroy_storage, - "mark_account_created": fast_state.mark_account_created, - "get_storage": fast_state.get_storage, - "get_storage_original": fast_state.get_storage_original, - "set_storage": fast_state.set_storage, - "state_root": fast_state.state_root, - "storage_root": fast_state.storage_root, - "begin_transaction": fast_state.begin_transaction, - "rollback_transaction": fast_state.rollback_transaction, - "commit_transaction": fast_state.commit_transaction, - "close_state": fast_state.close_state, - } - - for (name, value) in optimized_state_db_patches.items(): - setattr(slow_state, name, value) - - if state_path is not None: - fast_state.State.default_path = state_path - - -def monkey_patch_optimized_spec() -> None: - """ - Replace the ethash implementation with one that supports higher - performance. - - This function must be called before the spec interface is imported - anywhere. - """ - import ethereum.gray_glacier.fork as slow_spec - - from . import fork as fast_spec - - slow_spec.validate_proof_of_work = fast_spec.validate_proof_of_work - - -def monkey_patch(state_path: Optional[str]) -> None: - """ - Apply all monkey patches to swap in high performance implementations. - - This function must be called before any of the ethereum modules are - imported anywhere. - """ - monkey_patch_optimized_state_db(state_path) - monkey_patch_optimized_spec() diff --git a/src/ethereum_optimized/gray_glacier/fork.py b/src/ethereum_optimized/gray_glacier/fork.py deleted file mode 100644 index 1662744e2a..0000000000 --- a/src/ethereum_optimized/gray_glacier/fork.py +++ /dev/null @@ -1,47 +0,0 @@ -""" -Optimized Spec -^^^^^^^^^^^^^^ - -.. contents:: Table of Contents - :backlinks: none - :local: - -Introduction ------------- - -This module contains functions can be monkey patched into -`ethereum.gray_glacier.fork` to use alternate optimized implementations. -""" -from ethereum.base_types import U256_CEIL_VALUE -from ethereum.ethash import epoch -from ethereum.exceptions import InvalidBlock -from ethereum.gray_glacier.fork import generate_header_hash_for_pow -from ethereum.gray_glacier.fork_types import Header -from ethereum.utils.ensure import ensure - -try: - import ethash -except ImportError as e: - # Add a message, but keep it an ImportError. - raise e from Exception( - "Install with `pip install 'ethereum[optimized]'` to enable this " - "package" - ) - - -def validate_proof_of_work(header: Header) -> None: - """ - See `ethereum.gray_glacier.fork.validate_proof_of_work`. - """ - epoch_number = epoch(header.number) - header_hash = generate_header_hash_for_pow(header) - - result = ethash.verify( - int(epoch_number), - header_hash, - header.mix_digest, - int.from_bytes(header.nonce, "big"), - (U256_CEIL_VALUE // header.difficulty).to_be_bytes32(), - ) - - ensure(result, InvalidBlock) diff --git a/src/ethereum_optimized/gray_glacier/state_db.py b/src/ethereum_optimized/gray_glacier/state_db.py deleted file mode 100644 index 4778a41ce6..0000000000 --- a/src/ethereum_optimized/gray_glacier/state_db.py +++ /dev/null @@ -1,315 +0,0 @@ -""" -Optimized State -^^^^^^^^^^^^^^^ - -.. contents:: Table of Contents - :backlinks: none - :local: - -Introduction ------------- - -This module contains functions can be monkey patched into -`ethereum.gray_glacier.state` to use an optimized database backed state. -""" -import logging -from dataclasses import dataclass -from typing import Any, ClassVar, Dict, List, Optional, Set - -try: - import rust_pyspec_glue -except ImportError as e: - # Add a message, but keep it an ImportError. - raise e from Exception( - "Install with `pip install 'ethereum[optimized]'` to enable this " - "package" - ) - -from ethereum.base_types import U256, Bytes, Uint -from ethereum.gray_glacier.fork_types import Account, Address, Root - - -class UnmodifiedType: - """ - Sentinal type to represent a value that hasn't been modified. - """ - - pass - - -Unmodified = UnmodifiedType() - - -@dataclass -class State: - """ - The State, backed by a LMDB database. - - When created with `State()` store the db in a temporary directory. When - created with `State(path)` open or create the db located at `path`. - """ - - default_path: ClassVar[Optional[str]] = None - - db: Any - dirty_accounts: Dict[Address, Optional[Account]] - dirty_storage: Dict[Address, Dict[Bytes, U256]] - destroyed_accounts: Set[Address] - tx_restore_points: List[int] - journal: List[Any] - created_accounts: Set[Address] - - def __init__(self, path: Optional[str] = None) -> None: - logging.info("using optimized state db at %s", path) - - if path is None: - path = State.default_path - - self.db = rust_pyspec_glue.DB(path) - self.dirty_accounts = {} - self.dirty_storage = {} - self.destroyed_accounts = set() - self.tx_restore_points = [] - self.journal = [] - self.created_accounts = set() - self.db.begin_mutable() - - def __eq__(self, other: object) -> bool: - """ - Test for equality by comparing state roots. - """ - if not isinstance(other, State): - return NotImplemented - return state_root(self) == state_root(other) - - def __enter__(self) -> "State": - """Support with statements""" - return self - - def __exit__(self, *args: Any) -> None: - """Support with statements""" - close_state(self) - - -def close_state(state: State) -> None: - """Close a state, releasing all resources it holds""" - state.db.close() - state.db = None - del state.dirty_accounts - del state.dirty_storage - del state.destroyed_accounts - del state.journal - del state.created_accounts - - -def get_metadata(state: State, key: Bytes) -> Optional[Bytes]: - """Get a piece of metadata""" - return state.db.get_metadata(key) - - -def set_metadata(state: State, key: Bytes, value: Bytes) -> None: - """Set a piece of metadata""" - return state.db.set_metadata(key, value) - - -def begin_db_transaction(state: State) -> None: - """ - Start a database transaction. A transaction is automatically started when a - `State` is created. Nesting of DB transactions is not supported (unlike - non-db transactions). - - No operations are supported when not in a transaction. - """ - state.db.begin_mutable() - state.tx_restore_points = [] - state.journal = [] - - -def commit_db_transaction(state: State) -> None: - """ - Commit the current database transaction. - """ - if state.tx_restore_points: - raise Exception("In a non-db transaction") - flush(state) - state.db.commit_mutable() - - -def state_root(state: State) -> Root: - """ - See `ethereum.gray_glacier.state`. - """ - if state.tx_restore_points: - raise Exception("In a non-db transaction") - flush(state) - return state.db.state_root() - - -def storage_root(state: State, address: Address) -> Root: - """ - See `ethereum.gray_glacier.state`. - """ - if state.tx_restore_points: - raise Exception("In a non-db transaction") - flush(state) - return state.db.storage_root(address) - - -def flush(state: State) -> None: - """ - Send everything in the internal caches to the Rust layer. - """ - if state.tx_restore_points: - raise Exception("In a non-db transaction") - for address in state.destroyed_accounts: - state.db.destroy_storage(address) - for address, account in state.dirty_accounts.items(): - state.db.set_account(address, account) - for address, storage in state.dirty_storage.items(): - for key, value in storage.items(): - state.db.set_storage(address, key, value) - state.destroyed_accounts = set() - state.dirty_accounts.clear() - state.dirty_storage.clear() - - -def rollback_db_transaction(state: State) -> None: - """ - Rollback the current database transaction. - """ - if state.tx_restore_points: - raise Exception("In a non-db transaction") - state.db.rollback_mutable() - state.dirty_accounts.clear() - state.dirty_storage.clear() - state.destroyed_accounts = set() - - -def begin_transaction(state: State) -> None: - """ - See `ethereum.gray_glacier.state`. - """ - if not state.tx_restore_points: - flush(state) - state.tx_restore_points.append(len(state.journal)) - - -def commit_transaction(state: State) -> None: - """ - See `ethereum.gray_glacier.state`. - """ - state.tx_restore_points.pop() - if not state.tx_restore_points: - state.journal.clear() - state.created_accounts.clear() - flush(state) - - -def rollback_transaction(state: State) -> None: - """ - See `ethereum.gray_glacier.state`. - """ - restore_point = state.tx_restore_points.pop() - while len(state.journal) > restore_point: - item = state.journal.pop() - if len(item) == 3: - # Revert a storage key write - if item[2] is Unmodified: - del state.dirty_storage[item[0]][item[1]] - else: - state.dirty_storage[item[0]][item[1]] = item[2] - elif type(item[1]) is dict: - # Restore storage that was destroyed by `destroy_storage()` - state.destroyed_accounts.remove(item[0]) - state.dirty_storage[item[0]] = item[1] - else: - # Revert a change to an account - if item[1] is Unmodified: - del state.dirty_accounts[item[0]] - else: - state.dirty_accounts[item[0]] = item[1] - - if not state.tx_restore_points: - state.created_accounts.clear() - - -def get_storage(state: State, address: Address, key: Bytes) -> U256: - """ - See `ethereum.gray_glacier.state`. - """ - if address in state.dirty_storage and key in state.dirty_storage[address]: - return state.dirty_storage[address][key] - - if address in state.destroyed_accounts: - return U256(0) - else: - return U256(state.db.get_storage(address, key)) - - -def get_storage_original(state: State, address: Address, key: Bytes) -> U256: - """ - See `ethereum.istanbul.state`. - """ - if address in state.created_accounts: - return U256(0) - else: - return U256(state.db.get_storage(address, key)) - - -def set_storage( - state: State, address: Address, key: Bytes, value: U256 -) -> None: - """ - See `ethereum.gray_glacier.state`. - """ - if address not in state.dirty_accounts: - state.dirty_accounts[address] = get_account_optional(state, address) - if address not in state.dirty_storage: - state.dirty_storage[address] = {} - if key not in state.dirty_storage[address]: - state.journal.append((address, key, Unmodified)) - else: - state.journal.append((address, key, state.dirty_storage[address][key])) - state.dirty_storage[address][key] = value - - -def get_account_optional(state: State, address: Address) -> Optional[Account]: - """ - See `ethereum.gray_glacier.state`. - """ - if address in state.dirty_accounts: - return state.dirty_accounts[address] - account = state.db.get_account_optional(address) - if account is not None: - return Account(Uint(account[0]), U256(account[1]), account[2]) - else: - return None - - -def set_account( - state: State, address: Address, account: Optional[Account] -) -> None: - """ - See `ethereum.gray_glacier.state`. - """ - if address not in state.dirty_accounts: - state.journal.append((address, Unmodified)) - if address in state.dirty_accounts: - state.journal.append((address, state.dirty_accounts[address])) - state.dirty_accounts[address] = account - - -def destroy_storage(state: State, address: Address) -> None: - """ - See `ethereum.gray_glacier.state`. - """ - state.journal.append((address, state.dirty_storage.pop(address, {}))) - state.destroyed_accounts.add(address) - set_account(state, address, get_account_optional(state, address)) - - -def mark_account_created(state: State, address: Address) -> None: - """ - See `ethereum.frontier.state`. - """ - state.created_accounts.add(address) diff --git a/src/ethereum_optimized/homestead/__init__.py b/src/ethereum_optimized/homestead/__init__.py deleted file mode 100644 index e87d2c204e..0000000000 --- a/src/ethereum_optimized/homestead/__init__.py +++ /dev/null @@ -1,72 +0,0 @@ -""" -Optimized Implementations (Homestead) -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -.. contents:: Table of Contents - :backlinks: none - :local: -""" - -from typing import Optional - - -def monkey_patch_optimized_state_db(state_path: Optional[str]) -> None: - """ - Replace the state interface with one that supports high performance - updates and storing state in a database. - - This function must be called before the state interface is imported - anywhere. - """ - import ethereum.homestead.state as slow_state - - from . import state_db as fast_state - - optimized_state_db_patches = { - "State": fast_state.State, - "get_account_optional": fast_state.get_account_optional, - "set_account": fast_state.set_account, - "destroy_storage": fast_state.destroy_storage, - "mark_account_created": fast_state.mark_account_created, - "get_storage": fast_state.get_storage, - "get_storage_original": fast_state.get_storage_original, - "set_storage": fast_state.set_storage, - "state_root": fast_state.state_root, - "storage_root": fast_state.storage_root, - "begin_transaction": fast_state.begin_transaction, - "rollback_transaction": fast_state.rollback_transaction, - "commit_transaction": fast_state.commit_transaction, - "close_state": fast_state.close_state, - } - - for (name, value) in optimized_state_db_patches.items(): - setattr(slow_state, name, value) - - if state_path is not None: - fast_state.State.default_path = state_path - - -def monkey_patch_optimized_spec() -> None: - """ - Replace the ethash implementation with one that supports higher - performance. - - This function must be called before the spec interface is imported - anywhere. - """ - import ethereum.homestead.fork as slow_spec - - from . import fork as fast_spec - - slow_spec.validate_proof_of_work = fast_spec.validate_proof_of_work - - -def monkey_patch(state_path: Optional[str]) -> None: - """ - Apply all monkey patches to swap in high performance implementations. - - This function must be called before any of the ethereum modules are - imported anywhere. - """ - monkey_patch_optimized_state_db(state_path) - monkey_patch_optimized_spec() diff --git a/src/ethereum_optimized/homestead/fork.py b/src/ethereum_optimized/homestead/fork.py deleted file mode 100644 index f2f14ea110..0000000000 --- a/src/ethereum_optimized/homestead/fork.py +++ /dev/null @@ -1,47 +0,0 @@ -""" -Optimized Spec -^^^^^^^^^^^^^^ - -.. contents:: Table of Contents - :backlinks: none - :local: - -Introduction ------------- - -This module contains functions can be monkey patched into -`ethereum.homestead.fork` to use alternate optimized implementations. -""" -from ethereum.base_types import U256_CEIL_VALUE -from ethereum.ethash import epoch -from ethereum.exceptions import InvalidBlock -from ethereum.homestead.fork import generate_header_hash_for_pow -from ethereum.homestead.fork_types import Header -from ethereum.utils.ensure import ensure - -try: - import ethash -except ImportError as e: - # Add a message, but keep it an ImportError. - raise e from Exception( - "Install with `pip install 'ethereum[optimized]'` to enable this " - "package" - ) - - -def validate_proof_of_work(header: Header) -> None: - """ - See `ethereum.homestead.fork.validate_proof_of_work`. - """ - epoch_number = epoch(header.number) - header_hash = generate_header_hash_for_pow(header) - - result = ethash.verify( - int(epoch_number), - header_hash, - header.mix_digest, - int.from_bytes(header.nonce, "big"), - (U256_CEIL_VALUE // header.difficulty).to_be_bytes32(), - ) - - ensure(result, InvalidBlock) diff --git a/src/ethereum_optimized/homestead/state_db.py b/src/ethereum_optimized/homestead/state_db.py deleted file mode 100644 index 397c07539e..0000000000 --- a/src/ethereum_optimized/homestead/state_db.py +++ /dev/null @@ -1,315 +0,0 @@ -""" -Optimized State -^^^^^^^^^^^^^^^ - -.. contents:: Table of Contents - :backlinks: none - :local: - -Introduction ------------- - -This module contains functions can be monkey patched into -`ethereum.homestead.state` to use an optimized database backed state. -""" -import logging -from dataclasses import dataclass -from typing import Any, ClassVar, Dict, List, Optional, Set - -try: - import rust_pyspec_glue -except ImportError as e: - # Add a message, but keep it an ImportError. - raise e from Exception( - "Install with `pip install 'ethereum[optimized]'` to enable this " - "package" - ) - -from ethereum.base_types import U256, Bytes, Uint -from ethereum.homestead.fork_types import Account, Address, Root - - -class UnmodifiedType: - """ - Sentinal type to represent a value that hasn't been modified. - """ - - pass - - -Unmodified = UnmodifiedType() - - -@dataclass -class State: - """ - The State, backed by a LMDB database. - - When created with `State()` store the db in a temporary directory. When - created with `State(path)` open or create the db located at `path`. - """ - - default_path: ClassVar[Optional[str]] = None - - db: Any - dirty_accounts: Dict[Address, Optional[Account]] - dirty_storage: Dict[Address, Dict[Bytes, U256]] - destroyed_accounts: Set[Address] - tx_restore_points: List[int] - journal: List[Any] - created_accounts: Set[Address] - - def __init__(self, path: Optional[str] = None) -> None: - logging.info("using optimized state db at %s", path) - - if path is None: - path = State.default_path - - self.db = rust_pyspec_glue.DB(path) - self.dirty_accounts = {} - self.dirty_storage = {} - self.destroyed_accounts = set() - self.tx_restore_points = [] - self.journal = [] - self.created_accounts = set() - self.db.begin_mutable() - - def __eq__(self, other: object) -> bool: - """ - Test for equality by comparing state roots. - """ - if not isinstance(other, State): - return NotImplemented - return state_root(self) == state_root(other) - - def __enter__(self) -> "State": - """Support with statements""" - return self - - def __exit__(self, *args: Any) -> None: - """Support with statements""" - close_state(self) - - -def close_state(state: State) -> None: - """Close a state, releasing all resources it holds""" - state.db.close() - state.db = None - del state.dirty_accounts - del state.dirty_storage - del state.destroyed_accounts - del state.journal - del state.created_accounts - - -def get_metadata(state: State, key: Bytes) -> Optional[Bytes]: - """Get a piece of metadata""" - return state.db.get_metadata(key) - - -def set_metadata(state: State, key: Bytes, value: Bytes) -> None: - """Set a piece of metadata""" - return state.db.set_metadata(key, value) - - -def begin_db_transaction(state: State) -> None: - """ - Start a database transaction. A transaction is automatically started when a - `State` is created. Nesting of DB transactions is not supported (unlike - non-db transactions). - - No operations are supported when not in a transaction. - """ - state.db.begin_mutable() - state.tx_restore_points = [] - state.journal = [] - - -def commit_db_transaction(state: State) -> None: - """ - Commit the current database transaction. - """ - if state.tx_restore_points: - raise Exception("In a non-db transaction") - flush(state) - state.db.commit_mutable() - - -def state_root(state: State) -> Root: - """ - See `ethereum.homestead.state`. - """ - if state.tx_restore_points: - raise Exception("In a non-db transaction") - flush(state) - return state.db.state_root() - - -def storage_root(state: State, address: Address) -> Root: - """ - See `ethereum.homestead.state`. - """ - if state.tx_restore_points: - raise Exception("In a non-db transaction") - flush(state) - return state.db.storage_root(address) - - -def flush(state: State) -> None: - """ - Send everything in the internal caches to the Rust layer. - """ - if state.tx_restore_points: - raise Exception("In a non-db transaction") - for address in state.destroyed_accounts: - state.db.destroy_storage(address) - for address, account in state.dirty_accounts.items(): - state.db.set_account(address, account) - for address, storage in state.dirty_storage.items(): - for key, value in storage.items(): - state.db.set_storage(address, key, value) - state.destroyed_accounts = set() - state.dirty_accounts.clear() - state.dirty_storage.clear() - - -def rollback_db_transaction(state: State) -> None: - """ - Rollback the current database transaction. - """ - if state.tx_restore_points: - raise Exception("In a non-db transaction") - state.db.rollback_mutable() - state.dirty_accounts.clear() - state.dirty_storage.clear() - state.destroyed_accounts = set() - - -def begin_transaction(state: State) -> None: - """ - See `ethereum.homestead.state`. - """ - if not state.tx_restore_points: - flush(state) - state.tx_restore_points.append(len(state.journal)) - - -def commit_transaction(state: State) -> None: - """ - See `ethereum.homestead.state`. - """ - state.tx_restore_points.pop() - if not state.tx_restore_points: - state.journal.clear() - state.created_accounts.clear() - flush(state) - - -def rollback_transaction(state: State) -> None: - """ - See `ethereum.homestead.state`. - """ - restore_point = state.tx_restore_points.pop() - while len(state.journal) > restore_point: - item = state.journal.pop() - if len(item) == 3: - # Revert a storage key write - if item[2] is Unmodified: - del state.dirty_storage[item[0]][item[1]] - else: - state.dirty_storage[item[0]][item[1]] = item[2] - elif type(item[1]) is dict: - # Restore storage that was destroyed by `destroy_storage()` - state.destroyed_accounts.remove(item[0]) - state.dirty_storage[item[0]] = item[1] - else: - # Revert a change to an account - if item[1] is Unmodified: - del state.dirty_accounts[item[0]] - else: - state.dirty_accounts[item[0]] = item[1] - - if not state.tx_restore_points: - state.created_accounts.clear() - - -def get_storage(state: State, address: Address, key: Bytes) -> U256: - """ - See `ethereum.homestead.state`. - """ - if address in state.dirty_storage and key in state.dirty_storage[address]: - return state.dirty_storage[address][key] - - if address in state.destroyed_accounts: - return U256(0) - else: - return U256(state.db.get_storage(address, key)) - - -def get_storage_original(state: State, address: Address, key: Bytes) -> U256: - """ - See `ethereum.istanbul.state`. - """ - if address in state.created_accounts: - return U256(0) - else: - return U256(state.db.get_storage(address, key)) - - -def set_storage( - state: State, address: Address, key: Bytes, value: U256 -) -> None: - """ - See `ethereum.homestead.state`. - """ - if address not in state.dirty_accounts: - state.dirty_accounts[address] = get_account_optional(state, address) - if address not in state.dirty_storage: - state.dirty_storage[address] = {} - if key not in state.dirty_storage[address]: - state.journal.append((address, key, Unmodified)) - else: - state.journal.append((address, key, state.dirty_storage[address][key])) - state.dirty_storage[address][key] = value - - -def get_account_optional(state: State, address: Address) -> Optional[Account]: - """ - See `ethereum.homestead.state`. - """ - if address in state.dirty_accounts: - return state.dirty_accounts[address] - account = state.db.get_account_optional(address) - if account is not None: - return Account(Uint(account[0]), U256(account[1]), account[2]) - else: - return None - - -def set_account( - state: State, address: Address, account: Optional[Account] -) -> None: - """ - See `ethereum.homestead.state`. - """ - if address not in state.dirty_accounts: - state.journal.append((address, Unmodified)) - if address in state.dirty_accounts: - state.journal.append((address, state.dirty_accounts[address])) - state.dirty_accounts[address] = account - - -def destroy_storage(state: State, address: Address) -> None: - """ - See `ethereum.homestead.state`. - """ - state.journal.append((address, state.dirty_storage.pop(address, {}))) - state.destroyed_accounts.add(address) - set_account(state, address, get_account_optional(state, address)) - - -def mark_account_created(state: State, address: Address) -> None: - """ - See `ethereum.frontier.state`. - """ - state.created_accounts.add(address) diff --git a/src/ethereum_optimized/istanbul/__init__.py b/src/ethereum_optimized/istanbul/__init__.py deleted file mode 100644 index 92ab3c80a4..0000000000 --- a/src/ethereum_optimized/istanbul/__init__.py +++ /dev/null @@ -1,72 +0,0 @@ -""" -Optimized Implementations (Istanbul) -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -.. contents:: Table of Contents - :backlinks: none - :local: -""" - -from typing import Optional - - -def monkey_patch_optimized_state_db(state_path: Optional[str]) -> None: - """ - Replace the state interface with one that supports high performance - updates and storing state in a database. - - This function must be called before the state interface is imported - anywhere. - """ - import ethereum.istanbul.state as slow_state - - from . import state_db as fast_state - - optimized_state_db_patches = { - "State": fast_state.State, - "get_account_optional": fast_state.get_account_optional, - "set_account": fast_state.set_account, - "destroy_storage": fast_state.destroy_storage, - "mark_account_created": fast_state.mark_account_created, - "get_storage": fast_state.get_storage, - "get_storage_original": fast_state.get_storage_original, - "set_storage": fast_state.set_storage, - "state_root": fast_state.state_root, - "storage_root": fast_state.storage_root, - "begin_transaction": fast_state.begin_transaction, - "rollback_transaction": fast_state.rollback_transaction, - "commit_transaction": fast_state.commit_transaction, - "close_state": fast_state.close_state, - } - - for (name, value) in optimized_state_db_patches.items(): - setattr(slow_state, name, value) - - if state_path is not None: - fast_state.State.default_path = state_path - - -def monkey_patch_optimized_spec() -> None: - """ - Replace the ethash implementation with one that supports higher - performance. - - This function must be called before the spec interface is imported - anywhere. - """ - import ethereum.istanbul.fork as slow_spec - - from . import fork as fast_spec - - slow_spec.validate_proof_of_work = fast_spec.validate_proof_of_work - - -def monkey_patch(state_path: Optional[str]) -> None: - """ - Apply all monkey patches to swap in high performance implementations. - - This function must be called before any of the ethereum modules are - imported anywhere. - """ - monkey_patch_optimized_state_db(state_path) - monkey_patch_optimized_spec() diff --git a/src/ethereum_optimized/istanbul/fork.py b/src/ethereum_optimized/istanbul/fork.py deleted file mode 100644 index 03ceb9045e..0000000000 --- a/src/ethereum_optimized/istanbul/fork.py +++ /dev/null @@ -1,47 +0,0 @@ -""" -Optimized Spec -^^^^^^^^^^^^^^ - -.. contents:: Table of Contents - :backlinks: none - :local: - -Introduction ------------- - -This module contains functions can be monkey patched into -`ethereum.istanbul.fork` to use alternate optimized implementations. -""" -from ethereum.base_types import U256_CEIL_VALUE -from ethereum.ethash import epoch -from ethereum.exceptions import InvalidBlock -from ethereum.istanbul.fork import generate_header_hash_for_pow -from ethereum.istanbul.fork_types import Header -from ethereum.utils.ensure import ensure - -try: - import ethash -except ImportError as e: - # Add a message, but keep it an ImportError. - raise e from Exception( - "Install with `pip install 'ethereum[optimized]'` to enable this " - "package" - ) - - -def validate_proof_of_work(header: Header) -> None: - """ - See `ethereum.istanbul.fork.validate_proof_of_work`. - """ - epoch_number = epoch(header.number) - header_hash = generate_header_hash_for_pow(header) - - result = ethash.verify( - int(epoch_number), - header_hash, - header.mix_digest, - int.from_bytes(header.nonce, "big"), - (U256_CEIL_VALUE // header.difficulty).to_be_bytes32(), - ) - - ensure(result, InvalidBlock) diff --git a/src/ethereum_optimized/istanbul/state_db.py b/src/ethereum_optimized/istanbul/state_db.py deleted file mode 100644 index b00ab992ea..0000000000 --- a/src/ethereum_optimized/istanbul/state_db.py +++ /dev/null @@ -1,315 +0,0 @@ -""" -Optimized State -^^^^^^^^^^^^^^^ - -.. contents:: Table of Contents - :backlinks: none - :local: - -Introduction ------------- - -This module contains functions can be monkey patched into -`ethereum.istanbul.state` to use an optimized database backed state. -""" -import logging -from dataclasses import dataclass -from typing import Any, ClassVar, Dict, List, Optional, Set - -try: - import rust_pyspec_glue -except ImportError as e: - # Add a message, but keep it an ImportError. - raise e from Exception( - "Install with `pip install 'ethereum[optimized]'` to enable this " - "package" - ) - -from ethereum.base_types import U256, Bytes, Uint -from ethereum.istanbul.fork_types import Account, Address, Root - - -class UnmodifiedType: - """ - Sentinal type to represent a value that hasn't been modified. - """ - - pass - - -Unmodified = UnmodifiedType() - - -@dataclass -class State: - """ - The State, backed by a LMDB database. - - When created with `State()` store the db in a temporary directory. When - created with `State(path)` open or create the db located at `path`. - """ - - default_path: ClassVar[Optional[str]] = None - - db: Any - dirty_accounts: Dict[Address, Optional[Account]] - dirty_storage: Dict[Address, Dict[Bytes, U256]] - destroyed_accounts: Set[Address] - tx_restore_points: List[int] - journal: List[Any] - created_accounts: Set[Address] - - def __init__(self, path: Optional[str] = None) -> None: - logging.info("using optimized state db at %s", path) - - if path is None: - path = State.default_path - - self.db = rust_pyspec_glue.DB(path) - self.dirty_accounts = {} - self.dirty_storage = {} - self.destroyed_accounts = set() - self.tx_restore_points = [] - self.journal = [] - self.created_accounts = set() - self.db.begin_mutable() - - def __eq__(self, other: object) -> bool: - """ - Test for equality by comparing state roots. - """ - if not isinstance(other, State): - return NotImplemented - return state_root(self) == state_root(other) - - def __enter__(self) -> "State": - """Support with statements""" - return self - - def __exit__(self, *args: Any) -> None: - """Support with statements""" - close_state(self) - - -def close_state(state: State) -> None: - """Close a state, releasing all resources it holds""" - state.db.close() - state.db = None - del state.dirty_accounts - del state.dirty_storage - del state.destroyed_accounts - del state.journal - del state.created_accounts - - -def get_metadata(state: State, key: Bytes) -> Optional[Bytes]: - """Get a piece of metadata""" - return state.db.get_metadata(key) - - -def set_metadata(state: State, key: Bytes, value: Bytes) -> None: - """Set a piece of metadata""" - return state.db.set_metadata(key, value) - - -def begin_db_transaction(state: State) -> None: - """ - Start a database transaction. A transaction is automatically started when a - `State` is created. Nesting of DB transactions is not supported (unlike - non-db transactions). - - No operations are supported when not in a transaction. - """ - state.db.begin_mutable() - state.tx_restore_points = [] - state.journal = [] - - -def commit_db_transaction(state: State) -> None: - """ - Commit the current database transaction. - """ - if state.tx_restore_points: - raise Exception("In a non-db transaction") - flush(state) - state.db.commit_mutable() - - -def state_root(state: State) -> Root: - """ - See `ethereum.istanbul.state`. - """ - if state.tx_restore_points: - raise Exception("In a non-db transaction") - flush(state) - return state.db.state_root() - - -def storage_root(state: State, address: Address) -> Root: - """ - See `ethereum.istanbul.state`. - """ - if state.tx_restore_points: - raise Exception("In a non-db transaction") - flush(state) - return state.db.storage_root(address) - - -def flush(state: State) -> None: - """ - Send everything in the internal caches to the Rust layer. - """ - if state.tx_restore_points: - raise Exception("In a non-db transaction") - for address in state.destroyed_accounts: - state.db.destroy_storage(address) - for address, account in state.dirty_accounts.items(): - state.db.set_account(address, account) - for address, storage in state.dirty_storage.items(): - for key, value in storage.items(): - state.db.set_storage(address, key, value) - state.destroyed_accounts = set() - state.dirty_accounts.clear() - state.dirty_storage.clear() - - -def rollback_db_transaction(state: State) -> None: - """ - Rollback the current database transaction. - """ - if state.tx_restore_points: - raise Exception("In a non-db transaction") - state.db.rollback_mutable() - state.dirty_accounts.clear() - state.dirty_storage.clear() - state.destroyed_accounts = set() - - -def begin_transaction(state: State) -> None: - """ - See `ethereum.istanbul.state`. - """ - if not state.tx_restore_points: - flush(state) - state.tx_restore_points.append(len(state.journal)) - - -def commit_transaction(state: State) -> None: - """ - See `ethereum.istanbul.state`. - """ - state.tx_restore_points.pop() - if not state.tx_restore_points: - state.journal.clear() - state.created_accounts.clear() - flush(state) - - -def rollback_transaction(state: State) -> None: - """ - See `ethereum.istanbul.state`. - """ - restore_point = state.tx_restore_points.pop() - while len(state.journal) > restore_point: - item = state.journal.pop() - if len(item) == 3: - # Revert a storage key write - if item[2] is Unmodified: - del state.dirty_storage[item[0]][item[1]] - else: - state.dirty_storage[item[0]][item[1]] = item[2] - elif type(item[1]) is dict: - # Restore storage that was destroyed by `destroy_storage()` - state.destroyed_accounts.remove(item[0]) - state.dirty_storage[item[0]] = item[1] - else: - # Revert a change to an account - if item[1] is Unmodified: - del state.dirty_accounts[item[0]] - else: - state.dirty_accounts[item[0]] = item[1] - - if not state.tx_restore_points: - state.created_accounts.clear() - - -def get_storage(state: State, address: Address, key: Bytes) -> U256: - """ - See `ethereum.istanbul.state`. - """ - if address in state.dirty_storage and key in state.dirty_storage[address]: - return state.dirty_storage[address][key] - - if address in state.destroyed_accounts: - return U256(0) - else: - return U256(state.db.get_storage(address, key)) - - -def get_storage_original(state: State, address: Address, key: Bytes) -> U256: - """ - See `ethereum.istanbul.state`. - """ - if address in state.created_accounts: - return U256(0) - else: - return U256(state.db.get_storage(address, key)) - - -def set_storage( - state: State, address: Address, key: Bytes, value: U256 -) -> None: - """ - See `ethereum.istanbul.state`. - """ - if address not in state.dirty_accounts: - state.dirty_accounts[address] = get_account_optional(state, address) - if address not in state.dirty_storage: - state.dirty_storage[address] = {} - if key not in state.dirty_storage[address]: - state.journal.append((address, key, Unmodified)) - else: - state.journal.append((address, key, state.dirty_storage[address][key])) - state.dirty_storage[address][key] = value - - -def get_account_optional(state: State, address: Address) -> Optional[Account]: - """ - See `ethereum.istanbul.state`. - """ - if address in state.dirty_accounts: - return state.dirty_accounts[address] - account = state.db.get_account_optional(address) - if account is not None: - return Account(Uint(account[0]), U256(account[1]), account[2]) - else: - return None - - -def set_account( - state: State, address: Address, account: Optional[Account] -) -> None: - """ - See `ethereum.istanbul.state`. - """ - if address not in state.dirty_accounts: - state.journal.append((address, Unmodified)) - if address in state.dirty_accounts: - state.journal.append((address, state.dirty_accounts[address])) - state.dirty_accounts[address] = account - - -def destroy_storage(state: State, address: Address) -> None: - """ - See `ethereum.istanbul.state`. - """ - state.journal.append((address, state.dirty_storage.pop(address, {}))) - state.destroyed_accounts.add(address) - set_account(state, address, get_account_optional(state, address)) - - -def mark_account_created(state: State, address: Address) -> None: - """ - See `ethereum.frontier.state`. - """ - state.created_accounts.add(address) diff --git a/src/ethereum_optimized/london/__init__.py b/src/ethereum_optimized/london/__init__.py deleted file mode 100644 index fb65a3a562..0000000000 --- a/src/ethereum_optimized/london/__init__.py +++ /dev/null @@ -1,72 +0,0 @@ -""" -Optimized Implementations (London) -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -.. contents:: Table of Contents - :backlinks: none - :local: -""" - -from typing import Optional - - -def monkey_patch_optimized_state_db(state_path: Optional[str]) -> None: - """ - Replace the state interface with one that supports high performance - updates and storing state in a database. - - This function must be called before the state interface is imported - anywhere. - """ - import ethereum.london.state as slow_state - - from . import state_db as fast_state - - optimized_state_db_patches = { - "State": fast_state.State, - "get_account_optional": fast_state.get_account_optional, - "set_account": fast_state.set_account, - "destroy_storage": fast_state.destroy_storage, - "mark_account_created": fast_state.mark_account_created, - "get_storage": fast_state.get_storage, - "get_storage_original": fast_state.get_storage_original, - "set_storage": fast_state.set_storage, - "state_root": fast_state.state_root, - "storage_root": fast_state.storage_root, - "begin_transaction": fast_state.begin_transaction, - "rollback_transaction": fast_state.rollback_transaction, - "commit_transaction": fast_state.commit_transaction, - "close_state": fast_state.close_state, - } - - for (name, value) in optimized_state_db_patches.items(): - setattr(slow_state, name, value) - - if state_path is not None: - fast_state.State.default_path = state_path - - -def monkey_patch_optimized_spec() -> None: - """ - Replace the ethash implementation with one that supports higher - performance. - - This function must be called before the spec interface is imported - anywhere. - """ - import ethereum.london.fork as slow_spec - - from . import fork as fast_spec - - slow_spec.validate_proof_of_work = fast_spec.validate_proof_of_work - - -def monkey_patch(state_path: Optional[str]) -> None: - """ - Apply all monkey patches to swap in high performance implementations. - - This function must be called before any of the ethereum modules are - imported anywhere. - """ - monkey_patch_optimized_state_db(state_path) - monkey_patch_optimized_spec() diff --git a/src/ethereum_optimized/london/fork.py b/src/ethereum_optimized/london/fork.py deleted file mode 100644 index 0bf4418bee..0000000000 --- a/src/ethereum_optimized/london/fork.py +++ /dev/null @@ -1,47 +0,0 @@ -""" -Optimized Spec -^^^^^^^^^^^^^^ - -.. contents:: Table of Contents - :backlinks: none - :local: - -Introduction ------------- - -This module contains functions can be monkey patched into -`ethereum.london.fork` to use alternate optimized implementations. -""" -from ethereum.base_types import U256_CEIL_VALUE -from ethereum.ethash import epoch -from ethereum.exceptions import InvalidBlock -from ethereum.london.fork import generate_header_hash_for_pow -from ethereum.london.fork_types import Header -from ethereum.utils.ensure import ensure - -try: - import ethash -except ImportError as e: - # Add a message, but keep it an ImportError. - raise e from Exception( - "Install with `pip install 'ethereum[optimized]'` to enable this " - "package" - ) - - -def validate_proof_of_work(header: Header) -> None: - """ - See `ethereum.london.fork.validate_proof_of_work`. - """ - epoch_number = epoch(header.number) - header_hash = generate_header_hash_for_pow(header) - - result = ethash.verify( - int(epoch_number), - header_hash, - header.mix_digest, - int.from_bytes(header.nonce, "big"), - (U256_CEIL_VALUE // header.difficulty).to_be_bytes32(), - ) - - ensure(result, InvalidBlock) diff --git a/src/ethereum_optimized/london/state_db.py b/src/ethereum_optimized/london/state_db.py deleted file mode 100644 index cacb5e12a6..0000000000 --- a/src/ethereum_optimized/london/state_db.py +++ /dev/null @@ -1,315 +0,0 @@ -""" -Optimized State -^^^^^^^^^^^^^^^ - -.. contents:: Table of Contents - :backlinks: none - :local: - -Introduction ------------- - -This module contains functions can be monkey patched into -`ethereum.london.state` to use an optimized database backed state. -""" -import logging -from dataclasses import dataclass -from typing import Any, ClassVar, Dict, List, Optional, Set - -try: - import rust_pyspec_glue -except ImportError as e: - # Add a message, but keep it an ImportError. - raise e from Exception( - "Install with `pip install 'ethereum[optimized]'` to enable this " - "package" - ) - -from ethereum.base_types import U256, Bytes, Uint -from ethereum.london.fork_types import Account, Address, Root - - -class UnmodifiedType: - """ - Sentinal type to represent a value that hasn't been modified. - """ - - pass - - -Unmodified = UnmodifiedType() - - -@dataclass -class State: - """ - The State, backed by a LMDB database. - - When created with `State()` store the db in a temporary directory. When - created with `State(path)` open or create the db located at `path`. - """ - - default_path: ClassVar[Optional[str]] = None - - db: Any - dirty_accounts: Dict[Address, Optional[Account]] - dirty_storage: Dict[Address, Dict[Bytes, U256]] - destroyed_accounts: Set[Address] - tx_restore_points: List[int] - journal: List[Any] - created_accounts: Set[Address] - - def __init__(self, path: Optional[str] = None) -> None: - logging.info("using optimized state db at %s", path) - - if path is None: - path = State.default_path - - self.db = rust_pyspec_glue.DB(path) - self.dirty_accounts = {} - self.dirty_storage = {} - self.destroyed_accounts = set() - self.tx_restore_points = [] - self.journal = [] - self.created_accounts = set() - self.db.begin_mutable() - - def __eq__(self, other: object) -> bool: - """ - Test for equality by comparing state roots. - """ - if not isinstance(other, State): - return NotImplemented - return state_root(self) == state_root(other) - - def __enter__(self) -> "State": - """Support with statements""" - return self - - def __exit__(self, *args: Any) -> None: - """Support with statements""" - close_state(self) - - -def close_state(state: State) -> None: - """Close a state, releasing all resources it holds""" - state.db.close() - state.db = None - del state.dirty_accounts - del state.dirty_storage - del state.destroyed_accounts - del state.journal - del state.created_accounts - - -def get_metadata(state: State, key: Bytes) -> Optional[Bytes]: - """Get a piece of metadata""" - return state.db.get_metadata(key) - - -def set_metadata(state: State, key: Bytes, value: Bytes) -> None: - """Set a piece of metadata""" - return state.db.set_metadata(key, value) - - -def begin_db_transaction(state: State) -> None: - """ - Start a database transaction. A transaction is automatically started when a - `State` is created. Nesting of DB transactions is not supported (unlike - non-db transactions). - - No operations are supported when not in a transaction. - """ - state.db.begin_mutable() - state.tx_restore_points = [] - state.journal = [] - - -def commit_db_transaction(state: State) -> None: - """ - Commit the current database transaction. - """ - if state.tx_restore_points: - raise Exception("In a non-db transaction") - flush(state) - state.db.commit_mutable() - - -def state_root(state: State) -> Root: - """ - See `ethereum.london.state`. - """ - if state.tx_restore_points: - raise Exception("In a non-db transaction") - flush(state) - return state.db.state_root() - - -def storage_root(state: State, address: Address) -> Root: - """ - See `ethereum.london.state`. - """ - if state.tx_restore_points: - raise Exception("In a non-db transaction") - flush(state) - return state.db.storage_root(address) - - -def flush(state: State) -> None: - """ - Send everything in the internal caches to the Rust layer. - """ - if state.tx_restore_points: - raise Exception("In a non-db transaction") - for address in state.destroyed_accounts: - state.db.destroy_storage(address) - for address, account in state.dirty_accounts.items(): - state.db.set_account(address, account) - for address, storage in state.dirty_storage.items(): - for key, value in storage.items(): - state.db.set_storage(address, key, value) - state.destroyed_accounts = set() - state.dirty_accounts.clear() - state.dirty_storage.clear() - - -def rollback_db_transaction(state: State) -> None: - """ - Rollback the current database transaction. - """ - if state.tx_restore_points: - raise Exception("In a non-db transaction") - state.db.rollback_mutable() - state.dirty_accounts.clear() - state.dirty_storage.clear() - state.destroyed_accounts = set() - - -def begin_transaction(state: State) -> None: - """ - See `ethereum.london.state`. - """ - if not state.tx_restore_points: - flush(state) - state.tx_restore_points.append(len(state.journal)) - - -def commit_transaction(state: State) -> None: - """ - See `ethereum.london.state`. - """ - state.tx_restore_points.pop() - if not state.tx_restore_points: - state.journal.clear() - state.created_accounts.clear() - flush(state) - - -def rollback_transaction(state: State) -> None: - """ - See `ethereum.london.state`. - """ - restore_point = state.tx_restore_points.pop() - while len(state.journal) > restore_point: - item = state.journal.pop() - if len(item) == 3: - # Revert a storage key write - if item[2] is Unmodified: - del state.dirty_storage[item[0]][item[1]] - else: - state.dirty_storage[item[0]][item[1]] = item[2] - elif type(item[1]) is dict: - # Restore storage that was destroyed by `destroy_storage()` - state.destroyed_accounts.remove(item[0]) - state.dirty_storage[item[0]] = item[1] - else: - # Revert a change to an account - if item[1] is Unmodified: - del state.dirty_accounts[item[0]] - else: - state.dirty_accounts[item[0]] = item[1] - - if not state.tx_restore_points: - state.created_accounts.clear() - - -def get_storage(state: State, address: Address, key: Bytes) -> U256: - """ - See `ethereum.london.state`. - """ - if address in state.dirty_storage and key in state.dirty_storage[address]: - return state.dirty_storage[address][key] - - if address in state.destroyed_accounts: - return U256(0) - else: - return U256(state.db.get_storage(address, key)) - - -def get_storage_original(state: State, address: Address, key: Bytes) -> U256: - """ - See `ethereum.istanbul.state`. - """ - if address in state.created_accounts: - return U256(0) - else: - return U256(state.db.get_storage(address, key)) - - -def set_storage( - state: State, address: Address, key: Bytes, value: U256 -) -> None: - """ - See `ethereum.london.state`. - """ - if address not in state.dirty_accounts: - state.dirty_accounts[address] = get_account_optional(state, address) - if address not in state.dirty_storage: - state.dirty_storage[address] = {} - if key not in state.dirty_storage[address]: - state.journal.append((address, key, Unmodified)) - else: - state.journal.append((address, key, state.dirty_storage[address][key])) - state.dirty_storage[address][key] = value - - -def get_account_optional(state: State, address: Address) -> Optional[Account]: - """ - See `ethereum.london.state`. - """ - if address in state.dirty_accounts: - return state.dirty_accounts[address] - account = state.db.get_account_optional(address) - if account is not None: - return Account(Uint(account[0]), U256(account[1]), account[2]) - else: - return None - - -def set_account( - state: State, address: Address, account: Optional[Account] -) -> None: - """ - See `ethereum.london.state`. - """ - if address not in state.dirty_accounts: - state.journal.append((address, Unmodified)) - if address in state.dirty_accounts: - state.journal.append((address, state.dirty_accounts[address])) - state.dirty_accounts[address] = account - - -def destroy_storage(state: State, address: Address) -> None: - """ - See `ethereum.london.state`. - """ - state.journal.append((address, state.dirty_storage.pop(address, {}))) - state.destroyed_accounts.add(address) - set_account(state, address, get_account_optional(state, address)) - - -def mark_account_created(state: State, address: Address) -> None: - """ - See `ethereum.frontier.state`. - """ - state.created_accounts.add(address) diff --git a/src/ethereum_optimized/muir_glacier/__init__.py b/src/ethereum_optimized/muir_glacier/__init__.py deleted file mode 100644 index 4bbe562dae..0000000000 --- a/src/ethereum_optimized/muir_glacier/__init__.py +++ /dev/null @@ -1,72 +0,0 @@ -""" -Optimized Implementations (Muir Glacier) -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -.. contents:: Table of Contents - :backlinks: none - :local: -""" - -from typing import Optional - - -def monkey_patch_optimized_state_db(state_path: Optional[str]) -> None: - """ - Replace the state interface with one that supports high performance - updates and storing state in a database. - - This function must be called before the state interface is imported - anywhere. - """ - import ethereum.muir_glacier.state as slow_state - - from . import state_db as fast_state - - optimized_state_db_patches = { - "State": fast_state.State, - "get_account_optional": fast_state.get_account_optional, - "set_account": fast_state.set_account, - "destroy_storage": fast_state.destroy_storage, - "mark_account_created": fast_state.mark_account_created, - "get_storage": fast_state.get_storage, - "get_storage_original": fast_state.get_storage_original, - "set_storage": fast_state.set_storage, - "state_root": fast_state.state_root, - "storage_root": fast_state.storage_root, - "begin_transaction": fast_state.begin_transaction, - "rollback_transaction": fast_state.rollback_transaction, - "commit_transaction": fast_state.commit_transaction, - "close_state": fast_state.close_state, - } - - for (name, value) in optimized_state_db_patches.items(): - setattr(slow_state, name, value) - - if state_path is not None: - fast_state.State.default_path = state_path - - -def monkey_patch_optimized_spec() -> None: - """ - Replace the ethash implementation with one that supports higher - performance. - - This function must be called before the spec interface is imported - anywhere. - """ - import ethereum.muir_glacier.fork as slow_spec - - from . import fork as fast_spec - - slow_spec.validate_proof_of_work = fast_spec.validate_proof_of_work - - -def monkey_patch(state_path: Optional[str]) -> None: - """ - Apply all monkey patches to swap in high performance implementations. - - This function must be called before any of the ethereum modules are - imported anywhere. - """ - monkey_patch_optimized_state_db(state_path) - monkey_patch_optimized_spec() diff --git a/src/ethereum_optimized/muir_glacier/fork.py b/src/ethereum_optimized/muir_glacier/fork.py deleted file mode 100644 index 5200614aea..0000000000 --- a/src/ethereum_optimized/muir_glacier/fork.py +++ /dev/null @@ -1,47 +0,0 @@ -""" -Optimized Spec -^^^^^^^^^^^^^^ - -.. contents:: Table of Contents - :backlinks: none - :local: - -Introduction ------------- - -This module contains functions can be monkey patched into -`ethereum.muir_glacier.fork` to use alternate optimized implementations. -""" -from ethereum.base_types import U256_CEIL_VALUE -from ethereum.ethash import epoch -from ethereum.exceptions import InvalidBlock -from ethereum.muir_glacier.fork import generate_header_hash_for_pow -from ethereum.muir_glacier.fork_types import Header -from ethereum.utils.ensure import ensure - -try: - import ethash -except ImportError as e: - # Add a message, but keep it an ImportError. - raise e from Exception( - "Install with `pip install 'ethereum[optimized]'` to enable this " - "package" - ) - - -def validate_proof_of_work(header: Header) -> None: - """ - See `ethereum.muir_glacier.fork.validate_proof_of_work`. - """ - epoch_number = epoch(header.number) - header_hash = generate_header_hash_for_pow(header) - - result = ethash.verify( - int(epoch_number), - header_hash, - header.mix_digest, - int.from_bytes(header.nonce, "big"), - (U256_CEIL_VALUE // header.difficulty).to_be_bytes32(), - ) - - ensure(result, InvalidBlock) diff --git a/src/ethereum_optimized/muir_glacier/state_db.py b/src/ethereum_optimized/muir_glacier/state_db.py deleted file mode 100644 index 37c206429d..0000000000 --- a/src/ethereum_optimized/muir_glacier/state_db.py +++ /dev/null @@ -1,315 +0,0 @@ -""" -Optimized State -^^^^^^^^^^^^^^^ - -.. contents:: Table of Contents - :backlinks: none - :local: - -Introduction ------------- - -This module contains functions can be monkey patched into -`ethereum.muir_glacier.state` to use an optimized database backed state. -""" -import logging -from dataclasses import dataclass -from typing import Any, ClassVar, Dict, List, Optional, Set - -try: - import rust_pyspec_glue -except ImportError as e: - # Add a message, but keep it an ImportError. - raise e from Exception( - "Install with `pip install 'ethereum[optimized]'` to enable this " - "package" - ) - -from ethereum.base_types import U256, Bytes, Uint -from ethereum.muir_glacier.fork_types import Account, Address, Root - - -class UnmodifiedType: - """ - Sentinal type to represent a value that hasn't been modified. - """ - - pass - - -Unmodified = UnmodifiedType() - - -@dataclass -class State: - """ - The State, backed by a LMDB database. - - When created with `State()` store the db in a temporary directory. When - created with `State(path)` open or create the db located at `path`. - """ - - default_path: ClassVar[Optional[str]] = None - - db: Any - dirty_accounts: Dict[Address, Optional[Account]] - dirty_storage: Dict[Address, Dict[Bytes, U256]] - destroyed_accounts: Set[Address] - tx_restore_points: List[int] - journal: List[Any] - created_accounts: Set[Address] - - def __init__(self, path: Optional[str] = None) -> None: - logging.info("using optimized state db at %s", path) - - if path is None: - path = State.default_path - - self.db = rust_pyspec_glue.DB(path) - self.dirty_accounts = {} - self.dirty_storage = {} - self.destroyed_accounts = set() - self.tx_restore_points = [] - self.journal = [] - self.created_accounts = set() - self.db.begin_mutable() - - def __eq__(self, other: object) -> bool: - """ - Test for equality by comparing state roots. - """ - if not isinstance(other, State): - return NotImplemented - return state_root(self) == state_root(other) - - def __enter__(self) -> "State": - """Support with statements""" - return self - - def __exit__(self, *args: Any) -> None: - """Support with statements""" - close_state(self) - - -def close_state(state: State) -> None: - """Close a state, releasing all resources it holds""" - state.db.close() - state.db = None - del state.dirty_accounts - del state.dirty_storage - del state.destroyed_accounts - del state.journal - del state.created_accounts - - -def get_metadata(state: State, key: Bytes) -> Optional[Bytes]: - """Get a piece of metadata""" - return state.db.get_metadata(key) - - -def set_metadata(state: State, key: Bytes, value: Bytes) -> None: - """Set a piece of metadata""" - return state.db.set_metadata(key, value) - - -def begin_db_transaction(state: State) -> None: - """ - Start a database transaction. A transaction is automatically started when a - `State` is created. Nesting of DB transactions is not supported (unlike - non-db transactions). - - No operations are supported when not in a transaction. - """ - state.db.begin_mutable() - state.tx_restore_points = [] - state.journal = [] - - -def commit_db_transaction(state: State) -> None: - """ - Commit the current database transaction. - """ - if state.tx_restore_points: - raise Exception("In a non-db transaction") - flush(state) - state.db.commit_mutable() - - -def state_root(state: State) -> Root: - """ - See `ethereum.muir_glacier.state`. - """ - if state.tx_restore_points: - raise Exception("In a non-db transaction") - flush(state) - return state.db.state_root() - - -def storage_root(state: State, address: Address) -> Root: - """ - See `ethereum.muir_glacier.state`. - """ - if state.tx_restore_points: - raise Exception("In a non-db transaction") - flush(state) - return state.db.storage_root(address) - - -def flush(state: State) -> None: - """ - Send everything in the internal caches to the Rust layer. - """ - if state.tx_restore_points: - raise Exception("In a non-db transaction") - for address in state.destroyed_accounts: - state.db.destroy_storage(address) - for address, account in state.dirty_accounts.items(): - state.db.set_account(address, account) - for address, storage in state.dirty_storage.items(): - for key, value in storage.items(): - state.db.set_storage(address, key, value) - state.destroyed_accounts = set() - state.dirty_accounts.clear() - state.dirty_storage.clear() - - -def rollback_db_transaction(state: State) -> None: - """ - Rollback the current database transaction. - """ - if state.tx_restore_points: - raise Exception("In a non-db transaction") - state.db.rollback_mutable() - state.dirty_accounts.clear() - state.dirty_storage.clear() - state.destroyed_accounts = set() - - -def begin_transaction(state: State) -> None: - """ - See `ethereum.muir_glacier.state`. - """ - if not state.tx_restore_points: - flush(state) - state.tx_restore_points.append(len(state.journal)) - - -def commit_transaction(state: State) -> None: - """ - See `ethereum.muir_glacier.state`. - """ - state.tx_restore_points.pop() - if not state.tx_restore_points: - state.journal.clear() - state.created_accounts.clear() - flush(state) - - -def rollback_transaction(state: State) -> None: - """ - See `ethereum.muir_glacier.state`. - """ - restore_point = state.tx_restore_points.pop() - while len(state.journal) > restore_point: - item = state.journal.pop() - if len(item) == 3: - # Revert a storage key write - if item[2] is Unmodified: - del state.dirty_storage[item[0]][item[1]] - else: - state.dirty_storage[item[0]][item[1]] = item[2] - elif type(item[1]) is dict: - # Restore storage that was destroyed by `destroy_storage()` - state.destroyed_accounts.remove(item[0]) - state.dirty_storage[item[0]] = item[1] - else: - # Revert a change to an account - if item[1] is Unmodified: - del state.dirty_accounts[item[0]] - else: - state.dirty_accounts[item[0]] = item[1] - - if not state.tx_restore_points: - state.created_accounts.clear() - - -def get_storage(state: State, address: Address, key: Bytes) -> U256: - """ - See `ethereum.muir_glacier.state`. - """ - if address in state.dirty_storage and key in state.dirty_storage[address]: - return state.dirty_storage[address][key] - - if address in state.destroyed_accounts: - return U256(0) - else: - return U256(state.db.get_storage(address, key)) - - -def get_storage_original(state: State, address: Address, key: Bytes) -> U256: - """ - See `ethereum.istanbul.state`. - """ - if address in state.created_accounts: - return U256(0) - else: - return U256(state.db.get_storage(address, key)) - - -def set_storage( - state: State, address: Address, key: Bytes, value: U256 -) -> None: - """ - See `ethereum.muir_glacier.state`. - """ - if address not in state.dirty_accounts: - state.dirty_accounts[address] = get_account_optional(state, address) - if address not in state.dirty_storage: - state.dirty_storage[address] = {} - if key not in state.dirty_storage[address]: - state.journal.append((address, key, Unmodified)) - else: - state.journal.append((address, key, state.dirty_storage[address][key])) - state.dirty_storage[address][key] = value - - -def get_account_optional(state: State, address: Address) -> Optional[Account]: - """ - See `ethereum.muir_glacier.state`. - """ - if address in state.dirty_accounts: - return state.dirty_accounts[address] - account = state.db.get_account_optional(address) - if account is not None: - return Account(Uint(account[0]), U256(account[1]), account[2]) - else: - return None - - -def set_account( - state: State, address: Address, account: Optional[Account] -) -> None: - """ - See `ethereum.muir_glacier.state`. - """ - if address not in state.dirty_accounts: - state.journal.append((address, Unmodified)) - if address in state.dirty_accounts: - state.journal.append((address, state.dirty_accounts[address])) - state.dirty_accounts[address] = account - - -def destroy_storage(state: State, address: Address) -> None: - """ - See `ethereum.muir_glacier.state`. - """ - state.journal.append((address, state.dirty_storage.pop(address, {}))) - state.destroyed_accounts.add(address) - set_account(state, address, get_account_optional(state, address)) - - -def mark_account_created(state: State, address: Address) -> None: - """ - See `ethereum.frontier.state`. - """ - state.created_accounts.add(address) diff --git a/src/ethereum_optimized/paris/__init__.py b/src/ethereum_optimized/paris/__init__.py deleted file mode 100644 index d86ad76583..0000000000 --- a/src/ethereum_optimized/paris/__init__.py +++ /dev/null @@ -1,56 +0,0 @@ -""" -Optimized Implementations (London) -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -.. contents:: Table of Contents - :backlinks: none - :local: -""" - -from typing import Optional - - -def monkey_patch_optimized_state_db(state_path: Optional[str]) -> None: - """ - Replace the state interface with one that supports high performance - updates and storing state in a database. - - This function must be called before the state interface is imported - anywhere. - """ - import ethereum.paris.state as slow_state - - from . import state_db as fast_state - - optimized_state_db_patches = { - "State": fast_state.State, - "get_account_optional": fast_state.get_account_optional, - "set_account": fast_state.set_account, - "destroy_storage": fast_state.destroy_storage, - "mark_account_created": fast_state.mark_account_created, - "get_storage": fast_state.get_storage, - "get_storage_original": fast_state.get_storage_original, - "set_storage": fast_state.set_storage, - "state_root": fast_state.state_root, - "storage_root": fast_state.storage_root, - "begin_transaction": fast_state.begin_transaction, - "rollback_transaction": fast_state.rollback_transaction, - "commit_transaction": fast_state.commit_transaction, - "close_state": fast_state.close_state, - } - - for (name, value) in optimized_state_db_patches.items(): - setattr(slow_state, name, value) - - if state_path is not None: - fast_state.State.default_path = state_path - - -def monkey_patch(state_path: Optional[str]) -> None: - """ - Apply all monkey patches to swap in high performance implementations. - - This function must be called before any of the ethereum modules are - imported anywhere. - """ - monkey_patch_optimized_state_db(state_path) diff --git a/src/ethereum_optimized/paris/state_db.py b/src/ethereum_optimized/paris/state_db.py deleted file mode 100644 index 0aa9f60358..0000000000 --- a/src/ethereum_optimized/paris/state_db.py +++ /dev/null @@ -1,315 +0,0 @@ -""" -Optimized State -^^^^^^^^^^^^^^^ - -.. contents:: Table of Contents - :backlinks: none - :local: - -Introduction ------------- - -This module contains functions can be monkey patched into -`ethereum.paris.state` to use an optimized database backed state. -""" -import logging -from dataclasses import dataclass -from typing import Any, ClassVar, Dict, List, Optional, Set - -try: - import rust_pyspec_glue -except ImportError as e: - # Add a message, but keep it an ImportError. - raise e from Exception( - "Install with `pip install 'ethereum[optimized]'` to enable this " - "package" - ) - -from ethereum.base_types import U256, Bytes, Uint -from ethereum.paris.fork_types import Account, Address, Root - - -class UnmodifiedType: - """ - Sentinal type to represent a value that hasn't been modified. - """ - - pass - - -Unmodified = UnmodifiedType() - - -@dataclass -class State: - """ - The State, backed by a LMDB database. - - When created with `State()` store the db in a temporary directory. When - created with `State(path)` open or create the db located at `path`. - """ - - default_path: ClassVar[Optional[str]] = None - - db: Any - dirty_accounts: Dict[Address, Optional[Account]] - dirty_storage: Dict[Address, Dict[Bytes, U256]] - destroyed_accounts: Set[Address] - tx_restore_points: List[int] - journal: List[Any] - created_accounts: Set[Address] - - def __init__(self, path: Optional[str] = None) -> None: - logging.info("using optimized state db at %s", path) - - if path is None: - path = State.default_path - - self.db = rust_pyspec_glue.DB(path) - self.dirty_accounts = {} - self.dirty_storage = {} - self.destroyed_accounts = set() - self.tx_restore_points = [] - self.journal = [] - self.created_accounts = set() - self.db.begin_mutable() - - def __eq__(self, other: object) -> bool: - """ - Test for equality by comparing state roots. - """ - if not isinstance(other, State): - return NotImplemented - return state_root(self) == state_root(other) - - def __enter__(self) -> "State": - """Support with statements""" - return self - - def __exit__(self, *args: Any) -> None: - """Support with statements""" - close_state(self) - - -def close_state(state: State) -> None: - """Close a state, releasing all resources it holds""" - state.db.close() - state.db = None - del state.dirty_accounts - del state.dirty_storage - del state.destroyed_accounts - del state.journal - del state.created_accounts - - -def get_metadata(state: State, key: Bytes) -> Optional[Bytes]: - """Get a piece of metadata""" - return state.db.get_metadata(key) - - -def set_metadata(state: State, key: Bytes, value: Bytes) -> None: - """Set a piece of metadata""" - return state.db.set_metadata(key, value) - - -def begin_db_transaction(state: State) -> None: - """ - Start a database transaction. A transaction is automatically started when a - `State` is created. Nesting of DB transactions is not supported (unlike - non-db transactions). - - No operations are supported when not in a transaction. - """ - state.db.begin_mutable() - state.tx_restore_points = [] - state.journal = [] - - -def commit_db_transaction(state: State) -> None: - """ - Commit the current database transaction. - """ - if state.tx_restore_points: - raise Exception("In a non-db transaction") - flush(state) - state.db.commit_mutable() - - -def state_root(state: State) -> Root: - """ - See `ethereum.paris.state`. - """ - if state.tx_restore_points: - raise Exception("In a non-db transaction") - flush(state) - return state.db.state_root() - - -def storage_root(state: State, address: Address) -> Root: - """ - See `ethereum.paris.state`. - """ - if state.tx_restore_points: - raise Exception("In a non-db transaction") - flush(state) - return state.db.storage_root(address) - - -def flush(state: State) -> None: - """ - Send everything in the internal caches to the Rust layer. - """ - if state.tx_restore_points: - raise Exception("In a non-db transaction") - for address in state.destroyed_accounts: - state.db.destroy_storage(address) - for address, account in state.dirty_accounts.items(): - state.db.set_account(address, account) - for address, storage in state.dirty_storage.items(): - for key, value in storage.items(): - state.db.set_storage(address, key, value) - state.destroyed_accounts = set() - state.dirty_accounts.clear() - state.dirty_storage.clear() - - -def rollback_db_transaction(state: State) -> None: - """ - Rollback the current database transaction. - """ - if state.tx_restore_points: - raise Exception("In a non-db transaction") - state.db.rollback_mutable() - state.dirty_accounts.clear() - state.dirty_storage.clear() - state.destroyed_accounts = set() - - -def begin_transaction(state: State) -> None: - """ - See `ethereum.paris.state`. - """ - if not state.tx_restore_points: - flush(state) - state.tx_restore_points.append(len(state.journal)) - - -def commit_transaction(state: State) -> None: - """ - See `ethereum.paris.state`. - """ - state.tx_restore_points.pop() - if not state.tx_restore_points: - state.journal.clear() - state.created_accounts.clear() - flush(state) - - -def rollback_transaction(state: State) -> None: - """ - See `ethereum.paris.state`. - """ - restore_point = state.tx_restore_points.pop() - while len(state.journal) > restore_point: - item = state.journal.pop() - if len(item) == 3: - # Revert a storage key write - if item[2] is Unmodified: - del state.dirty_storage[item[0]][item[1]] - else: - state.dirty_storage[item[0]][item[1]] = item[2] - elif type(item[1]) is dict: - # Restore storage that was destroyed by `destroy_storage()` - state.destroyed_accounts.remove(item[0]) - state.dirty_storage[item[0]] = item[1] - else: - # Revert a change to an account - if item[1] is Unmodified: - del state.dirty_accounts[item[0]] - else: - state.dirty_accounts[item[0]] = item[1] - - if not state.tx_restore_points: - state.created_accounts.clear() - - -def get_storage(state: State, address: Address, key: Bytes) -> U256: - """ - See `ethereum.paris.state`. - """ - if address in state.dirty_storage and key in state.dirty_storage[address]: - return state.dirty_storage[address][key] - - if address in state.destroyed_accounts: - return U256(0) - else: - return U256(state.db.get_storage(address, key)) - - -def get_storage_original(state: State, address: Address, key: Bytes) -> U256: - """ - See `ethereum.istanbul.state`. - """ - if address in state.created_accounts: - return U256(0) - else: - return U256(state.db.get_storage(address, key)) - - -def set_storage( - state: State, address: Address, key: Bytes, value: U256 -) -> None: - """ - See `ethereum.paris.state`. - """ - if address not in state.dirty_accounts: - state.dirty_accounts[address] = get_account_optional(state, address) - if address not in state.dirty_storage: - state.dirty_storage[address] = {} - if key not in state.dirty_storage[address]: - state.journal.append((address, key, Unmodified)) - else: - state.journal.append((address, key, state.dirty_storage[address][key])) - state.dirty_storage[address][key] = value - - -def get_account_optional(state: State, address: Address) -> Optional[Account]: - """ - See `ethereum.paris.state`. - """ - if address in state.dirty_accounts: - return state.dirty_accounts[address] - account = state.db.get_account_optional(address) - if account is not None: - return Account(Uint(account[0]), U256(account[1]), account[2]) - else: - return None - - -def set_account( - state: State, address: Address, account: Optional[Account] -) -> None: - """ - See `ethereum.paris.state`. - """ - if address not in state.dirty_accounts: - state.journal.append((address, Unmodified)) - if address in state.dirty_accounts: - state.journal.append((address, state.dirty_accounts[address])) - state.dirty_accounts[address] = account - - -def destroy_storage(state: State, address: Address) -> None: - """ - See `ethereum.paris.state`. - """ - state.journal.append((address, state.dirty_storage.pop(address, {}))) - state.destroyed_accounts.add(address) - set_account(state, address, get_account_optional(state, address)) - - -def mark_account_created(state: State, address: Address) -> None: - """ - See `ethereum.frontier.state`. - """ - state.created_accounts.add(address) diff --git a/src/ethereum_optimized/shanghai/__init__.py b/src/ethereum_optimized/shanghai/__init__.py deleted file mode 100644 index b13cc1a93b..0000000000 --- a/src/ethereum_optimized/shanghai/__init__.py +++ /dev/null @@ -1,56 +0,0 @@ -""" -Optimized Implementations (London) -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -.. contents:: Table of Contents - :backlinks: none - :local: -""" - -from typing import Optional - - -def monkey_patch_optimized_state_db(state_path: Optional[str]) -> None: - """ - Replace the state interface with one that supports high performance - updates and storing state in a database. - - This function must be called before the state interface is imported - anywhere. - """ - import ethereum.shanghai.state as slow_state - - from . import state_db as fast_state - - optimized_state_db_patches = { - "State": fast_state.State, - "get_account_optional": fast_state.get_account_optional, - "set_account": fast_state.set_account, - "destroy_storage": fast_state.destroy_storage, - "mark_account_created": fast_state.mark_account_created, - "get_storage": fast_state.get_storage, - "get_storage_original": fast_state.get_storage_original, - "set_storage": fast_state.set_storage, - "state_root": fast_state.state_root, - "storage_root": fast_state.storage_root, - "begin_transaction": fast_state.begin_transaction, - "rollback_transaction": fast_state.rollback_transaction, - "commit_transaction": fast_state.commit_transaction, - "close_state": fast_state.close_state, - } - - for (name, value) in optimized_state_db_patches.items(): - setattr(slow_state, name, value) - - if state_path is not None: - fast_state.State.default_path = state_path - - -def monkey_patch(state_path: Optional[str]) -> None: - """ - Apply all monkey patches to swap in high performance implementations. - - This function must be called before any of the ethereum modules are - imported anywhere. - """ - monkey_patch_optimized_state_db(state_path) diff --git a/src/ethereum_optimized/shanghai/state_db.py b/src/ethereum_optimized/shanghai/state_db.py deleted file mode 100644 index 9d01ed0992..0000000000 --- a/src/ethereum_optimized/shanghai/state_db.py +++ /dev/null @@ -1,315 +0,0 @@ -""" -Optimized State -^^^^^^^^^^^^^^^ - -.. contents:: Table of Contents - :backlinks: none - :local: - -Introduction ------------- - -This module contains functions can be monkey patched into -`ethereum.shanghai.state` to use an optimized database backed state. -""" -import logging -from dataclasses import dataclass -from typing import Any, ClassVar, Dict, List, Optional, Set - -try: - import rust_pyspec_glue -except ImportError as e: - # Add a message, but keep it an ImportError. - raise e from Exception( - "Install with `pip install 'ethereum[optimized]'` to enable this " - "package" - ) - -from ethereum.base_types import U256, Bytes, Uint -from ethereum.shanghai.fork_types import Account, Address, Root - - -class UnmodifiedType: - """ - Sentinal type to represent a value that hasn't been modified. - """ - - pass - - -Unmodified = UnmodifiedType() - - -@dataclass -class State: - """ - The State, backed by a LMDB database. - - When created with `State()` store the db in a temporary directory. When - created with `State(path)` open or create the db located at `path`. - """ - - default_path: ClassVar[Optional[str]] = None - - db: Any - dirty_accounts: Dict[Address, Optional[Account]] - dirty_storage: Dict[Address, Dict[Bytes, U256]] - destroyed_accounts: Set[Address] - tx_restore_points: List[int] - journal: List[Any] - created_accounts: Set[Address] - - def __init__(self, path: Optional[str] = None) -> None: - logging.info("using optimized state db at %s", path) - - if path is None: - path = State.default_path - - self.db = rust_pyspec_glue.DB(path) - self.dirty_accounts = {} - self.dirty_storage = {} - self.destroyed_accounts = set() - self.tx_restore_points = [] - self.journal = [] - self.created_accounts = set() - self.db.begin_mutable() - - def __eq__(self, other: object) -> bool: - """ - Test for equality by comparing state roots. - """ - if not isinstance(other, State): - return NotImplemented - return state_root(self) == state_root(other) - - def __enter__(self) -> "State": - """Support with statements""" - return self - - def __exit__(self, *args: Any) -> None: - """Support with statements""" - close_state(self) - - -def close_state(state: State) -> None: - """Close a state, releasing all resources it holds""" - state.db.close() - state.db = None - del state.dirty_accounts - del state.dirty_storage - del state.destroyed_accounts - del state.journal - del state.created_accounts - - -def get_metadata(state: State, key: Bytes) -> Optional[Bytes]: - """Get a piece of metadata""" - return state.db.get_metadata(key) - - -def set_metadata(state: State, key: Bytes, value: Bytes) -> None: - """Set a piece of metadata""" - return state.db.set_metadata(key, value) - - -def begin_db_transaction(state: State) -> None: - """ - Start a database transaction. A transaction is automatically started when a - `State` is created. Nesting of DB transactions is not supported (unlike - non-db transactions). - - No operations are supported when not in a transaction. - """ - state.db.begin_mutable() - state.tx_restore_points = [] - state.journal = [] - - -def commit_db_transaction(state: State) -> None: - """ - Commit the current database transaction. - """ - if state.tx_restore_points: - raise Exception("In a non-db transaction") - flush(state) - state.db.commit_mutable() - - -def state_root(state: State) -> Root: - """ - See `ethereum.shanghai.state`. - """ - if state.tx_restore_points: - raise Exception("In a non-db transaction") - flush(state) - return state.db.state_root() - - -def storage_root(state: State, address: Address) -> Root: - """ - See `ethereum.shanghai.state`. - """ - if state.tx_restore_points: - raise Exception("In a non-db transaction") - flush(state) - return state.db.storage_root(address) - - -def flush(state: State) -> None: - """ - Send everything in the internal caches to the Rust layer. - """ - if state.tx_restore_points: - raise Exception("In a non-db transaction") - for address in state.destroyed_accounts: - state.db.destroy_storage(address) - for address, account in state.dirty_accounts.items(): - state.db.set_account(address, account) - for address, storage in state.dirty_storage.items(): - for key, value in storage.items(): - state.db.set_storage(address, key, value) - state.destroyed_accounts = set() - state.dirty_accounts.clear() - state.dirty_storage.clear() - - -def rollback_db_transaction(state: State) -> None: - """ - Rollback the current database transaction. - """ - if state.tx_restore_points: - raise Exception("In a non-db transaction") - state.db.rollback_mutable() - state.dirty_accounts.clear() - state.dirty_storage.clear() - state.destroyed_accounts = set() - - -def begin_transaction(state: State) -> None: - """ - See `ethereum.shanghai.state`. - """ - if not state.tx_restore_points: - flush(state) - state.tx_restore_points.append(len(state.journal)) - - -def commit_transaction(state: State) -> None: - """ - See `ethereum.shanghai.state`. - """ - state.tx_restore_points.pop() - if not state.tx_restore_points: - state.journal.clear() - state.created_accounts.clear() - flush(state) - - -def rollback_transaction(state: State) -> None: - """ - See `ethereum.shanghai.state`. - """ - restore_point = state.tx_restore_points.pop() - while len(state.journal) > restore_point: - item = state.journal.pop() - if len(item) == 3: - # Revert a storage key write - if item[2] is Unmodified: - del state.dirty_storage[item[0]][item[1]] - else: - state.dirty_storage[item[0]][item[1]] = item[2] - elif type(item[1]) is dict: - # Restore storage that was destroyed by `destroy_storage()` - state.destroyed_accounts.remove(item[0]) - state.dirty_storage[item[0]] = item[1] - else: - # Revert a change to an account - if item[1] is Unmodified: - del state.dirty_accounts[item[0]] - else: - state.dirty_accounts[item[0]] = item[1] - - if not state.tx_restore_points: - state.created_accounts.clear() - - -def get_storage(state: State, address: Address, key: Bytes) -> U256: - """ - See `ethereum.shanghai.state`. - """ - if address in state.dirty_storage and key in state.dirty_storage[address]: - return state.dirty_storage[address][key] - - if address in state.destroyed_accounts: - return U256(0) - else: - return U256(state.db.get_storage(address, key)) - - -def get_storage_original(state: State, address: Address, key: Bytes) -> U256: - """ - See `ethereum.istanbul.state`. - """ - if address in state.created_accounts: - return U256(0) - else: - return U256(state.db.get_storage(address, key)) - - -def set_storage( - state: State, address: Address, key: Bytes, value: U256 -) -> None: - """ - See `ethereum.shanghai.state`. - """ - if address not in state.dirty_accounts: - state.dirty_accounts[address] = get_account_optional(state, address) - if address not in state.dirty_storage: - state.dirty_storage[address] = {} - if key not in state.dirty_storage[address]: - state.journal.append((address, key, Unmodified)) - else: - state.journal.append((address, key, state.dirty_storage[address][key])) - state.dirty_storage[address][key] = value - - -def get_account_optional(state: State, address: Address) -> Optional[Account]: - """ - See `ethereum.shanghai.state`. - """ - if address in state.dirty_accounts: - return state.dirty_accounts[address] - account = state.db.get_account_optional(address) - if account is not None: - return Account(Uint(account[0]), U256(account[1]), account[2]) - else: - return None - - -def set_account( - state: State, address: Address, account: Optional[Account] -) -> None: - """ - See `ethereum.shanghai.state`. - """ - if address not in state.dirty_accounts: - state.journal.append((address, Unmodified)) - if address in state.dirty_accounts: - state.journal.append((address, state.dirty_accounts[address])) - state.dirty_accounts[address] = account - - -def destroy_storage(state: State, address: Address) -> None: - """ - See `ethereum.shanghai.state`. - """ - state.journal.append((address, state.dirty_storage.pop(address, {}))) - state.destroyed_accounts.add(address) - set_account(state, address, get_account_optional(state, address)) - - -def mark_account_created(state: State, address: Address) -> None: - """ - See `ethereum.frontier.state`. - """ - state.created_accounts.add(address) diff --git a/src/ethereum_optimized/spurious_dragon/__init__.py b/src/ethereum_optimized/spurious_dragon/__init__.py deleted file mode 100644 index 29cb674534..0000000000 --- a/src/ethereum_optimized/spurious_dragon/__init__.py +++ /dev/null @@ -1,72 +0,0 @@ -""" -Optimized Implementations (Spurious Dragon) -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -.. contents:: Table of Contents - :backlinks: none - :local: -""" - -from typing import Optional - - -def monkey_patch_optimized_state_db(state_path: Optional[str]) -> None: - """ - Replace the state interface with one that supports high performance - updates and storing state in a database. - - This function must be called before the state interface is imported - anywhere. - """ - import ethereum.spurious_dragon.state as slow_state - - from . import state_db as fast_state - - optimized_state_db_patches = { - "State": fast_state.State, - "get_account_optional": fast_state.get_account_optional, - "set_account": fast_state.set_account, - "destroy_storage": fast_state.destroy_storage, - "mark_account_created": fast_state.mark_account_created, - "get_storage": fast_state.get_storage, - "get_storage_original": fast_state.get_storage_original, - "set_storage": fast_state.set_storage, - "state_root": fast_state.state_root, - "storage_root": fast_state.storage_root, - "begin_transaction": fast_state.begin_transaction, - "rollback_transaction": fast_state.rollback_transaction, - "commit_transaction": fast_state.commit_transaction, - "close_state": fast_state.close_state, - } - - for (name, value) in optimized_state_db_patches.items(): - setattr(slow_state, name, value) - - if state_path is not None: - fast_state.State.default_path = state_path - - -def monkey_patch_optimized_spec() -> None: - """ - Replace the ethash implementation with one that supports higher - performance. - - This function must be called before the spec interface is imported - anywhere. - """ - import ethereum.spurious_dragon.fork as slow_spec - - from . import fork as fast_spec - - slow_spec.validate_proof_of_work = fast_spec.validate_proof_of_work - - -def monkey_patch(state_path: Optional[str]) -> None: - """ - Apply all monkey patches to swap in high performance implementations. - - This function must be called before any of the ethereum modules are - imported anywhere. - """ - monkey_patch_optimized_state_db(state_path) - monkey_patch_optimized_spec() diff --git a/src/ethereum_optimized/spurious_dragon/fork.py b/src/ethereum_optimized/spurious_dragon/fork.py deleted file mode 100644 index b590fad44c..0000000000 --- a/src/ethereum_optimized/spurious_dragon/fork.py +++ /dev/null @@ -1,47 +0,0 @@ -""" -Optimized Spec -^^^^^^^^^^^^^^ - -.. contents:: Table of Contents - :backlinks: none - :local: - -Introduction ------------- - -This module contains functions can be monkey patched into -`ethereum.spurious_dragon.fork` to use alternate optimized implementations. -""" -from ethereum.base_types import U256_CEIL_VALUE -from ethereum.ethash import epoch -from ethereum.exceptions import InvalidBlock -from ethereum.spurious_dragon.fork import generate_header_hash_for_pow -from ethereum.spurious_dragon.fork_types import Header -from ethereum.utils.ensure import ensure - -try: - import ethash -except ImportError as e: - # Add a message, but keep it an ImportError. - raise e from Exception( - "Install with `pip install 'ethereum[optimized]'` to enable this " - "package" - ) - - -def validate_proof_of_work(header: Header) -> None: - """ - See `ethereum.spurious_dragon.fork.validate_proof_of_work`. - """ - epoch_number = epoch(header.number) - header_hash = generate_header_hash_for_pow(header) - - result = ethash.verify( - int(epoch_number), - header_hash, - header.mix_digest, - int.from_bytes(header.nonce, "big"), - (U256_CEIL_VALUE // header.difficulty).to_be_bytes32(), - ) - - ensure(result, InvalidBlock) diff --git a/src/ethereum_optimized/spurious_dragon/state_db.py b/src/ethereum_optimized/spurious_dragon/state_db.py deleted file mode 100644 index 7e8a086cbf..0000000000 --- a/src/ethereum_optimized/spurious_dragon/state_db.py +++ /dev/null @@ -1,315 +0,0 @@ -""" -Optimized State -^^^^^^^^^^^^^^^ - -.. contents:: Table of Contents - :backlinks: none - :local: - -Introduction ------------- - -This module contains functions can be monkey patched into -`ethereum.spurious_dragon.state` to use an optimized database backed state. -""" -import logging -from dataclasses import dataclass -from typing import Any, ClassVar, Dict, List, Optional, Set - -try: - import rust_pyspec_glue -except ImportError as e: - # Add a message, but keep it an ImportError. - raise e from Exception( - "Install with `pip install 'ethereum[optimized]'` to enable this " - "package" - ) - -from ethereum.base_types import U256, Bytes, Uint -from ethereum.spurious_dragon.fork_types import Account, Address, Root - - -class UnmodifiedType: - """ - Sentinal type to represent a value that hasn't been modified. - """ - - pass - - -Unmodified = UnmodifiedType() - - -@dataclass -class State: - """ - The State, backed by a LMDB database. - - When created with `State()` store the db in a temporary directory. When - created with `State(path)` open or create the db located at `path`. - """ - - default_path: ClassVar[Optional[str]] = None - - db: Any - dirty_accounts: Dict[Address, Optional[Account]] - dirty_storage: Dict[Address, Dict[Bytes, U256]] - destroyed_accounts: Set[Address] - tx_restore_points: List[int] - journal: List[Any] - created_accounts: Set[Address] - - def __init__(self, path: Optional[str] = None) -> None: - logging.info("using optimized state db at %s", path) - - if path is None: - path = State.default_path - - self.db = rust_pyspec_glue.DB(path) - self.dirty_accounts = {} - self.dirty_storage = {} - self.destroyed_accounts = set() - self.tx_restore_points = [] - self.journal = [] - self.created_accounts = set() - self.db.begin_mutable() - - def __eq__(self, other: object) -> bool: - """ - Test for equality by comparing state roots. - """ - if not isinstance(other, State): - return NotImplemented - return state_root(self) == state_root(other) - - def __enter__(self) -> "State": - """Support with statements""" - return self - - def __exit__(self, *args: Any) -> None: - """Support with statements""" - close_state(self) - - -def close_state(state: State) -> None: - """Close a state, releasing all resources it holds""" - state.db.close() - state.db = None - del state.dirty_accounts - del state.dirty_storage - del state.destroyed_accounts - del state.journal - del state.created_accounts - - -def get_metadata(state: State, key: Bytes) -> Optional[Bytes]: - """Get a piece of metadata""" - return state.db.get_metadata(key) - - -def set_metadata(state: State, key: Bytes, value: Bytes) -> None: - """Set a piece of metadata""" - return state.db.set_metadata(key, value) - - -def begin_db_transaction(state: State) -> None: - """ - Start a database transaction. A transaction is automatically started when a - `State` is created. Nesting of DB transactions is not supported (unlike - non-db transactions). - - No operations are supported when not in a transaction. - """ - state.db.begin_mutable() - state.tx_restore_points = [] - state.journal = [] - - -def commit_db_transaction(state: State) -> None: - """ - Commit the current database transaction. - """ - if state.tx_restore_points: - raise Exception("In a non-db transaction") - flush(state) - state.db.commit_mutable() - - -def state_root(state: State) -> Root: - """ - See `ethereum.spurious_dragon.state`. - """ - if state.tx_restore_points: - raise Exception("In a non-db transaction") - flush(state) - return state.db.state_root() - - -def storage_root(state: State, address: Address) -> Root: - """ - See `ethereum.spurious_dragon.state`. - """ - if state.tx_restore_points: - raise Exception("In a non-db transaction") - flush(state) - return state.db.storage_root(address) - - -def flush(state: State) -> None: - """ - Send everything in the internal caches to the Rust layer. - """ - if state.tx_restore_points: - raise Exception("In a non-db transaction") - for address in state.destroyed_accounts: - state.db.destroy_storage(address) - for address, account in state.dirty_accounts.items(): - state.db.set_account(address, account) - for address, storage in state.dirty_storage.items(): - for key, value in storage.items(): - state.db.set_storage(address, key, value) - state.destroyed_accounts = set() - state.dirty_accounts.clear() - state.dirty_storage.clear() - - -def rollback_db_transaction(state: State) -> None: - """ - Rollback the current database transaction. - """ - if state.tx_restore_points: - raise Exception("In a non-db transaction") - state.db.rollback_mutable() - state.dirty_accounts.clear() - state.dirty_storage.clear() - state.destroyed_accounts = set() - - -def begin_transaction(state: State) -> None: - """ - See `ethereum.spurious_dragon.state`. - """ - if not state.tx_restore_points: - flush(state) - state.tx_restore_points.append(len(state.journal)) - - -def commit_transaction(state: State) -> None: - """ - See `ethereum.spurious_dragon.state`. - """ - state.tx_restore_points.pop() - if not state.tx_restore_points: - state.journal.clear() - state.created_accounts.clear() - flush(state) - - -def rollback_transaction(state: State) -> None: - """ - See `ethereum.spurious_dragon.state`. - """ - restore_point = state.tx_restore_points.pop() - while len(state.journal) > restore_point: - item = state.journal.pop() - if len(item) == 3: - # Revert a storage key write - if item[2] is Unmodified: - del state.dirty_storage[item[0]][item[1]] - else: - state.dirty_storage[item[0]][item[1]] = item[2] - elif type(item[1]) is dict: - # Restore storage that was destroyed by `destroy_storage()` - state.destroyed_accounts.remove(item[0]) - state.dirty_storage[item[0]] = item[1] - else: - # Revert a change to an account - if item[1] is Unmodified: - del state.dirty_accounts[item[0]] - else: - state.dirty_accounts[item[0]] = item[1] - - if not state.tx_restore_points: - state.created_accounts.clear() - - -def get_storage(state: State, address: Address, key: Bytes) -> U256: - """ - See `ethereum.spurious_dragon.state`. - """ - if address in state.dirty_storage and key in state.dirty_storage[address]: - return state.dirty_storage[address][key] - - if address in state.destroyed_accounts: - return U256(0) - else: - return U256(state.db.get_storage(address, key)) - - -def get_storage_original(state: State, address: Address, key: Bytes) -> U256: - """ - See `ethereum.istanbul.state`. - """ - if address in state.created_accounts: - return U256(0) - else: - return U256(state.db.get_storage(address, key)) - - -def set_storage( - state: State, address: Address, key: Bytes, value: U256 -) -> None: - """ - See `ethereum.spurious_dragon.state`. - """ - if address not in state.dirty_accounts: - state.dirty_accounts[address] = get_account_optional(state, address) - if address not in state.dirty_storage: - state.dirty_storage[address] = {} - if key not in state.dirty_storage[address]: - state.journal.append((address, key, Unmodified)) - else: - state.journal.append((address, key, state.dirty_storage[address][key])) - state.dirty_storage[address][key] = value - - -def get_account_optional(state: State, address: Address) -> Optional[Account]: - """ - See `ethereum.spurious_dragon.state`. - """ - if address in state.dirty_accounts: - return state.dirty_accounts[address] - account = state.db.get_account_optional(address) - if account is not None: - return Account(Uint(account[0]), U256(account[1]), account[2]) - else: - return None - - -def set_account( - state: State, address: Address, account: Optional[Account] -) -> None: - """ - See `ethereum.spurious_dragon.state`. - """ - if address not in state.dirty_accounts: - state.journal.append((address, Unmodified)) - if address in state.dirty_accounts: - state.journal.append((address, state.dirty_accounts[address])) - state.dirty_accounts[address] = account - - -def destroy_storage(state: State, address: Address) -> None: - """ - See `ethereum.spurious_dragon.state`. - """ - state.journal.append((address, state.dirty_storage.pop(address, {}))) - state.destroyed_accounts.add(address) - set_account(state, address, get_account_optional(state, address)) - - -def mark_account_created(state: State, address: Address) -> None: - """ - See `ethereum.frontier.state`. - """ - state.created_accounts.add(address) diff --git a/src/ethereum_optimized/state_db.py b/src/ethereum_optimized/state_db.py new file mode 100644 index 0000000000..9fe03cb000 --- /dev/null +++ b/src/ethereum_optimized/state_db.py @@ -0,0 +1,346 @@ +""" +Optimized State +^^^^^^^^^^^^^^^ + +.. contents:: Table of Contents + :backlinks: none + :local: + +Introduction +------------ + +This module contains functions can be monkey patched into the fork's `state` +module to use an optimized database backed state. +""" +import logging +from dataclasses import dataclass +from importlib import import_module +from typing import Any, ClassVar, Dict, List, Optional, Set, cast + +try: + import rust_pyspec_glue +except ImportError as e: + # Add a message, but keep it an ImportError. + raise e from Exception( + "Install with `pip install 'ethereum[optimized]'` to enable this " + "package" + ) + +from ethereum.base_types import U256, Bytes, Bytes20, Uint +from ethereum.crypto.hash import Hash32 + +from .utils import add_item + +Address = Bytes20 +Root = Hash32 +Account_ = Any + + +class UnmodifiedType: + """ + Sentinel type to represent a value that hasn't been modified. + """ + + pass + + +Unmodified = UnmodifiedType() + + +def get_optimized_state_patches(fork: str) -> Dict[str, Any]: + """ + Get a dictionary of functions/objects to be monkey patched into the state + to make it optimized. + """ + patches: Dict[str, Any] = {} + + mod = cast(Any, import_module("ethereum." + fork + ".fork_types")) + Account = mod.Account + + @add_item(patches) + @dataclass + class State: + """ + The State, backed by a LMDB database. + + When created with `State()` store the db in a temporary directory. When + created with `State(path)` open or create the db located at `path`. + """ + + default_path: ClassVar[Optional[str]] = None + + db: Any + dirty_accounts: Dict[Address, Optional[Account_]] + dirty_storage: Dict[Address, Dict[Bytes, U256]] + destroyed_accounts: Set[Address] + tx_restore_points: List[int] + journal: List[Any] + created_accounts: Set[Address] + + def __init__(self, path: Optional[str] = None) -> None: + logging.info("using optimized state db at %s", path) + + if path is None: + path = State.default_path + + self.db = rust_pyspec_glue.DB(path) + self.dirty_accounts = {} + self.dirty_storage = {} + self.destroyed_accounts = set() + self.tx_restore_points = [] + self.journal = [] + self.created_accounts = set() + self.db.begin_mutable() + + def __eq__(self, other: object) -> bool: + """ + Test for equality by comparing state roots. + """ + if not isinstance(other, State): + return NotImplemented + return state_root(self) == state_root(other) + + def __enter__(self) -> "State": + """Support with statements""" + return self + + def __exit__(self, *args: Any) -> None: + """Support with statements""" + close_state(self) + + @add_item(patches) + def close_state(state: State) -> None: + """Close a state, releasing all resources it holds""" + state.db.close() + state.db = None + del state.dirty_accounts + del state.dirty_storage + del state.destroyed_accounts + del state.journal + del state.created_accounts + + @add_item(patches) + def get_metadata(state: State, key: Bytes) -> Optional[Bytes]: + """Get a piece of metadata""" + return state.db.get_metadata(key) + + @add_item(patches) + def set_metadata(state: State, key: Bytes, value: Bytes) -> None: + """Set a piece of metadata""" + return state.db.set_metadata(key, value) + + @add_item(patches) + def begin_db_transaction(state: State) -> None: + """ + Start a database transaction. A transaction is automatically started + when a `State` is created. Nesting of DB transactions is not supported + (unlike non-db transactions). + + No operations are supported when not in a transaction. + """ + state.db.begin_mutable() + state.tx_restore_points = [] + state.journal = [] + + @add_item(patches) + def commit_db_transaction(state: State) -> None: + """ + Commit the current database transaction. + """ + if state.tx_restore_points: + raise Exception("In a non-db transaction") + flush(state) + state.db.commit_mutable() + + @add_item(patches) + def state_root(state: State) -> Root: + """ + See `state`. + """ + if state.tx_restore_points: + raise Exception("In a non-db transaction") + flush(state) + return state.db.state_root() + + @add_item(patches) + def storage_root(state: State, address: Address) -> Root: + """ + See `state`. + """ + if state.tx_restore_points: + raise Exception("In a non-db transaction") + flush(state) + return state.db.storage_root(address) + + @add_item(patches) + def flush(state: State) -> None: + """ + Send everything in the internal caches to the Rust layer. + """ + if state.tx_restore_points: + raise Exception("In a non-db transaction") + for address in state.destroyed_accounts: + state.db.destroy_storage(address) + for address, account in state.dirty_accounts.items(): + state.db.set_account(address, account) + for address, storage in state.dirty_storage.items(): + for key, value in storage.items(): + state.db.set_storage(address, key, value) + state.destroyed_accounts = set() + state.dirty_accounts.clear() + state.dirty_storage.clear() + + @add_item(patches) + def rollback_db_transaction(state: State) -> None: + """ + Rollback the current database transaction. + """ + if state.tx_restore_points: + raise Exception("In a non-db transaction") + state.db.rollback_mutable() + state.dirty_accounts.clear() + state.dirty_storage.clear() + state.destroyed_accounts = set() + + @add_item(patches) + def begin_transaction(state: State) -> None: + """ + See `state`. + """ + if not state.tx_restore_points: + flush(state) + state.tx_restore_points.append(len(state.journal)) + + @add_item(patches) + def commit_transaction(state: State) -> None: + """ + See `state`. + """ + state.tx_restore_points.pop() + if not state.tx_restore_points: + state.journal.clear() + state.created_accounts.clear() + flush(state) + + @add_item(patches) + def rollback_transaction(state: State) -> None: + """ + See `state`. + """ + restore_point = state.tx_restore_points.pop() + while len(state.journal) > restore_point: + item = state.journal.pop() + if len(item) == 3: + # Revert a storage key write + if item[2] is Unmodified: + del state.dirty_storage[item[0]][item[1]] + else: + state.dirty_storage[item[0]][item[1]] = item[2] + elif type(item[1]) is dict: + # Restore storage that was destroyed by `destroy_storage()` + state.destroyed_accounts.remove(item[0]) + state.dirty_storage[item[0]] = item[1] + else: + # Revert a change to an account + if item[1] is Unmodified: + del state.dirty_accounts[item[0]] + else: + state.dirty_accounts[item[0]] = item[1] + + if not state.tx_restore_points: + state.created_accounts.clear() + + @add_item(patches) + def get_storage(state: State, address: Address, key: Bytes) -> U256: + """ + See `state`. + """ + if ( + address in state.dirty_storage + and key in state.dirty_storage[address] + ): + return state.dirty_storage[address][key] + + if address in state.destroyed_accounts: + return U256(0) + else: + return U256(state.db.get_storage(address, key)) + + @add_item(patches) + def get_storage_original( + state: State, address: Address, key: Bytes + ) -> U256: + """ + See `state`. + """ + if address in state.created_accounts: + return U256(0) + else: + return U256(state.db.get_storage(address, key)) + + @add_item(patches) + def set_storage( + state: State, address: Address, key: Bytes, value: U256 + ) -> None: + """ + See `state`. + """ + if address not in state.dirty_accounts: + state.dirty_accounts[address] = get_account_optional( + state, address + ) + if address not in state.dirty_storage: + state.dirty_storage[address] = {} + if key not in state.dirty_storage[address]: + state.journal.append((address, key, Unmodified)) + else: + state.journal.append( + (address, key, state.dirty_storage[address][key]) + ) + state.dirty_storage[address][key] = value + + @add_item(patches) + def get_account_optional( + state: State, address: Address + ) -> Optional[Account_]: + """ + See `state`. + """ + if address in state.dirty_accounts: + return state.dirty_accounts[address] + account = state.db.get_account_optional(address) + if account is not None: + return Account(Uint(account[0]), U256(account[1]), account[2]) + else: + return None + + @add_item(patches) + def set_account( + state: State, address: Address, account: Optional[Account_] + ) -> None: + """ + See `state`. + """ + if address not in state.dirty_accounts: + state.journal.append((address, Unmodified)) + if address in state.dirty_accounts: + state.journal.append((address, state.dirty_accounts[address])) + state.dirty_accounts[address] = account + + @add_item(patches) + def destroy_storage(state: State, address: Address) -> None: + """ + See `state`. + """ + state.journal.append((address, state.dirty_storage.pop(address, {}))) + state.destroyed_accounts.add(address) + set_account(state, address, get_account_optional(state, address)) + + @add_item(patches) + def mark_account_created(state: State, address: Address) -> None: + """ + See `state`. + """ + state.created_accounts.add(address) + + return patches diff --git a/src/ethereum_optimized/tangerine_whistle/__init__.py b/src/ethereum_optimized/tangerine_whistle/__init__.py deleted file mode 100644 index 4fe486b51d..0000000000 --- a/src/ethereum_optimized/tangerine_whistle/__init__.py +++ /dev/null @@ -1,72 +0,0 @@ -""" -Optimized Implementations (Tangerine Whistle) -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -.. contents:: Table of Contents - :backlinks: none - :local: -""" - -from typing import Optional - - -def monkey_patch_optimized_state_db(state_path: Optional[str]) -> None: - """ - Replace the state interface with one that supports high performance - updates and storing state in a database. - - This function must be called before the state interface is imported - anywhere. - """ - import ethereum.tangerine_whistle.state as slow_state - - from . import state_db as fast_state - - optimized_state_db_patches = { - "State": fast_state.State, - "get_account_optional": fast_state.get_account_optional, - "set_account": fast_state.set_account, - "destroy_storage": fast_state.destroy_storage, - "mark_account_created": fast_state.mark_account_created, - "get_storage": fast_state.get_storage, - "get_storage_original": fast_state.get_storage_original, - "set_storage": fast_state.set_storage, - "state_root": fast_state.state_root, - "storage_root": fast_state.storage_root, - "begin_transaction": fast_state.begin_transaction, - "rollback_transaction": fast_state.rollback_transaction, - "commit_transaction": fast_state.commit_transaction, - "close_state": fast_state.close_state, - } - - for (name, value) in optimized_state_db_patches.items(): - setattr(slow_state, name, value) - - if state_path is not None: - fast_state.State.default_path = state_path - - -def monkey_patch_optimized_spec() -> None: - """ - Replace the ethash implementation with one that supports higher - performance. - - This function must be called before the spec interface is imported - anywhere. - """ - import ethereum.tangerine_whistle.fork as slow_spec - - from . import fork as fast_spec - - slow_spec.validate_proof_of_work = fast_spec.validate_proof_of_work - - -def monkey_patch(state_path: Optional[str]) -> None: - """ - Apply all monkey patches to swap in high performance implementations. - - This function must be called before any of the ethereum modules are - imported anywhere. - """ - monkey_patch_optimized_state_db(state_path) - monkey_patch_optimized_spec() diff --git a/src/ethereum_optimized/tangerine_whistle/fork.py b/src/ethereum_optimized/tangerine_whistle/fork.py deleted file mode 100644 index 571724c638..0000000000 --- a/src/ethereum_optimized/tangerine_whistle/fork.py +++ /dev/null @@ -1,47 +0,0 @@ -""" -Optimized Spec -^^^^^^^^^^^^^^ - -.. contents:: Table of Contents - :backlinks: none - :local: - -Introduction ------------- - -This module contains functions can be monkey patched into -`ethereum.tangerine_whistle.fork` to use alternate optimized implementations. -""" -from ethereum.base_types import U256_CEIL_VALUE -from ethereum.ethash import epoch -from ethereum.exceptions import InvalidBlock -from ethereum.tangerine_whistle.fork import generate_header_hash_for_pow -from ethereum.tangerine_whistle.fork_types import Header -from ethereum.utils.ensure import ensure - -try: - import ethash -except ImportError as e: - # Add a message, but keep it an ImportError. - raise e from Exception( - "Install with `pip install 'ethereum[optimized]'` to enable this " - "package" - ) - - -def validate_proof_of_work(header: Header) -> None: - """ - See `ethereum.tangerine_whistle.fork.validate_proof_of_work`. - """ - epoch_number = epoch(header.number) - header_hash = generate_header_hash_for_pow(header) - - result = ethash.verify( - int(epoch_number), - header_hash, - header.mix_digest, - int.from_bytes(header.nonce, "big"), - (U256_CEIL_VALUE // header.difficulty).to_be_bytes32(), - ) - - ensure(result, InvalidBlock) diff --git a/src/ethereum_optimized/tangerine_whistle/state_db.py b/src/ethereum_optimized/tangerine_whistle/state_db.py deleted file mode 100644 index b874f85658..0000000000 --- a/src/ethereum_optimized/tangerine_whistle/state_db.py +++ /dev/null @@ -1,315 +0,0 @@ -""" -Optimized State -^^^^^^^^^^^^^^^ - -.. contents:: Table of Contents - :backlinks: none - :local: - -Introduction ------------- - -This module contains functions can be monkey patched into -`ethereum.tangerine_whistle.state` to use an optimized database backed state. -""" -import logging -from dataclasses import dataclass -from typing import Any, ClassVar, Dict, List, Optional, Set - -try: - import rust_pyspec_glue -except ImportError as e: - # Add a message, but keep it an ImportError. - raise e from Exception( - "Install with `pip install 'ethereum[optimized]'` to enable this " - "package" - ) - -from ethereum.base_types import U256, Bytes, Uint -from ethereum.tangerine_whistle.fork_types import Account, Address, Root - - -class UnmodifiedType: - """ - Sentinal type to represent a value that hasn't been modified. - """ - - pass - - -Unmodified = UnmodifiedType() - - -@dataclass -class State: - """ - The State, backed by a LMDB database. - - When created with `State()` store the db in a temporary directory. When - created with `State(path)` open or create the db located at `path`. - """ - - default_path: ClassVar[Optional[str]] = None - - db: Any - dirty_accounts: Dict[Address, Optional[Account]] - dirty_storage: Dict[Address, Dict[Bytes, U256]] - destroyed_accounts: Set[Address] - tx_restore_points: List[int] - journal: List[Any] - created_accounts: Set[Address] - - def __init__(self, path: Optional[str] = None) -> None: - logging.info("using optimized state db at %s", path) - - if path is None: - path = State.default_path - - self.db = rust_pyspec_glue.DB(path) - self.dirty_accounts = {} - self.dirty_storage = {} - self.destroyed_accounts = set() - self.tx_restore_points = [] - self.journal = [] - self.created_accounts = set() - self.db.begin_mutable() - - def __eq__(self, other: object) -> bool: - """ - Test for equality by comparing state roots. - """ - if not isinstance(other, State): - return NotImplemented - return state_root(self) == state_root(other) - - def __enter__(self) -> "State": - """Support with statements""" - return self - - def __exit__(self, *args: Any) -> None: - """Support with statements""" - close_state(self) - - -def close_state(state: State) -> None: - """Close a state, releasing all resources it holds""" - state.db.close() - state.db = None - del state.dirty_accounts - del state.dirty_storage - del state.destroyed_accounts - del state.journal - del state.created_accounts - - -def get_metadata(state: State, key: Bytes) -> Optional[Bytes]: - """Get a piece of metadata""" - return state.db.get_metadata(key) - - -def set_metadata(state: State, key: Bytes, value: Bytes) -> None: - """Set a piece of metadata""" - return state.db.set_metadata(key, value) - - -def begin_db_transaction(state: State) -> None: - """ - Start a database transaction. A transaction is automatically started when a - `State` is created. Nesting of DB transactions is not supported (unlike - non-db transactions). - - No operations are supported when not in a transaction. - """ - state.db.begin_mutable() - state.tx_restore_points = [] - state.journal = [] - - -def commit_db_transaction(state: State) -> None: - """ - Commit the current database transaction. - """ - if state.tx_restore_points: - raise Exception("In a non-db transaction") - flush(state) - state.db.commit_mutable() - - -def state_root(state: State) -> Root: - """ - See `ethereum.tangerine_whistle.state`. - """ - if state.tx_restore_points: - raise Exception("In a non-db transaction") - flush(state) - return state.db.state_root() - - -def storage_root(state: State, address: Address) -> Root: - """ - See `ethereum.tangerine_whistle.state`. - """ - if state.tx_restore_points: - raise Exception("In a non-db transaction") - flush(state) - return state.db.storage_root(address) - - -def flush(state: State) -> None: - """ - Send everything in the internal caches to the Rust layer. - """ - if state.tx_restore_points: - raise Exception("In a non-db transaction") - for address in state.destroyed_accounts: - state.db.destroy_storage(address) - for address, account in state.dirty_accounts.items(): - state.db.set_account(address, account) - for address, storage in state.dirty_storage.items(): - for key, value in storage.items(): - state.db.set_storage(address, key, value) - state.destroyed_accounts = set() - state.dirty_accounts.clear() - state.dirty_storage.clear() - - -def rollback_db_transaction(state: State) -> None: - """ - Rollback the current database transaction. - """ - if state.tx_restore_points: - raise Exception("In a non-db transaction") - state.db.rollback_mutable() - state.dirty_accounts.clear() - state.dirty_storage.clear() - state.destroyed_accounts = set() - - -def begin_transaction(state: State) -> None: - """ - See `ethereum.tangerine_whistle.state`. - """ - if not state.tx_restore_points: - flush(state) - state.tx_restore_points.append(len(state.journal)) - - -def commit_transaction(state: State) -> None: - """ - See `ethereum.tangerine_whistle.state`. - """ - state.tx_restore_points.pop() - if not state.tx_restore_points: - state.journal.clear() - state.created_accounts.clear() - flush(state) - - -def rollback_transaction(state: State) -> None: - """ - See `ethereum.tangerine_whistle.state`. - """ - restore_point = state.tx_restore_points.pop() - while len(state.journal) > restore_point: - item = state.journal.pop() - if len(item) == 3: - # Revert a storage key write - if item[2] is Unmodified: - del state.dirty_storage[item[0]][item[1]] - else: - state.dirty_storage[item[0]][item[1]] = item[2] - elif type(item[1]) is dict: - # Restore storage that was destroyed by `destroy_storage()` - state.destroyed_accounts.remove(item[0]) - state.dirty_storage[item[0]] = item[1] - else: - # Revert a change to an account - if item[1] is Unmodified: - del state.dirty_accounts[item[0]] - else: - state.dirty_accounts[item[0]] = item[1] - - if not state.tx_restore_points: - state.created_accounts.clear() - - -def get_storage(state: State, address: Address, key: Bytes) -> U256: - """ - See `ethereum.tangerine_whistle.state`. - """ - if address in state.dirty_storage and key in state.dirty_storage[address]: - return state.dirty_storage[address][key] - - if address in state.destroyed_accounts: - return U256(0) - else: - return U256(state.db.get_storage(address, key)) - - -def get_storage_original(state: State, address: Address, key: Bytes) -> U256: - """ - See `ethereum.istanbul.state`. - """ - if address in state.created_accounts: - return U256(0) - else: - return U256(state.db.get_storage(address, key)) - - -def set_storage( - state: State, address: Address, key: Bytes, value: U256 -) -> None: - """ - See `ethereum.tangerine_whistle.state`. - """ - if address not in state.dirty_accounts: - state.dirty_accounts[address] = get_account_optional(state, address) - if address not in state.dirty_storage: - state.dirty_storage[address] = {} - if key not in state.dirty_storage[address]: - state.journal.append((address, key, Unmodified)) - else: - state.journal.append((address, key, state.dirty_storage[address][key])) - state.dirty_storage[address][key] = value - - -def get_account_optional(state: State, address: Address) -> Optional[Account]: - """ - See `ethereum.tangerine_whistle.state`. - """ - if address in state.dirty_accounts: - return state.dirty_accounts[address] - account = state.db.get_account_optional(address) - if account is not None: - return Account(Uint(account[0]), U256(account[1]), account[2]) - else: - return None - - -def set_account( - state: State, address: Address, account: Optional[Account] -) -> None: - """ - See `ethereum.tangerine_whistle.state`. - """ - if address not in state.dirty_accounts: - state.journal.append((address, Unmodified)) - if address in state.dirty_accounts: - state.journal.append((address, state.dirty_accounts[address])) - state.dirty_accounts[address] = account - - -def destroy_storage(state: State, address: Address) -> None: - """ - See `ethereum.tangerine_whistle.state`. - """ - state.journal.append((address, state.dirty_storage.pop(address, {}))) - state.destroyed_accounts.add(address) - set_account(state, address, get_account_optional(state, address)) - - -def mark_account_created(state: State, address: Address) -> None: - """ - See `ethereum.frontier.state`. - """ - state.created_accounts.add(address) diff --git a/src/ethereum_optimized/utils.py b/src/ethereum_optimized/utils.py new file mode 100644 index 0000000000..e154d44d55 --- /dev/null +++ b/src/ethereum_optimized/utils.py @@ -0,0 +1,27 @@ +""" +Optimized Implementation Utilities +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +.. contents:: Table of Contents + :backlinks: none + :local: + +Introduction +------------ + +Utility functions for optimized implementations. +""" + +from typing import Any, Dict + + +def add_item(patches: Dict[str, Any]) -> Any: + """ + Decorator to add a function to a patches dictionary. + """ + + def inner(f: Any) -> Any: + patches[f.__name__] = f + return f + + return inner diff --git a/src/ethereum_spec_tools/new_fork.py b/src/ethereum_spec_tools/new_fork.py index 24a1ff1b08..3fb1d918d2 100644 --- a/src/ethereum_spec_tools/new_fork.py +++ b/src/ethereum_spec_tools/new_fork.py @@ -79,7 +79,6 @@ def __init__( to_test_names: str, ): self.package_folder = "src/ethereum" - self.optimized_package_folder = "src/ethereum_optimized" self.test_folder = "tests" # Get the fork specific data for from fork @@ -87,7 +86,6 @@ def __init__( self.from_fork, self.from_package, self.from_path, - self.from_optimized_path, self.from_test_path, ) = self.get_fork_paths(from_fork) @@ -96,7 +94,6 @@ def __init__( self.to_fork, self.to_package, self.to_path, - self.to_optimized_path, self.to_test_path, ) = self.get_fork_paths(to_fork) @@ -110,16 +107,14 @@ def get_fork_paths(self, fork: str) -> Tuple[str, ...]: name = fork package = name.replace(" ", "_").lower() path = os.path.join(self.package_folder, package) - optimized_path = os.path.join(self.optimized_package_folder, package) test_path = os.path.join(self.test_folder, package) - return (name, package, path, optimized_path, test_path) + return (name, package, path, test_path) def duplicate_fork(self) -> None: """ Copy the relevant files/folders from the old fork """ copytree(self.from_path, self.to_path) - copytree(self.from_optimized_path, self.to_optimized_path) copytree(self.from_test_path, self.to_test_path) def update_new_fork_contents(self) -> None: @@ -137,20 +132,6 @@ def update_new_fork_contents(self) -> None: self.to_path, self.from_fork.lower(), self.to_fork.lower(), "*.py" ) - # Update optimized source code - find_replace( - self.to_optimized_path, self.from_fork, self.to_fork, "*.py" - ) - find_replace( - self.to_optimized_path, self.from_package, self.to_package, "*.py" - ) - find_replace( - self.to_optimized_path, - self.from_fork.lower(), - self.to_fork.lower(), - "*.py", - ) - # Update test files starting with the names used in the test fixtures find_replace( self.to_test_path, self.from_test_names, self.to_test_names, "*.py" diff --git a/src/ethereum_spec_tools/patch_tool.py b/src/ethereum_spec_tools/patch_tool.py index e4be20d64b..99987f77ec 100644 --- a/src/ethereum_spec_tools/patch_tool.py +++ b/src/ethereum_spec_tools/patch_tool.py @@ -16,14 +16,6 @@ parser.add_argument("source_fork", metavar="SOURCE_FORK", type=str, nargs=1) parser.add_argument("targets", metavar="TARGET_FORK", type=str, nargs="*") -parser.add_argument( - "--optimized", - action="store_const", - const="src/ethereum_optimized/", - dest="prefix", - default="src/ethereum/", - help="Patch the optimized code instead", -) parser.add_argument( "--tests", action="store_const", diff --git a/tests/berlin/optimized/__init__.py b/tests/berlin/optimized/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/tests/berlin/optimized/test_state_db.py b/tests/berlin/optimized/test_state_db.py deleted file mode 100644 index 7571a555fa..0000000000 --- a/tests/berlin/optimized/test_state_db.py +++ /dev/null @@ -1,66 +0,0 @@ -import sys -from typing import Any - -import pytest - -import ethereum.berlin.state as state -from ethereum.base_types import U256 -from ethereum.berlin.fork_types import EMPTY_ACCOUNT -from ethereum.berlin.utils.hexadecimal import hex_to_address - -try: - import ethereum_optimized.berlin.state_db as state_db -except ImportError: - pass - - -ADDRESS_FOO = hex_to_address("0x00000000219ab540356cbb839cbe05303d7705fa") -STORAGE_FOO = U256(101).to_be_bytes32() - - -@pytest.mark.skipif( - "ethereum_optimized.berlin.state_db" not in sys.modules, - reason="missing dependency (use `pip install 'ethereum[optimized]'`)", -) -def test_storage_key() -> None: - def actions(impl: Any) -> Any: - obj = impl.State() - impl.set_account(obj, ADDRESS_FOO, EMPTY_ACCOUNT) - impl.set_storage(obj, ADDRESS_FOO, STORAGE_FOO, U256(42)) - impl.state_root(obj) - return obj - - state_normal = actions(state) - state_optimized = actions(state_db) - assert state.get_storage( - state_normal, ADDRESS_FOO, STORAGE_FOO - ) == state_db.get_storage(state_optimized, ADDRESS_FOO, STORAGE_FOO) - assert state.state_root(state_normal) == state_db.state_root( - state_optimized - ) - - -@pytest.mark.skipif( - "ethereum_optimized.berlin.state_db" not in sys.modules, - reason="missing dependency (use `pip install 'ethereum[optimized]'`)", -) -def test_resurrection() -> None: - def actions(impl: Any) -> Any: - obj = impl.State() - impl.set_account(obj, ADDRESS_FOO, EMPTY_ACCOUNT) - impl.set_storage(obj, ADDRESS_FOO, STORAGE_FOO, U256(42)) - impl.state_root(obj) - impl.destroy_storage(obj, ADDRESS_FOO) - impl.state_root(obj) - impl.set_account(obj, ADDRESS_FOO, EMPTY_ACCOUNT) - return obj - - state_normal = actions(state) - state_optimized = actions(state_db) - state_db.state_root(state_optimized) - assert state.get_storage( - state_normal, ADDRESS_FOO, STORAGE_FOO - ) == state_db.get_storage(state_optimized, ADDRESS_FOO, STORAGE_FOO) - assert state.state_root(state_normal) == state_db.state_root( - state_optimized - ) diff --git a/tests/byzantium/optimized/__init__.py b/tests/byzantium/optimized/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/tests/byzantium/optimized/test_state_db.py b/tests/byzantium/optimized/test_state_db.py deleted file mode 100644 index 33c1cd6a92..0000000000 --- a/tests/byzantium/optimized/test_state_db.py +++ /dev/null @@ -1,66 +0,0 @@ -import sys -from typing import Any - -import pytest - -import ethereum.byzantium.state as state -from ethereum.base_types import U256 -from ethereum.byzantium.fork_types import EMPTY_ACCOUNT -from ethereum.byzantium.utils.hexadecimal import hex_to_address - -try: - import ethereum_optimized.byzantium.state_db as state_db -except ImportError: - pass - - -ADDRESS_FOO = hex_to_address("0x00000000219ab540356cbb839cbe05303d7705fa") -STORAGE_FOO = U256(101).to_be_bytes32() - - -@pytest.mark.skipif( - "ethereum_optimized.byzantium.state_db" not in sys.modules, - reason="missing dependency (use `pip install 'ethereum[optimized]'`)", -) -def test_storage_key() -> None: - def actions(impl: Any) -> Any: - obj = impl.State() - impl.set_account(obj, ADDRESS_FOO, EMPTY_ACCOUNT) - impl.set_storage(obj, ADDRESS_FOO, STORAGE_FOO, U256(42)) - impl.state_root(obj) - return obj - - state_normal = actions(state) - state_optimized = actions(state_db) - assert state.get_storage( - state_normal, ADDRESS_FOO, STORAGE_FOO - ) == state_db.get_storage(state_optimized, ADDRESS_FOO, STORAGE_FOO) - assert state.state_root(state_normal) == state_db.state_root( - state_optimized - ) - - -@pytest.mark.skipif( - "ethereum_optimized.byzantium.state_db" not in sys.modules, - reason="missing dependency (use `pip install 'ethereum[optimized]'`)", -) -def test_resurrection() -> None: - def actions(impl: Any) -> Any: - obj = impl.State() - impl.set_account(obj, ADDRESS_FOO, EMPTY_ACCOUNT) - impl.set_storage(obj, ADDRESS_FOO, STORAGE_FOO, U256(42)) - impl.state_root(obj) - impl.destroy_storage(obj, ADDRESS_FOO) - impl.state_root(obj) - impl.set_account(obj, ADDRESS_FOO, EMPTY_ACCOUNT) - return obj - - state_normal = actions(state) - state_optimized = actions(state_db) - state_db.state_root(state_optimized) - assert state.get_storage( - state_normal, ADDRESS_FOO, STORAGE_FOO - ) == state_db.get_storage(state_optimized, ADDRESS_FOO, STORAGE_FOO) - assert state.state_root(state_normal) == state_db.state_root( - state_optimized - ) diff --git a/tests/constantinople/optimized/__init__.py b/tests/constantinople/optimized/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/tests/constantinople/optimized/test_state_db.py b/tests/constantinople/optimized/test_state_db.py deleted file mode 100644 index c2b3bad3c0..0000000000 --- a/tests/constantinople/optimized/test_state_db.py +++ /dev/null @@ -1,66 +0,0 @@ -import sys -from typing import Any - -import pytest - -import ethereum.constantinople.state as state -from ethereum.base_types import U256 -from ethereum.constantinople.fork_types import EMPTY_ACCOUNT -from ethereum.constantinople.utils.hexadecimal import hex_to_address - -try: - import ethereum_optimized.constantinople.state_db as state_db -except ImportError: - pass - - -ADDRESS_FOO = hex_to_address("0x00000000219ab540356cbb839cbe05303d7705fa") -STORAGE_FOO = U256(101).to_be_bytes32() - - -@pytest.mark.skipif( - "ethereum_optimized.constantinople.state_db" not in sys.modules, - reason="missing dependency (use `pip install 'ethereum[optimized]'`)", -) -def test_storage_key() -> None: - def actions(impl: Any) -> Any: - obj = impl.State() - impl.set_account(obj, ADDRESS_FOO, EMPTY_ACCOUNT) - impl.set_storage(obj, ADDRESS_FOO, STORAGE_FOO, U256(42)) - impl.state_root(obj) - return obj - - state_normal = actions(state) - state_optimized = actions(state_db) - assert state.get_storage( - state_normal, ADDRESS_FOO, STORAGE_FOO - ) == state_db.get_storage(state_optimized, ADDRESS_FOO, STORAGE_FOO) - assert state.state_root(state_normal) == state_db.state_root( - state_optimized - ) - - -@pytest.mark.skipif( - "ethereum_optimized.constantinople.state_db" not in sys.modules, - reason="missing dependency (use `pip install 'ethereum[optimized]'`)", -) -def test_resurrection() -> None: - def actions(impl: Any) -> Any: - obj = impl.State() - impl.set_account(obj, ADDRESS_FOO, EMPTY_ACCOUNT) - impl.set_storage(obj, ADDRESS_FOO, STORAGE_FOO, U256(42)) - impl.state_root(obj) - impl.destroy_storage(obj, ADDRESS_FOO) - impl.state_root(obj) - impl.set_account(obj, ADDRESS_FOO, EMPTY_ACCOUNT) - return obj - - state_normal = actions(state) - state_optimized = actions(state_db) - state_db.state_root(state_optimized) - assert state.get_storage( - state_normal, ADDRESS_FOO, STORAGE_FOO - ) == state_db.get_storage(state_optimized, ADDRESS_FOO, STORAGE_FOO) - assert state.state_root(state_normal) == state_db.state_root( - state_optimized - ) diff --git a/tests/frontier/optimized/__init__.py b/tests/frontier/optimized/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/tests/homestead/optimized/__init__.py b/tests/homestead/optimized/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/tests/homestead/optimized/test_state_db.py b/tests/homestead/optimized/test_state_db.py deleted file mode 100644 index 8fe3e77189..0000000000 --- a/tests/homestead/optimized/test_state_db.py +++ /dev/null @@ -1,66 +0,0 @@ -import sys -from typing import Any - -import pytest - -import ethereum.homestead.state as state -from ethereum.base_types import U256 -from ethereum.homestead.fork_types import EMPTY_ACCOUNT -from ethereum.homestead.utils.hexadecimal import hex_to_address - -try: - import ethereum_optimized.homestead.state_db as state_db -except ImportError: - pass - - -ADDRESS_FOO = hex_to_address("0x00000000219ab540356cbb839cbe05303d7705fa") -STORAGE_FOO = U256(101).to_be_bytes32() - - -@pytest.mark.skipif( - "ethereum_optimized.homestead.state_db" not in sys.modules, - reason="missing dependency (use `pip install 'ethereum[optimized]'`)", -) -def test_storage_key() -> None: - def actions(impl: Any) -> Any: - obj = impl.State() - impl.set_account(obj, ADDRESS_FOO, EMPTY_ACCOUNT) - impl.set_storage(obj, ADDRESS_FOO, STORAGE_FOO, U256(42)) - impl.state_root(obj) - return obj - - state_normal = actions(state) - state_optimized = actions(state_db) - assert state.get_storage( - state_normal, ADDRESS_FOO, STORAGE_FOO - ) == state_db.get_storage(state_optimized, ADDRESS_FOO, STORAGE_FOO) - assert state.state_root(state_normal) == state_db.state_root( - state_optimized - ) - - -@pytest.mark.skipif( - "ethereum_optimized.homestead.state_db" not in sys.modules, - reason="missing dependency (use `pip install 'ethereum[optimized]'`)", -) -def test_resurrection() -> None: - def actions(impl: Any) -> Any: - obj = impl.State() - impl.set_account(obj, ADDRESS_FOO, EMPTY_ACCOUNT) - impl.set_storage(obj, ADDRESS_FOO, STORAGE_FOO, U256(42)) - impl.state_root(obj) - impl.destroy_storage(obj, ADDRESS_FOO) - impl.state_root(obj) - impl.set_account(obj, ADDRESS_FOO, EMPTY_ACCOUNT) - return obj - - state_normal = actions(state) - state_optimized = actions(state_db) - state_db.state_root(state_optimized) - assert state.get_storage( - state_normal, ADDRESS_FOO, STORAGE_FOO - ) == state_db.get_storage(state_optimized, ADDRESS_FOO, STORAGE_FOO) - assert state.state_root(state_normal) == state_db.state_root( - state_optimized - ) diff --git a/tests/istanbul/optimized/__init__.py b/tests/istanbul/optimized/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/tests/istanbul/optimized/test_state_db.py b/tests/istanbul/optimized/test_state_db.py deleted file mode 100644 index fb3150aaba..0000000000 --- a/tests/istanbul/optimized/test_state_db.py +++ /dev/null @@ -1,66 +0,0 @@ -import sys -from typing import Any - -import pytest - -import ethereum.istanbul.state as state -from ethereum.base_types import U256 -from ethereum.istanbul.fork_types import EMPTY_ACCOUNT -from ethereum.istanbul.utils.hexadecimal import hex_to_address - -try: - import ethereum_optimized.istanbul.state_db as state_db -except ImportError: - pass - - -ADDRESS_FOO = hex_to_address("0x00000000219ab540356cbb839cbe05303d7705fa") -STORAGE_FOO = U256(101).to_be_bytes32() - - -@pytest.mark.skipif( - "ethereum_optimized.istanbul.state_db" not in sys.modules, - reason="missing dependency (use `pip install 'ethereum[optimized]'`)", -) -def test_storage_key() -> None: - def actions(impl: Any) -> Any: - obj = impl.State() - impl.set_account(obj, ADDRESS_FOO, EMPTY_ACCOUNT) - impl.set_storage(obj, ADDRESS_FOO, STORAGE_FOO, U256(42)) - impl.state_root(obj) - return obj - - state_normal = actions(state) - state_optimized = actions(state_db) - assert state.get_storage( - state_normal, ADDRESS_FOO, STORAGE_FOO - ) == state_db.get_storage(state_optimized, ADDRESS_FOO, STORAGE_FOO) - assert state.state_root(state_normal) == state_db.state_root( - state_optimized - ) - - -@pytest.mark.skipif( - "ethereum_optimized.istanbul.state_db" not in sys.modules, - reason="missing dependency (use `pip install 'ethereum[optimized]'`)", -) -def test_resurrection() -> None: - def actions(impl: Any) -> Any: - obj = impl.State() - impl.set_account(obj, ADDRESS_FOO, EMPTY_ACCOUNT) - impl.set_storage(obj, ADDRESS_FOO, STORAGE_FOO, U256(42)) - impl.state_root(obj) - impl.destroy_storage(obj, ADDRESS_FOO) - impl.state_root(obj) - impl.set_account(obj, ADDRESS_FOO, EMPTY_ACCOUNT) - return obj - - state_normal = actions(state) - state_optimized = actions(state_db) - state_db.state_root(state_optimized) - assert state.get_storage( - state_normal, ADDRESS_FOO, STORAGE_FOO - ) == state_db.get_storage(state_optimized, ADDRESS_FOO, STORAGE_FOO) - assert state.state_root(state_normal) == state_db.state_root( - state_optimized - ) diff --git a/tests/london/optimized/__init__.py b/tests/london/optimized/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/tests/london/optimized/test_state_db.py b/tests/london/optimized/test_state_db.py deleted file mode 100644 index c73c792866..0000000000 --- a/tests/london/optimized/test_state_db.py +++ /dev/null @@ -1,66 +0,0 @@ -import sys -from typing import Any - -import pytest - -import ethereum.london.state as state -from ethereum.base_types import U256 -from ethereum.london.fork_types import EMPTY_ACCOUNT -from ethereum.london.utils.hexadecimal import hex_to_address - -try: - import ethereum_optimized.london.state_db as state_db -except ImportError: - pass - - -ADDRESS_FOO = hex_to_address("0x00000000219ab540356cbb839cbe05303d7705fa") -STORAGE_FOO = U256(101).to_be_bytes32() - - -@pytest.mark.skipif( - "ethereum_optimized.london.state_db" not in sys.modules, - reason="missing dependency (use `pip install 'ethereum[optimized]'`)", -) -def test_storage_key() -> None: - def actions(impl: Any) -> Any: - obj = impl.State() - impl.set_account(obj, ADDRESS_FOO, EMPTY_ACCOUNT) - impl.set_storage(obj, ADDRESS_FOO, STORAGE_FOO, U256(42)) - impl.state_root(obj) - return obj - - state_normal = actions(state) - state_optimized = actions(state_db) - assert state.get_storage( - state_normal, ADDRESS_FOO, STORAGE_FOO - ) == state_db.get_storage(state_optimized, ADDRESS_FOO, STORAGE_FOO) - assert state.state_root(state_normal) == state_db.state_root( - state_optimized - ) - - -@pytest.mark.skipif( - "ethereum_optimized.london.state_db" not in sys.modules, - reason="missing dependency (use `pip install 'ethereum[optimized]'`)", -) -def test_resurrection() -> None: - def actions(impl: Any) -> Any: - obj = impl.State() - impl.set_account(obj, ADDRESS_FOO, EMPTY_ACCOUNT) - impl.set_storage(obj, ADDRESS_FOO, STORAGE_FOO, U256(42)) - impl.state_root(obj) - impl.destroy_storage(obj, ADDRESS_FOO) - impl.state_root(obj) - impl.set_account(obj, ADDRESS_FOO, EMPTY_ACCOUNT) - return obj - - state_normal = actions(state) - state_optimized = actions(state_db) - state_db.state_root(state_optimized) - assert state.get_storage( - state_normal, ADDRESS_FOO, STORAGE_FOO - ) == state_db.get_storage(state_optimized, ADDRESS_FOO, STORAGE_FOO) - assert state.state_root(state_normal) == state_db.state_root( - state_optimized - ) diff --git a/tests/paris/optimized/__init__.py b/tests/paris/optimized/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/tests/paris/optimized/test_state_db.py b/tests/paris/optimized/test_state_db.py deleted file mode 100644 index 77eabdf4f3..0000000000 --- a/tests/paris/optimized/test_state_db.py +++ /dev/null @@ -1,66 +0,0 @@ -import sys -from typing import Any - -import pytest - -import ethereum.paris.state as state -from ethereum.base_types import U256 -from ethereum.paris.fork_types import EMPTY_ACCOUNT -from ethereum.paris.utils.hexadecimal import hex_to_address - -try: - import ethereum_optimized.paris.state_db as state_db -except ImportError: - pass - - -ADDRESS_FOO = hex_to_address("0x00000000219ab540356cbb839cbe05303d7705fa") -STORAGE_FOO = U256(101).to_be_bytes32() - - -@pytest.mark.skipif( - "ethereum_optimized.paris.state_db" not in sys.modules, - reason="missing dependency (use `pip install 'ethereum[optimized]'`)", -) -def test_storage_key() -> None: - def actions(impl: Any) -> Any: - obj = impl.State() - impl.set_account(obj, ADDRESS_FOO, EMPTY_ACCOUNT) - impl.set_storage(obj, ADDRESS_FOO, STORAGE_FOO, U256(42)) - impl.state_root(obj) - return obj - - state_normal = actions(state) - state_optimized = actions(state_db) - assert state.get_storage( - state_normal, ADDRESS_FOO, STORAGE_FOO - ) == state_db.get_storage(state_optimized, ADDRESS_FOO, STORAGE_FOO) - assert state.state_root(state_normal) == state_db.state_root( - state_optimized - ) - - -@pytest.mark.skipif( - "ethereum_optimized.paris.state_db" not in sys.modules, - reason="missing dependency (use `pip install 'ethereum[optimized]'`)", -) -def test_resurrection() -> None: - def actions(impl: Any) -> Any: - obj = impl.State() - impl.set_account(obj, ADDRESS_FOO, EMPTY_ACCOUNT) - impl.set_storage(obj, ADDRESS_FOO, STORAGE_FOO, U256(42)) - impl.state_root(obj) - impl.destroy_storage(obj, ADDRESS_FOO) - impl.state_root(obj) - impl.set_account(obj, ADDRESS_FOO, EMPTY_ACCOUNT) - return obj - - state_normal = actions(state) - state_optimized = actions(state_db) - state_db.state_root(state_optimized) - assert state.get_storage( - state_normal, ADDRESS_FOO, STORAGE_FOO - ) == state_db.get_storage(state_optimized, ADDRESS_FOO, STORAGE_FOO) - assert state.state_root(state_normal) == state_db.state_root( - state_optimized - ) diff --git a/tests/shanghai/optimized/__init__.py b/tests/shanghai/optimized/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/tests/shanghai/optimized/test_state_db.py b/tests/shanghai/optimized/test_state_db.py deleted file mode 100644 index 6d9e2d4517..0000000000 --- a/tests/shanghai/optimized/test_state_db.py +++ /dev/null @@ -1,66 +0,0 @@ -import sys -from typing import Any - -import pytest - -import ethereum.shanghai.state as state -from ethereum.base_types import U256 -from ethereum.shanghai.fork_types import EMPTY_ACCOUNT -from ethereum.shanghai.utils.hexadecimal import hex_to_address - -try: - import ethereum_optimized.shanghai.state_db as state_db -except ImportError: - pass - - -ADDRESS_FOO = hex_to_address("0x00000000219ab540356cbb839cbe05303d7705fa") -STORAGE_FOO = U256(101).to_be_bytes32() - - -@pytest.mark.skipif( - "ethereum_optimized.shanghai.state_db" not in sys.modules, - reason="missing dependency (use `pip install 'ethereum[optimized]'`)", -) -def test_storage_key() -> None: - def actions(impl: Any) -> Any: - obj = impl.State() - impl.set_account(obj, ADDRESS_FOO, EMPTY_ACCOUNT) - impl.set_storage(obj, ADDRESS_FOO, STORAGE_FOO, U256(42)) - impl.state_root(obj) - return obj - - state_normal = actions(state) - state_optimized = actions(state_db) - assert state.get_storage( - state_normal, ADDRESS_FOO, STORAGE_FOO - ) == state_db.get_storage(state_optimized, ADDRESS_FOO, STORAGE_FOO) - assert state.state_root(state_normal) == state_db.state_root( - state_optimized - ) - - -@pytest.mark.skipif( - "ethereum_optimized.shanghai.state_db" not in sys.modules, - reason="missing dependency (use `pip install 'ethereum[optimized]'`)", -) -def test_resurrection() -> None: - def actions(impl: Any) -> Any: - obj = impl.State() - impl.set_account(obj, ADDRESS_FOO, EMPTY_ACCOUNT) - impl.set_storage(obj, ADDRESS_FOO, STORAGE_FOO, U256(42)) - impl.state_root(obj) - impl.destroy_storage(obj, ADDRESS_FOO) - impl.state_root(obj) - impl.set_account(obj, ADDRESS_FOO, EMPTY_ACCOUNT) - return obj - - state_normal = actions(state) - state_optimized = actions(state_db) - state_db.state_root(state_optimized) - assert state.get_storage( - state_normal, ADDRESS_FOO, STORAGE_FOO - ) == state_db.get_storage(state_optimized, ADDRESS_FOO, STORAGE_FOO) - assert state.state_root(state_normal) == state_db.state_root( - state_optimized - ) diff --git a/tests/spurious_dragon/optimized/__init__.py b/tests/spurious_dragon/optimized/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/tests/spurious_dragon/optimized/test_state_db.py b/tests/spurious_dragon/optimized/test_state_db.py deleted file mode 100644 index 9c552c3025..0000000000 --- a/tests/spurious_dragon/optimized/test_state_db.py +++ /dev/null @@ -1,66 +0,0 @@ -import sys -from typing import Any - -import pytest - -import ethereum.spurious_dragon.state as state -from ethereum.base_types import U256 -from ethereum.spurious_dragon.fork_types import EMPTY_ACCOUNT -from ethereum.spurious_dragon.utils.hexadecimal import hex_to_address - -try: - import ethereum_optimized.spurious_dragon.state_db as state_db -except ImportError: - pass - - -ADDRESS_FOO = hex_to_address("0x00000000219ab540356cbb839cbe05303d7705fa") -STORAGE_FOO = U256(101).to_be_bytes32() - - -@pytest.mark.skipif( - "ethereum_optimized.spurious_dragon.state_db" not in sys.modules, - reason="missing dependency (use `pip install 'ethereum[optimized]'`)", -) -def test_storage_key() -> None: - def actions(impl: Any) -> Any: - obj = impl.State() - impl.set_account(obj, ADDRESS_FOO, EMPTY_ACCOUNT) - impl.set_storage(obj, ADDRESS_FOO, STORAGE_FOO, U256(42)) - impl.state_root(obj) - return obj - - state_normal = actions(state) - state_optimized = actions(state_db) - assert state.get_storage( - state_normal, ADDRESS_FOO, STORAGE_FOO - ) == state_db.get_storage(state_optimized, ADDRESS_FOO, STORAGE_FOO) - assert state.state_root(state_normal) == state_db.state_root( - state_optimized - ) - - -@pytest.mark.skipif( - "ethereum_optimized.spurious_dragon.state_db" not in sys.modules, - reason="missing dependency (use `pip install 'ethereum[optimized]'`)", -) -def test_resurrection() -> None: - def actions(impl: Any) -> Any: - obj = impl.State() - impl.set_account(obj, ADDRESS_FOO, EMPTY_ACCOUNT) - impl.set_storage(obj, ADDRESS_FOO, STORAGE_FOO, U256(42)) - impl.state_root(obj) - impl.destroy_storage(obj, ADDRESS_FOO) - impl.state_root(obj) - impl.set_account(obj, ADDRESS_FOO, EMPTY_ACCOUNT) - return obj - - state_normal = actions(state) - state_optimized = actions(state_db) - state_db.state_root(state_optimized) - assert state.get_storage( - state_normal, ADDRESS_FOO, STORAGE_FOO - ) == state_db.get_storage(state_optimized, ADDRESS_FOO, STORAGE_FOO) - assert state.state_root(state_normal) == state_db.state_root( - state_optimized - ) diff --git a/tests/tangerine_whistle/optimized/__init__.py b/tests/tangerine_whistle/optimized/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/tests/tangerine_whistle/optimized/test_state_db.py b/tests/tangerine_whistle/optimized/test_state_db.py deleted file mode 100644 index 219197c283..0000000000 --- a/tests/tangerine_whistle/optimized/test_state_db.py +++ /dev/null @@ -1,66 +0,0 @@ -import sys -from typing import Any - -import pytest - -import ethereum.tangerine_whistle.state as state -from ethereum.base_types import U256 -from ethereum.tangerine_whistle.fork_types import EMPTY_ACCOUNT -from ethereum.tangerine_whistle.utils.hexadecimal import hex_to_address - -try: - import ethereum_optimized.tangerine_whistle.state_db as state_db -except ImportError: - pass - - -ADDRESS_FOO = hex_to_address("0x00000000219ab540356cbb839cbe05303d7705fa") -STORAGE_FOO = U256(101).to_be_bytes32() - - -@pytest.mark.skipif( - "ethereum_optimized.tangerine_whistle.state_db" not in sys.modules, - reason="missing dependency (use `pip install 'ethereum[optimized]'`)", -) -def test_storage_key() -> None: - def actions(impl: Any) -> Any: - obj = impl.State() - impl.set_account(obj, ADDRESS_FOO, EMPTY_ACCOUNT) - impl.set_storage(obj, ADDRESS_FOO, STORAGE_FOO, U256(42)) - impl.state_root(obj) - return obj - - state_normal = actions(state) - state_optimized = actions(state_db) - assert state.get_storage( - state_normal, ADDRESS_FOO, STORAGE_FOO - ) == state_db.get_storage(state_optimized, ADDRESS_FOO, STORAGE_FOO) - assert state.state_root(state_normal) == state_db.state_root( - state_optimized - ) - - -@pytest.mark.skipif( - "ethereum_optimized.tangerine_whistle.state_db" not in sys.modules, - reason="missing dependency (use `pip install 'ethereum[optimized]'`)", -) -def test_resurrection() -> None: - def actions(impl: Any) -> Any: - obj = impl.State() - impl.set_account(obj, ADDRESS_FOO, EMPTY_ACCOUNT) - impl.set_storage(obj, ADDRESS_FOO, STORAGE_FOO, U256(42)) - impl.state_root(obj) - impl.destroy_storage(obj, ADDRESS_FOO) - impl.state_root(obj) - impl.set_account(obj, ADDRESS_FOO, EMPTY_ACCOUNT) - return obj - - state_normal = actions(state) - state_optimized = actions(state_db) - state_db.state_root(state_optimized) - assert state.get_storage( - state_normal, ADDRESS_FOO, STORAGE_FOO - ) == state_db.get_storage(state_optimized, ADDRESS_FOO, STORAGE_FOO) - assert state.state_root(state_normal) == state_db.state_root( - state_optimized - ) diff --git a/tests/frontier/optimized/test_state_db.py b/tests/test_optimized_state.py similarity index 60% rename from tests/frontier/optimized/test_state_db.py rename to tests/test_optimized_state.py index 88f417ffc5..b09e9d43a3 100644 --- a/tests/frontier/optimized/test_state_db.py +++ b/tests/test_optimized_state.py @@ -1,15 +1,26 @@ import sys -from typing import Any +from typing import Any, cast import pytest import ethereum.frontier.state as state from ethereum.base_types import U256 from ethereum.frontier.fork_types import EMPTY_ACCOUNT -from ethereum.frontier.utils.hexadecimal import hex_to_address +from ethereum.tangerine_whistle.utils.hexadecimal import hex_to_address try: - import ethereum_optimized.frontier.state_db as state_db + import ethereum_optimized.state_db as state_db + + class OptimizedState: + pass + + optimized_state = cast(Any, OptimizedState()) + + for (name, value) in state_db.get_optimized_state_patches( + "frontier" + ).items(): + setattr(optimized_state, name, value) + except ImportError: pass @@ -19,7 +30,7 @@ @pytest.mark.skipif( - "ethereum_optimized.frontier.state_db" not in sys.modules, + "ethereum_optimized.state_db" not in sys.modules, reason="missing dependency (use `pip install 'ethereum[optimized]'`)", ) def test_storage_key() -> None: @@ -31,17 +42,17 @@ def actions(impl: Any) -> Any: return obj state_normal = actions(state) - state_optimized = actions(state_db) + state_optimized = actions(optimized_state) assert state.get_storage( state_normal, ADDRESS_FOO, STORAGE_FOO - ) == state_db.get_storage(state_optimized, ADDRESS_FOO, STORAGE_FOO) - assert state.state_root(state_normal) == state_db.state_root( + ) == optimized_state.get_storage(state_optimized, ADDRESS_FOO, STORAGE_FOO) + assert state.state_root(state_normal) == optimized_state.state_root( state_optimized ) @pytest.mark.skipif( - "ethereum_optimized.frontier.state_db" not in sys.modules, + "ethereum_optimized.state_db" not in sys.modules, reason="missing dependency (use `pip install 'ethereum[optimized]'`)", ) def test_resurrection() -> None: @@ -56,11 +67,11 @@ def actions(impl: Any) -> Any: return obj state_normal = actions(state) - state_optimized = actions(state_db) - state_db.state_root(state_optimized) + state_optimized = actions(optimized_state) + optimized_state.state_root(state_optimized) assert state.get_storage( state_normal, ADDRESS_FOO, STORAGE_FOO - ) == state_db.get_storage(state_optimized, ADDRESS_FOO, STORAGE_FOO) - assert state.state_root(state_normal) == state_db.state_root( + ) == optimized_state.get_storage(state_optimized, ADDRESS_FOO, STORAGE_FOO) + assert state.state_root(state_normal) == optimized_state.state_root( state_optimized )