diff --git a/README.md b/README.md index 33d0ba0..e90ce0a 100644 --- a/README.md +++ b/README.md @@ -6,10 +6,10 @@ A collection of [msgspec.Struct](https://jcristharif.com/msgspec/structs.html) d #### Modules: -- [block](https://bobthebuidler.github.io/evmspec/source/evmspec.html#module-evmspec.block) +- [block](https://bobthebuidler.github.io/evmspec/source/evmspec.html#module-evmspec.structs.block) - [log](https://bobthebuidler.github.io/evmspec/source/evmspec.html#module-evmspec.log) - [receipt](https://bobthebuidler.github.io/evmspec/source/evmspec.html#module-evmspec.receipt) -- [trace](https://bobthebuidler.github.io/evmspec/source/evmspec.trace.html) +- [trace](https://bobthebuidler.github.io/evmspec/source/evmspec.structs.trace.html) - [transaction](https://bobthebuidler.github.io/evmspec/source/evmspec.html#module-evmspec.transaction) - and more diff --git a/evmspec/__init__.py b/evmspec/__init__.py index 40715ab..fd8241e 100644 --- a/evmspec/__init__.py +++ b/evmspec/__init__.py @@ -1,12 +1,16 @@ -from evmspec import block, structs, trace, transaction +from evmspec import structs from evmspec.structs import ( ErigonBlockHeader, FilterTrace, FullTransactionReceipt, + Log, TransactionReceipt, + block, header, + trace, + transaction, ) -from evmspec.transaction import ( +from evmspec.structs.transaction import ( TransactionRLP, TransactionLegacy, Transaction1559, @@ -26,6 +30,8 @@ "structs", # - header "ErigonBlockHeader", + # - log + "Log", # - receipt "FullTransactionReceipt", "TransactionReceipt", @@ -44,12 +50,12 @@ This library provides a collection of msgspec.Struct definitions for use with the Ethereum Virtual Machine. Modules: - :mod:`~evmspec.block`: Contains structures related to Ethereum blocks. - :mod:`~evmspec.header`: Contains structures related to Ethereum block headers. - :mod:`~evmspec.log`: Contains structures related to Ethereum logs. - :mod:`~evmspec.receipt`: Contains structures related to Ethereum transaction receipts. - :mod:`~evmspec.trace`: Contains structures related to Ethereum transaction traces. - :mod:`~evmspec.transaction`: Contains structures related to Ethereum transactions. + :mod:`~evmspec.structs.block`: Contains structures related to Ethereum blocks. + :mod:`~evmspec.structs.header`: Contains structures related to Ethereum block headers. + :mod:`~evmspec.structs.log`: Contains structures related to Ethereum logs. + :mod:`~evmspec.structs.receipt`: Contains structures related to Ethereum transaction receipts. + :mod:`~evmspec.structs.trace`: Contains structures related to Ethereum transaction traces. + :mod:`~evmspec.structs.transaction`: Contains structures related to Ethereum transactions. Structs: :class:`~evmspec.ErigonBlockHeader`: Represents a block header in the Erigon client. diff --git a/evmspec/data/__init__.py b/evmspec/data/__init__.py index 76c102c..9bc285f 100644 --- a/evmspec/data/__init__.py +++ b/evmspec/data/__init__.py @@ -1,5 +1,6 @@ from evmspec.data import uints -from evmspec.data.main import ( +from evmspec.data._ids import ChainId, LogIndex, TransactionIndex +from evmspec.data._main import ( Address, BlockHash, BlockNumber, @@ -23,4 +24,7 @@ "uints", "UnixTimestamp", "Nonce", + "ChainId", + "LogIndex", + "TransactionIndex", ] diff --git a/evmspec/_enum.py b/evmspec/data/_enum.py similarity index 100% rename from evmspec/_enum.py rename to evmspec/data/_enum.py diff --git a/evmspec/_ids.py b/evmspec/data/_ids.py similarity index 98% rename from evmspec/_ids.py rename to evmspec/data/_ids.py index f6d7df2..8a0d37e 100644 --- a/evmspec/_ids.py +++ b/evmspec/data/_ids.py @@ -1,4 +1,4 @@ -from evmspec.data import uint +from evmspec.data._main import uint class IntId(uint): @@ -146,4 +146,4 @@ class LogIndex(IntId): See Also: - :class:`IntId` - """ \ No newline at end of file + """ diff --git a/evmspec/data/main.py b/evmspec/data/_main.py similarity index 90% rename from evmspec/data/main.py rename to evmspec/data/_main.py index 0e88085..4b8e281 100644 --- a/evmspec/data/main.py +++ b/evmspec/data/_main.py @@ -5,7 +5,7 @@ from typing import TYPE_CHECKING, Any, Callable, Tuple, Type, TypeVar, Union from cachetools.func import ttl_cache -from eth_utils import to_checksum_address +from cchecksum import to_checksum_address from hexbytes import HexBytes from msgspec import Raw, Struct, json from typing_extensions import Self @@ -17,16 +17,17 @@ _T = TypeVar("_T") """A generic type variable.""" -_DecodeHook = Callable[[Type[_T], Any], _T] +DecodeHook = Callable[[Type[_T], Any], _T] """A type alias for a function that decodes an object into a specific type.""" class Address(str): """ - Represents an Ethereum address with checksum validation. + Represents an Ethereum address in its EIP-55 checksum format. This class ensures that any Ethereum address is stored in its checksummed format, - which is a mixed-case encoding of the address that includes a checksum. + as defined by EIP-55. It uses a custom Cython implementation for the checksum + conversion to optimize performance. Examples: >>> addr = Address("0x52908400098527886E0F7030069857D2E4169EE7") @@ -34,12 +35,16 @@ class Address(str): 0x52908400098527886E0F7030069857D2E4169EE7 See Also: - - `eth_utils.to_checksum_address`: Function used for checksum validation. + - `cchecksum.to_checksum_address`: Function used for checksum conversion. """ def __new__(cls, address: str): """Creates a new Address instance with checksum validation. + This function takes a hex address and returns it in the checksummed format + as defined by EIP-55. It uses a custom Cython implementation for the + checksum conversion to optimize performance. + Args: address: A string representing the Ethereum address. @@ -49,6 +54,9 @@ def __new__(cls, address: str): Examples: >>> Address("0xde0B295669a9FD93d5F28D9Ec85E40f4cb697BAe") Address('0xDe0B295669a9FD93d5F28D9Ec85E40f4cb697BAe') + + See Also: + - `cchecksum.to_checksum_address`: Function used for checksum conversion. """ return super().__new__(cls, to_checksum_address(address)) @@ -56,6 +64,10 @@ def __new__(cls, address: str): def _decode_hook(cls, typ: Type["Address"], obj: str): """Decodes an object into an Address instance with checksum validation. + This function takes a hex address and returns it in the checksummed format + as defined by EIP-55. It uses a custom Cython implementation for the + checksum conversion to optimize performance. + Args: typ: The type that is expected to be decoded to. obj: The object to decode, expected to be a string representation of an Ethereum address. @@ -69,6 +81,9 @@ def _decode_hook(cls, typ: Type["Address"], obj: str): Note: This method utilizes :meth:`cls.checksum` as a class method to ensure the address is checksummed. + + See Also: + - `cchecksum.to_checksum_address`: Function used for checksum conversion. """ return cls.checksum(obj) @@ -77,6 +92,10 @@ def _decode_hook(cls, typ: Type["Address"], obj: str): def checksum(cls, address: str) -> Self: """Returns the checksummed version of the address. + This function takes a hex address and returns it in the checksummed format + as defined by EIP-55. It uses a custom Cython implementation for the + checksum conversion to optimize performance. + Args: address: A string representing the Ethereum address. @@ -86,6 +105,9 @@ def checksum(cls, address: str) -> Self: Examples: >>> Address.checksum("0xde0B295669a9FD93d5F28D9Ec85E40f4cb697BAe") Address('0xDe0B295669a9FD93d5F28D9Ec85E40f4cb697BAe') + + See Also: + - `cchecksum.to_checksum_address`: Function used for checksum conversion. """ return cls(address) @@ -377,7 +399,7 @@ class TransactionHash(HexBytes32): async def get_receipt( self, decode_to: ReceiptDataType, - decode_hook: _DecodeHook[ReceiptDataType] = _decode_hook, + decode_hook: DecodeHook[ReceiptDataType] = _decode_hook, ) -> "TransactionReceipt": """Async method to get the transaction receipt. diff --git a/evmspec/data/uints.py b/evmspec/data/uints.py index df8a191..1df5411 100644 --- a/evmspec/data/uints.py +++ b/evmspec/data/uints.py @@ -12,7 +12,7 @@ from hexbytes import HexBytes -from evmspec.data.main import uint +from evmspec.data._main import uint class _UintData(uint): @@ -139,4 +139,4 @@ class uint256(_UintData): ) setattr(sys.modules[__name__], cls_name, new_cls) -__all__ = [f"uint{bytes*8}" for bytes in range(1, 32)] \ No newline at end of file +__all__ = [f"uint{bytes*8}" for bytes in range(1, 32)] diff --git a/evmspec/structs/__init__.py b/evmspec/structs/__init__.py index 6e0dd9a..b197305 100644 --- a/evmspec/structs/__init__.py +++ b/evmspec/structs/__init__.py @@ -1,10 +1,21 @@ from evmspec.structs.header import ErigonBlockHeader +from evmspec.structs.log import Log from evmspec.structs.receipt import FullTransactionReceipt, TransactionReceipt from evmspec.structs.trace import FilterTrace +from evmspec.structs.transaction import ( + Transaction, + AnyTransaction, + TransactionRLP, + TransactionLegacy, + Transaction1559, + Transaction2930, +) __all__ = [ # - header "ErigonBlockHeader", + # - log + "Log", # - receipt "FullTransactionReceipt", "TransactionReceipt", diff --git a/evmspec/block.py b/evmspec/structs/block.py similarity index 96% rename from evmspec/block.py rename to evmspec/structs/block.py index f7a0ff6..281a26e 100644 --- a/evmspec/block.py +++ b/evmspec/structs/block.py @@ -6,9 +6,19 @@ from hexbytes import HexBytes from msgspec import UNSET, Raw, ValidationError, field, json -from evmspec._ids import IntId -from evmspec.data import * -from evmspec.transaction import Transaction, TransactionRLP +from evmspec.data import ( + Address, + BlockHash, + BlockNumber, + Nonce, + TransactionHash, + UnixTimestamp, + Wei, + _decode_hook, + uint, +) +from evmspec.data._ids import IntId +from evmspec.structs.transaction import Transaction, TransactionRLP logger = logging.getLogger(__name__) @@ -239,4 +249,4 @@ def withdrawals(self) -> Tuple[StakingWithdrawal, ...]: """ return json.decode( self._withdrawals, type=Tuple[StakingWithdrawal, ...], dec_hook=_decode_hook - ) \ No newline at end of file + ) diff --git a/evmspec/structs/header.py b/evmspec/structs/header.py index 59fbc5b..b1bcdbb 100644 --- a/evmspec/structs/header.py +++ b/evmspec/structs/header.py @@ -1,6 +1,5 @@ -from hexbytes import HexBytes - from dictstruct import LazyDictStruct +from hexbytes import HexBytes from evmspec.data import Address, UnixTimestamp, uint @@ -112,4 +111,4 @@ class ErigonBlockHeader(LazyDictStruct, frozen=True, kw_only=True, forbid_unknow ... ) >>> header.difficulty uint(1000) - """ \ No newline at end of file + """ diff --git a/evmspec/structs/log.py b/evmspec/structs/log.py index 821be79..5ea1fe6 100644 --- a/evmspec/structs/log.py +++ b/evmspec/structs/log.py @@ -3,7 +3,6 @@ from dictstruct import LazyDictStruct from hexbytes import HexBytes -from evmspec._ids import LogIndex, TransactionIndex from evmspec.data import ( Address, BlockHash, @@ -13,6 +12,8 @@ uint, uints, ) +from evmspec.data._ids import LogIndex, TransactionIndex + _ADDRESS_TOPIC_PREFIX = HexBytes("0") * 12 diff --git a/evmspec/structs/receipt.py b/evmspec/structs/receipt.py index d9d042b..201e757 100644 --- a/evmspec/structs/receipt.py +++ b/evmspec/structs/receipt.py @@ -7,8 +7,8 @@ from hexbytes import HexBytes from msgspec import UNSET, Raw, field, json -from evmspec._ids import TransactionIndex -from evmspec.data import Address, BlockNumber, TransactionHash, Wei, uint, _decode_hook +from evmspec.data import Address, BlockNumber, TransactionHash, Wei, _decode_hook, uint +from evmspec.data._ids import TransactionIndex from evmspec.structs.log import Log diff --git a/evmspec/structs/trace/__init__.py b/evmspec/structs/trace/__init__.py index 3ab7305..4a07b13 100644 --- a/evmspec/structs/trace/__init__.py +++ b/evmspec/structs/trace/__init__.py @@ -1,30 +1,30 @@ from typing import Union -from evmspec.trace import call, create, reward, suicide +from evmspec.structs.trace import call, create, reward, suicide FilterTrace = Union[call.Trace, create.Trace, reward.Trace, suicide.Trace] """A type alias for filtering trace types. FilterTrace is a Union of the following trace types: -- :class:`evmspec.trace.call.Trace` -- :class:`evmspec.trace.create.Trace` -- :class:`evmspec.trace.reward.Trace` -- :class:`evmspec.trace.suicide.Trace` +- :class:`evmspec.structs.trace.call.Trace` +- :class:`evmspec.structs.trace.create.Trace` +- :class:`evmspec.structs.trace.reward.Trace` +- :class:`evmspec.structs.trace.suicide.Trace` Examples: You can use `FilterTrace` to specify a variable that can hold any of the trace types: - >>> from evmspec.trace import FilterTrace + >>> from evmspec.structs.trace import FilterTrace >>> trace: FilterTrace = call.Trace(...) >>> trace = create.Trace(...) >>> trace = reward.Trace(...) >>> trace = suicide.Trace(...) See Also: - - :class:`evmspec.trace.call.Trace` - - :class:`evmspec.trace.create.Trace` - - :class:`evmspec.trace.reward.Trace` - - :class:`evmspec.trace.suicide.Trace` + - :class:`evmspec.structs.trace.call.Trace` + - :class:`evmspec.structs.trace.create.Trace` + - :class:`evmspec.structs.trace.reward.Trace` + - :class:`evmspec.structs.trace.suicide.Trace` """ __all__ = ["call", "create", "reward", "suicide", "FilterTrace"] diff --git a/evmspec/structs/trace/_base.py b/evmspec/structs/trace/_base.py index ea703fc..2ad2d40 100644 --- a/evmspec/structs/trace/_base.py +++ b/evmspec/structs/trace/_base.py @@ -24,7 +24,7 @@ class _ActionBase( internal purposes. Examples: - >>> from evmspec.trace._base import _ActionBase + >>> from evmspec.structs.trace._base import _ActionBase >>> class MyAction(_ActionBase): ... pass >>> action = MyAction(sender="0xabc...", value=1000, gas=21000) @@ -95,7 +95,7 @@ class _ResultBase( internal purposes. Examples: - >>> from evmspec.trace._base import _ResultBase + >>> from evmspec.structs.trace._base import _ResultBase >>> class MyResult(_ResultBase): ... pass >>> result = MyResult(gasUsed=21000) @@ -138,7 +138,7 @@ class _FilterTraceBase( internal purposes. Examples: - >>> from evmspec.trace._base import _FilterTraceBase + >>> from evmspec.structs.trace._base import _FilterTraceBase >>> class MyTrace(_FilterTraceBase): ... pass >>> trace = MyTrace(blockNumber=123456, blockHash="0xabc...", transactionHash="0xdef...", transactionPosition=1, traceAddress=[0, 1], subtraces=2) @@ -238,4 +238,4 @@ def block(self) -> BlockNumber: >>> trace.block 123456 """ - return self.blockNumber \ No newline at end of file + return self.blockNumber diff --git a/evmspec/structs/trace/call.py b/evmspec/structs/trace/call.py index fe6c503..65c9bd5 100644 --- a/evmspec/structs/trace/call.py +++ b/evmspec/structs/trace/call.py @@ -5,9 +5,9 @@ from hexbytes import HexBytes from msgspec import UNSET, Raw, field, json -from evmspec._enum import StringToIntEnumMeta from evmspec.data import Address, _decode_hook -from evmspec.trace._base import _ActionBase, _FilterTraceBase, _ResultBase +from evmspec.data._enum import StringToIntEnumMeta +from evmspec.structs.trace._base import _ActionBase, _FilterTraceBase, _ResultBase class Type(Enum, metaclass=StringToIntEnumMeta): diff --git a/evmspec/structs/trace/create.py b/evmspec/structs/trace/create.py index d8614b8..ad764cf 100644 --- a/evmspec/structs/trace/create.py +++ b/evmspec/structs/trace/create.py @@ -5,7 +5,7 @@ from msgspec import Raw, field, json from evmspec.data import Address, _decode_hook -from evmspec.trace._base import _ActionBase, _FilterTraceBase, _ResultBase +from evmspec.structs.trace._base import _ActionBase, _FilterTraceBase, _ResultBase class Action( @@ -27,7 +27,7 @@ class Action( HexBytes('0x6000600055') See Also: - - :class:`evmspec.trace._base._ActionBase` + - :class:`evmspec.structs.trace._base._ActionBase` """ init: HexBytes @@ -56,7 +56,7 @@ class Result( HexBytes('0x6000600055') See Also: - - :class:`evmspec.trace._base._ResultBase` + - :class:`evmspec.structs.trace._base._ResultBase` """ address: Address @@ -89,7 +89,7 @@ class Trace( HexBytes('0x6000600055') See Also: - - :class:`evmspec.trace._base._FilterTraceBase` + - :class:`evmspec.structs.trace._base._FilterTraceBase` - :class:`Action` - :class:`Result` """ @@ -139,4 +139,4 @@ def action(self) -> Action: See Also: - :class:`Result` for more details on the result structure. - """ \ No newline at end of file + """ diff --git a/evmspec/structs/trace/reward.py b/evmspec/structs/trace/reward.py index 35845c4..a0b39a3 100644 --- a/evmspec/structs/trace/reward.py +++ b/evmspec/structs/trace/reward.py @@ -4,9 +4,9 @@ from msgspec import Raw, field, json -from evmspec._enum import StringToIntEnumMeta from evmspec.data import Address, _decode_hook -from evmspec.trace._base import _ActionBase, _FilterTraceBase +from evmspec.data._enum import StringToIntEnumMeta +from evmspec.structs.trace._base import _ActionBase, _FilterTraceBase class Type(Enum, metaclass=StringToIntEnumMeta): diff --git a/evmspec/structs/trace/suicide.py b/evmspec/structs/trace/suicide.py index f8731de..d54d85b 100644 --- a/evmspec/structs/trace/suicide.py +++ b/evmspec/structs/trace/suicide.py @@ -1,6 +1,6 @@ from typing import ClassVar, Literal -from evmspec.trace._base import _ActionBase, _FilterTraceBase +from evmspec.structs.trace._base import _ActionBase, _FilterTraceBase class Action( @@ -25,7 +25,7 @@ class Action( - :class:`_ActionBase` for common action attributes. Examples: - >>> from evmspec.trace.suicide import Action + >>> from evmspec.structs.trace.suicide import Action >>> from evmspec.data import Address >>> action = Action(sender=Address("0x1234567890abcdef1234567890abcdef12345678"), value=1000, gas=21000) >>> action.sender @@ -58,7 +58,7 @@ class Trace( - :class:`Action` for details on the action attribute. Examples: - >>> from evmspec.trace.suicide import Trace, Action + >>> from evmspec.structs.trace.suicide import Trace, Action >>> from evmspec.data import Address >>> action = Action(sender=Address("0x1234567890abcdef1234567890abcdef12345678"), value=1000, gas=21000) >>> trace = Trace( @@ -94,4 +94,4 @@ class Trace( """The suicide action, parity style.""" result: Literal[None] - """Explicitly set to None, indicating no meaningful result is expected from a contract self-destruct operation.""" \ No newline at end of file + """Explicitly set to None, indicating no meaningful result is expected from a contract self-destruct operation.""" diff --git a/evmspec/transaction.py b/evmspec/structs/transaction.py similarity index 96% rename from evmspec/transaction.py rename to evmspec/structs/transaction.py index 865fec8..27ff60a 100644 --- a/evmspec/transaction.py +++ b/evmspec/structs/transaction.py @@ -5,8 +5,17 @@ from hexbytes import HexBytes from msgspec import UNSET, Raw, field, json -from evmspec._ids import ChainId, TransactionIndex -from evmspec.data import * +from evmspec.data import ( + Address, + BlockHash, + BlockNumber, + HexBytes32, + Nonce, + TransactionHash, + Wei, + uint, +) +from evmspec.data._ids import ChainId, TransactionIndex class AccessListEntry(LazyDictStruct, frozen=True, forbid_unknown_fields=True): # type: ignore [call-arg] @@ -214,4 +223,4 @@ class Transaction1559(_TransactionBase, tag="0x2", frozen=True, kw_only=True, fo Transaction = Union[TransactionLegacy, Transaction2930, Transaction1559] -AnyTransaction = Union[Transaction, TransactionRLP] \ No newline at end of file +AnyTransaction = Union[Transaction, TransactionRLP] diff --git a/pyproject.toml b/pyproject.toml index 1cad7b6..5e01f20 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [tool.poetry] name = "evmspec" -version = "0.0.7" +version = "0.1.0" description = "A collection of msgspec.Struct definitions for use with the Ethereum Virtual Machine" authors = ["BobTheBuidler "] diff --git a/tests/test_data.py b/tests/test_data.py new file mode 100644 index 0000000..65b6ca1 --- /dev/null +++ b/tests/test_data.py @@ -0,0 +1 @@ +from evmspec.data import * diff --git a/tests/test_structs.py b/tests/test_structs.py new file mode 100644 index 0000000..5ff1c9b --- /dev/null +++ b/tests/test_structs.py @@ -0,0 +1 @@ +from evmspec.structs import *