faster-eth-abi 5.2.25__cp314-cp314-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- faster_eth_abi/__init__.py +17 -0
- faster_eth_abi/_codec.cpython-314-i386-linux-gnu.so +0 -0
- faster_eth_abi/_codec.py +83 -0
- faster_eth_abi/_decoding.cpython-314-i386-linux-gnu.so +0 -0
- faster_eth_abi/_decoding.py +371 -0
- faster_eth_abi/_encoding.cpython-314-i386-linux-gnu.so +0 -0
- faster_eth_abi/_encoding.py +514 -0
- faster_eth_abi/_grammar.cpython-314-i386-linux-gnu.so +0 -0
- faster_eth_abi/_grammar.py +389 -0
- faster_eth_abi/abi.cpython-314-i386-linux-gnu.so +0 -0
- faster_eth_abi/abi.py +17 -0
- faster_eth_abi/base.py +45 -0
- faster_eth_abi/codec.py +2809 -0
- faster_eth_abi/constants.cpython-314-i386-linux-gnu.so +0 -0
- faster_eth_abi/constants.py +7 -0
- faster_eth_abi/decoding.py +555 -0
- faster_eth_abi/encoding.py +738 -0
- faster_eth_abi/exceptions.cpython-314-i386-linux-gnu.so +0 -0
- faster_eth_abi/exceptions.py +127 -0
- faster_eth_abi/from_type_str.cpython-314-i386-linux-gnu.so +0 -0
- faster_eth_abi/from_type_str.py +141 -0
- faster_eth_abi/grammar.py +179 -0
- faster_eth_abi/io.cpython-314-i386-linux-gnu.so +0 -0
- faster_eth_abi/io.py +137 -0
- faster_eth_abi/packed.cpython-314-i386-linux-gnu.so +0 -0
- faster_eth_abi/packed.py +19 -0
- faster_eth_abi/py.typed +0 -0
- faster_eth_abi/registry.py +758 -0
- faster_eth_abi/tools/__init__.cpython-314-i386-linux-gnu.so +0 -0
- faster_eth_abi/tools/__init__.py +3 -0
- faster_eth_abi/tools/_strategies.cpython-314-i386-linux-gnu.so +0 -0
- faster_eth_abi/tools/_strategies.py +247 -0
- faster_eth_abi/typing.py +4627 -0
- faster_eth_abi/utils/__init__.cpython-314-i386-linux-gnu.so +0 -0
- faster_eth_abi/utils/__init__.py +0 -0
- faster_eth_abi/utils/localcontext.cpython-314-i386-linux-gnu.so +0 -0
- faster_eth_abi/utils/localcontext.py +49 -0
- faster_eth_abi/utils/numeric.cpython-314-i386-linux-gnu.so +0 -0
- faster_eth_abi/utils/numeric.py +117 -0
- faster_eth_abi/utils/padding.cpython-314-i386-linux-gnu.so +0 -0
- faster_eth_abi/utils/padding.py +22 -0
- faster_eth_abi/utils/string.cpython-314-i386-linux-gnu.so +0 -0
- faster_eth_abi/utils/string.py +19 -0
- faster_eth_abi/utils/validation.cpython-314-i386-linux-gnu.so +0 -0
- faster_eth_abi/utils/validation.py +18 -0
- faster_eth_abi-5.2.25.dist-info/METADATA +134 -0
- faster_eth_abi-5.2.25.dist-info/RECORD +50 -0
- faster_eth_abi-5.2.25.dist-info/WHEEL +7 -0
- faster_eth_abi-5.2.25.dist-info/top_level.txt +2 -0
- faster_eth_abi__mypyc.cpython-314-i386-linux-gnu.so +0 -0
|
Binary file
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
# mypy: disable-error-code="misc"
|
|
2
|
+
# cannot subclass `Any`
|
|
3
|
+
|
|
4
|
+
"""
|
|
5
|
+
Exception classes for error handling during ABI encoding and decoding operations.
|
|
6
|
+
|
|
7
|
+
faster-eth-abi exceptions always inherit from eth-abi exceptions, so porting to faster-eth-abi
|
|
8
|
+
does not require any change to your existing exception handlers. They will continue to work.
|
|
9
|
+
"""
|
|
10
|
+
|
|
11
|
+
import eth_abi.exceptions
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
class EncodingError(eth_abi.exceptions.EncodingError):
|
|
15
|
+
"""
|
|
16
|
+
Base exception for any error that occurs during encoding.
|
|
17
|
+
"""
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
class EncodingTypeError(EncodingError, eth_abi.exceptions.EncodingTypeError):
|
|
21
|
+
"""
|
|
22
|
+
Raised when trying to encode a python value whose type is not supported for
|
|
23
|
+
the output ABI type.
|
|
24
|
+
"""
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
class IllegalValue(EncodingError, eth_abi.exceptions.IllegalValue):
|
|
28
|
+
"""
|
|
29
|
+
Raised when trying to encode a python value with the correct type but with
|
|
30
|
+
a value that is not considered legal for the output ABI type.
|
|
31
|
+
|
|
32
|
+
.. code-block:: python
|
|
33
|
+
|
|
34
|
+
fixed128x19_encoder(Decimal('NaN')) # cannot encode NaN
|
|
35
|
+
|
|
36
|
+
"""
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
class ValueOutOfBounds(IllegalValue, eth_abi.exceptions.ValueOutOfBounds):
|
|
40
|
+
"""
|
|
41
|
+
Raised when trying to encode a python value with the correct type but with
|
|
42
|
+
a value that appears outside the range of valid values for the output ABI
|
|
43
|
+
type.
|
|
44
|
+
|
|
45
|
+
.. code-block:: python
|
|
46
|
+
|
|
47
|
+
ufixed8x1_encoder(Decimal('25.6')) # out of bounds
|
|
48
|
+
|
|
49
|
+
"""
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
class DecodingError(eth_abi.exceptions.DecodingError):
|
|
53
|
+
"""
|
|
54
|
+
Base exception for any error that occurs during decoding.
|
|
55
|
+
"""
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
class InsufficientDataBytes(DecodingError, eth_abi.exceptions.InsufficientDataBytes):
|
|
59
|
+
"""
|
|
60
|
+
Raised when there are insufficient data to decode a value for a given ABI type.
|
|
61
|
+
"""
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
class NonEmptyPaddingBytes(DecodingError, eth_abi.exceptions.NonEmptyPaddingBytes):
|
|
65
|
+
"""
|
|
66
|
+
Raised when the padding bytes of an ABI value are malformed.
|
|
67
|
+
"""
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
class InvalidPointer(DecodingError, eth_abi.exceptions.InvalidPointer):
|
|
71
|
+
"""
|
|
72
|
+
Raised when the pointer to a value in the ABI encoding is invalid.
|
|
73
|
+
"""
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
class ParseError(eth_abi.exceptions.ParseError):
|
|
77
|
+
"""
|
|
78
|
+
Raised when an ABI type string cannot be parsed.
|
|
79
|
+
"""
|
|
80
|
+
|
|
81
|
+
def __str__(self) -> str:
|
|
82
|
+
return (
|
|
83
|
+
f"Parse error at '{self.text[self.pos : self.pos + 5]}' "
|
|
84
|
+
f"(column {self.column()}) in type string '{self.text}'"
|
|
85
|
+
)
|
|
86
|
+
|
|
87
|
+
|
|
88
|
+
class ABITypeError(eth_abi.exceptions.ABITypeError):
|
|
89
|
+
"""
|
|
90
|
+
Raised when a parsed ABI type has inconsistent properties; for example,
|
|
91
|
+
when trying to parse the type string ``'uint7'`` (which has a bit-width
|
|
92
|
+
that is not congruent with zero modulo eight).
|
|
93
|
+
"""
|
|
94
|
+
|
|
95
|
+
|
|
96
|
+
class PredicateMappingError(eth_abi.exceptions.PredicateMappingError):
|
|
97
|
+
"""
|
|
98
|
+
Raised when an error occurs in a registry's internal mapping.
|
|
99
|
+
"""
|
|
100
|
+
|
|
101
|
+
|
|
102
|
+
class NoEntriesFound(PredicateMappingError, eth_abi.exceptions.NoEntriesFound):
|
|
103
|
+
"""
|
|
104
|
+
Raised when no registration is found for a type string in a registry's
|
|
105
|
+
internal mapping.
|
|
106
|
+
|
|
107
|
+
.. warning::
|
|
108
|
+
|
|
109
|
+
In a future version of ``faster-eth-abi``, this error class will no longer
|
|
110
|
+
inherit from ``ValueError``.
|
|
111
|
+
"""
|
|
112
|
+
|
|
113
|
+
|
|
114
|
+
class MultipleEntriesFound(
|
|
115
|
+
PredicateMappingError, eth_abi.exceptions.MultipleEntriesFound
|
|
116
|
+
):
|
|
117
|
+
"""
|
|
118
|
+
Raised when multiple registrations are found for a type string in a
|
|
119
|
+
registry's internal mapping. This error is non-recoverable and indicates
|
|
120
|
+
that a registry was configured incorrectly. Registrations are expected to
|
|
121
|
+
cover completely distinct ranges of type strings.
|
|
122
|
+
|
|
123
|
+
.. warning::
|
|
124
|
+
|
|
125
|
+
In a future version of ``faster-eth-abi``, this error class will no longer
|
|
126
|
+
inherit from ``ValueError``.
|
|
127
|
+
"""
|
|
Binary file
|
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
"""Helpers for parsing and normalizing ABI type strings.
|
|
2
|
+
|
|
3
|
+
Provides decorators and utilities for implementing from_type_str methods on coder classes.
|
|
4
|
+
"""
|
|
5
|
+
import functools
|
|
6
|
+
from typing import (
|
|
7
|
+
TYPE_CHECKING,
|
|
8
|
+
Any,
|
|
9
|
+
Callable,
|
|
10
|
+
Optional,
|
|
11
|
+
Type,
|
|
12
|
+
TypeVar,
|
|
13
|
+
)
|
|
14
|
+
|
|
15
|
+
from eth_typing import (
|
|
16
|
+
TypeStr,
|
|
17
|
+
)
|
|
18
|
+
|
|
19
|
+
from ._grammar import (
|
|
20
|
+
ABIType,
|
|
21
|
+
BasicType,
|
|
22
|
+
TupleType,
|
|
23
|
+
normalize,
|
|
24
|
+
)
|
|
25
|
+
from .grammar import (
|
|
26
|
+
parse,
|
|
27
|
+
)
|
|
28
|
+
|
|
29
|
+
if TYPE_CHECKING:
|
|
30
|
+
from .base import (
|
|
31
|
+
BaseCoder,
|
|
32
|
+
)
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
TType = TypeVar("TType", bound=Type["BaseCoder"])
|
|
36
|
+
OldFromTypeStr = Callable[["BaseCoder", ABIType, Any], TType]
|
|
37
|
+
if TYPE_CHECKING:
|
|
38
|
+
NewFromTypeStr = classmethod[TType, [TypeStr, Any], TType]
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
def parse_type_str(
|
|
42
|
+
expected_base: Optional[str] = None,
|
|
43
|
+
with_arrlist: bool = False,
|
|
44
|
+
) -> Callable[[OldFromTypeStr[TType]], "NewFromTypeStr[TType]"]:
|
|
45
|
+
"""
|
|
46
|
+
Used by BaseCoder subclasses as a convenience for implementing the
|
|
47
|
+
``from_type_str`` method required by ``ABIRegistry``. Useful if normalizing
|
|
48
|
+
then parsing a type string with an (optional) expected base is required in
|
|
49
|
+
that method.
|
|
50
|
+
"""
|
|
51
|
+
|
|
52
|
+
def decorator(old_from_type_str: OldFromTypeStr[TType]) -> "NewFromTypeStr[TType]":
|
|
53
|
+
@functools.wraps(old_from_type_str)
|
|
54
|
+
def new_from_type_str(cls: TType, type_str: TypeStr, registry: Any) -> TType:
|
|
55
|
+
normalized_type_str = normalize(type_str)
|
|
56
|
+
abi_type = parse(normalized_type_str)
|
|
57
|
+
|
|
58
|
+
type_str_repr = repr(type_str)
|
|
59
|
+
if type_str != normalized_type_str:
|
|
60
|
+
type_str_repr = (
|
|
61
|
+
f"{type_str_repr} (normalized to {normalized_type_str!r})"
|
|
62
|
+
)
|
|
63
|
+
|
|
64
|
+
if expected_base is not None:
|
|
65
|
+
if not isinstance(abi_type, BasicType):
|
|
66
|
+
raise ValueError(
|
|
67
|
+
"Cannot create {} for non-basic type {}".format(
|
|
68
|
+
cls.__name__,
|
|
69
|
+
type_str_repr,
|
|
70
|
+
)
|
|
71
|
+
)
|
|
72
|
+
if abi_type.base != expected_base:
|
|
73
|
+
raise ValueError(
|
|
74
|
+
"Cannot create {} for type {}: expected type with "
|
|
75
|
+
"base '{}'".format(
|
|
76
|
+
cls.__name__,
|
|
77
|
+
type_str_repr,
|
|
78
|
+
expected_base,
|
|
79
|
+
)
|
|
80
|
+
)
|
|
81
|
+
|
|
82
|
+
if not with_arrlist and abi_type.arrlist is not None:
|
|
83
|
+
raise ValueError(
|
|
84
|
+
"Cannot create {} for type {}: expected type with "
|
|
85
|
+
"no array dimension list".format(
|
|
86
|
+
cls.__name__,
|
|
87
|
+
type_str_repr,
|
|
88
|
+
)
|
|
89
|
+
)
|
|
90
|
+
if with_arrlist and abi_type.arrlist is None:
|
|
91
|
+
raise ValueError(
|
|
92
|
+
"Cannot create {} for type {}: expected type with "
|
|
93
|
+
"array dimension list".format(
|
|
94
|
+
cls.__name__,
|
|
95
|
+
type_str_repr,
|
|
96
|
+
)
|
|
97
|
+
)
|
|
98
|
+
|
|
99
|
+
# Perform general validation of default solidity types
|
|
100
|
+
abi_type.validate()
|
|
101
|
+
|
|
102
|
+
return old_from_type_str(cls, abi_type, registry)
|
|
103
|
+
|
|
104
|
+
return classmethod(new_from_type_str)
|
|
105
|
+
|
|
106
|
+
return decorator
|
|
107
|
+
|
|
108
|
+
|
|
109
|
+
def parse_tuple_type_str(
|
|
110
|
+
old_from_type_str: OldFromTypeStr[TType],
|
|
111
|
+
) -> "NewFromTypeStr[TType]":
|
|
112
|
+
"""
|
|
113
|
+
Used by BaseCoder subclasses as a convenience for implementing the
|
|
114
|
+
``from_type_str`` method required by ``ABIRegistry``. Useful if normalizing
|
|
115
|
+
then parsing a tuple type string is required in that method.
|
|
116
|
+
"""
|
|
117
|
+
|
|
118
|
+
@functools.wraps(old_from_type_str)
|
|
119
|
+
def new_from_type_str(cls: TType, type_str: TypeStr, registry: Any) -> TType:
|
|
120
|
+
normalized_type_str = normalize(type_str)
|
|
121
|
+
abi_type = parse(normalized_type_str)
|
|
122
|
+
|
|
123
|
+
if not isinstance(abi_type, TupleType):
|
|
124
|
+
type_str_repr = repr(type_str)
|
|
125
|
+
if type_str != normalized_type_str:
|
|
126
|
+
type_str_repr = "{} (normalized to {})".format(
|
|
127
|
+
type_str_repr,
|
|
128
|
+
repr(normalized_type_str),
|
|
129
|
+
)
|
|
130
|
+
raise ValueError(
|
|
131
|
+
"Cannot create {} for non-tuple type {}".format(
|
|
132
|
+
cls.__name__,
|
|
133
|
+
type_str_repr,
|
|
134
|
+
)
|
|
135
|
+
)
|
|
136
|
+
|
|
137
|
+
abi_type.validate()
|
|
138
|
+
|
|
139
|
+
return old_from_type_str(cls, abi_type, registry)
|
|
140
|
+
|
|
141
|
+
return classmethod(new_from_type_str)
|
|
@@ -0,0 +1,179 @@
|
|
|
1
|
+
"""Parsing and normalization logic for ABI type strings.
|
|
2
|
+
|
|
3
|
+
Implements grammar, parsing, and type validation for ABI type strings.
|
|
4
|
+
"""
|
|
5
|
+
import functools
|
|
6
|
+
from typing import (
|
|
7
|
+
Any,
|
|
8
|
+
Final,
|
|
9
|
+
TYPE_CHECKING,
|
|
10
|
+
final,
|
|
11
|
+
)
|
|
12
|
+
|
|
13
|
+
import parsimonious
|
|
14
|
+
from eth_typing import (
|
|
15
|
+
TypeStr,
|
|
16
|
+
)
|
|
17
|
+
from parsimonious import (
|
|
18
|
+
expressions,
|
|
19
|
+
)
|
|
20
|
+
|
|
21
|
+
from faster_eth_abi._grammar import (
|
|
22
|
+
TYPE_ALIAS_RE,
|
|
23
|
+
TYPE_ALIASES,
|
|
24
|
+
ABIType,
|
|
25
|
+
BasicType,
|
|
26
|
+
TupleType,
|
|
27
|
+
normalize,
|
|
28
|
+
)
|
|
29
|
+
from faster_eth_abi.exceptions import (
|
|
30
|
+
ParseError,
|
|
31
|
+
)
|
|
32
|
+
|
|
33
|
+
grammar: Final = parsimonious.Grammar(
|
|
34
|
+
r"""
|
|
35
|
+
type = tuple_type / basic_type
|
|
36
|
+
|
|
37
|
+
tuple_type = components arrlist?
|
|
38
|
+
components = non_zero_tuple
|
|
39
|
+
|
|
40
|
+
non_zero_tuple = "(" type next_type* ")"
|
|
41
|
+
next_type = "," type
|
|
42
|
+
|
|
43
|
+
basic_type = base sub? arrlist?
|
|
44
|
+
|
|
45
|
+
base = alphas
|
|
46
|
+
|
|
47
|
+
sub = two_size / digits
|
|
48
|
+
two_size = (digits "x" digits)
|
|
49
|
+
|
|
50
|
+
arrlist = (const_arr / dynam_arr)+
|
|
51
|
+
const_arr = "[" digits "]"
|
|
52
|
+
dynam_arr = "[]"
|
|
53
|
+
|
|
54
|
+
alphas = ~"[A-Za-z]+"
|
|
55
|
+
digits = ~"[1-9][0-9]*"
|
|
56
|
+
"""
|
|
57
|
+
)
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
if TYPE_CHECKING:
|
|
61
|
+
_NodeVisitorBase = parsimonious.NodeVisitor[ABIType]
|
|
62
|
+
else:
|
|
63
|
+
_NodeVisitorBase = parsimonious.NodeVisitor
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
@final
|
|
67
|
+
class NodeVisitor(_NodeVisitorBase):
|
|
68
|
+
"""
|
|
69
|
+
Parsimonious node visitor which performs both parsing of type strings and
|
|
70
|
+
post-processing of parse trees. Parsing operations are cached.
|
|
71
|
+
"""
|
|
72
|
+
|
|
73
|
+
def __init__(self) -> None:
|
|
74
|
+
self.parse: Final = functools.lru_cache(maxsize=None)(self._parse_uncached)
|
|
75
|
+
|
|
76
|
+
grammar = grammar
|
|
77
|
+
|
|
78
|
+
def visit_non_zero_tuple(self, node, visited_children):
|
|
79
|
+
# Ignore left and right parens
|
|
80
|
+
_, first, rest, _ = visited_children
|
|
81
|
+
|
|
82
|
+
return (first,) + rest
|
|
83
|
+
|
|
84
|
+
def visit_tuple_type(self, node, visited_children):
|
|
85
|
+
components, arrlist = visited_children
|
|
86
|
+
|
|
87
|
+
return TupleType(components, arrlist, node=node)
|
|
88
|
+
|
|
89
|
+
def visit_next_type(self, node, visited_children):
|
|
90
|
+
# Ignore comma
|
|
91
|
+
_, abi_type = visited_children
|
|
92
|
+
|
|
93
|
+
return abi_type
|
|
94
|
+
|
|
95
|
+
def visit_basic_type(self, node, visited_children):
|
|
96
|
+
base, sub, arrlist = visited_children
|
|
97
|
+
|
|
98
|
+
return BasicType(base, sub, arrlist, node=node)
|
|
99
|
+
|
|
100
|
+
def visit_two_size(self, node, visited_children):
|
|
101
|
+
# Ignore "x"
|
|
102
|
+
first, _, second = visited_children
|
|
103
|
+
|
|
104
|
+
return first, second
|
|
105
|
+
|
|
106
|
+
def visit_const_arr(self, node, visited_children):
|
|
107
|
+
# Ignore left and right brackets
|
|
108
|
+
_, int_value, _ = visited_children
|
|
109
|
+
|
|
110
|
+
return (int_value,)
|
|
111
|
+
|
|
112
|
+
def visit_dynam_arr(self, node, visited_children):
|
|
113
|
+
return ()
|
|
114
|
+
|
|
115
|
+
def visit_alphas(self, node, visited_children):
|
|
116
|
+
return node.text
|
|
117
|
+
|
|
118
|
+
def visit_digits(self, node, visited_children):
|
|
119
|
+
return int(node.text)
|
|
120
|
+
|
|
121
|
+
def generic_visit(self, node, visited_children):
|
|
122
|
+
expr = node.expr
|
|
123
|
+
if isinstance(expr, expressions.OneOf):
|
|
124
|
+
# Unwrap value chosen from alternatives
|
|
125
|
+
return visited_children[0]
|
|
126
|
+
|
|
127
|
+
if isinstance(expr, expressions.Quantifier) and expr.min == 0 and expr.max == 1:
|
|
128
|
+
# Unwrap optional value or return `None`
|
|
129
|
+
if len(visited_children) != 0:
|
|
130
|
+
return visited_children[0]
|
|
131
|
+
|
|
132
|
+
return None
|
|
133
|
+
|
|
134
|
+
return tuple(visited_children)
|
|
135
|
+
|
|
136
|
+
def _parse_uncached(self, type_str: TypeStr, **kwargs: Any) -> ABIType:
|
|
137
|
+
"""
|
|
138
|
+
Parses a type string into an appropriate instance of
|
|
139
|
+
:class:`~faster_eth_abi.grammar.ABIType`. If a type string cannot be parsed,
|
|
140
|
+
throws :class:`~faster_eth_abi.exceptions.ParseError`.
|
|
141
|
+
|
|
142
|
+
:param type_str: The type string to be parsed.
|
|
143
|
+
:returns: An instance of :class:`~faster_eth_abi.grammar.ABIType` containing
|
|
144
|
+
information about the parsed type string.
|
|
145
|
+
"""
|
|
146
|
+
if not isinstance(type_str, str):
|
|
147
|
+
raise TypeError(f"Can only parse string values: got {type(type_str)}")
|
|
148
|
+
|
|
149
|
+
try:
|
|
150
|
+
return super().parse(type_str, **kwargs)
|
|
151
|
+
except parsimonious.ParseError as e:
|
|
152
|
+
# This is a good place to add some better messaging around the type string.
|
|
153
|
+
# If this logic grows any bigger, we should abstract it to its own function.
|
|
154
|
+
if "()" in type_str:
|
|
155
|
+
# validate against zero-sized tuple types
|
|
156
|
+
raise ValueError(
|
|
157
|
+
'Zero-sized tuple types "()" are not supported.'
|
|
158
|
+
) from None
|
|
159
|
+
|
|
160
|
+
raise ParseError(e.text, e.pos, e.expr) from e
|
|
161
|
+
|
|
162
|
+
|
|
163
|
+
visitor: Final = NodeVisitor()
|
|
164
|
+
|
|
165
|
+
parse: Final = visitor.parse
|
|
166
|
+
|
|
167
|
+
|
|
168
|
+
__all__ = [
|
|
169
|
+
"NodeVisitor",
|
|
170
|
+
"ABIType",
|
|
171
|
+
"TupleType",
|
|
172
|
+
"BasicType",
|
|
173
|
+
"grammar",
|
|
174
|
+
"parse",
|
|
175
|
+
"normalize",
|
|
176
|
+
"visitor",
|
|
177
|
+
"TYPE_ALIASES",
|
|
178
|
+
"TYPE_ALIAS_RE",
|
|
179
|
+
]
|
|
Binary file
|
faster_eth_abi/io.py
ADDED
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Context-aware byte stream for ABI decoding.
|
|
3
|
+
|
|
4
|
+
Implements a lightweight frame-aware reader that avoids BytesIO overhead in hot decoding paths.
|
|
5
|
+
"""
|
|
6
|
+
from typing import (
|
|
7
|
+
Any,
|
|
8
|
+
Final,
|
|
9
|
+
final,
|
|
10
|
+
)
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
@final
|
|
14
|
+
class ContextFramesBytesIO:
|
|
15
|
+
"""
|
|
16
|
+
A byte stream which can track a series of contextual frames in a stack. This
|
|
17
|
+
data structure is necessary to perform nested decodings using the
|
|
18
|
+
:py:class:``HeadTailDecoder`` since offsets present in head sections are
|
|
19
|
+
relative only to a particular encoded object. These offsets can only be
|
|
20
|
+
used to locate a position in a decoding stream if they are paired with a
|
|
21
|
+
contextual offset that establishes the position of the object in which they
|
|
22
|
+
are found.
|
|
23
|
+
|
|
24
|
+
For example, consider the encoding of a value for the following type::
|
|
25
|
+
|
|
26
|
+
type: (int,(int,int[]))
|
|
27
|
+
value: (1,(2,[3,3]))
|
|
28
|
+
|
|
29
|
+
There are two tuples in this type: one inner and one outer. The inner tuple
|
|
30
|
+
type contains a dynamic type ``int[]`` and, therefore, is itself dynamic.
|
|
31
|
+
This means that its value encoding will be placed in the tail section of the
|
|
32
|
+
outer tuple's encoding. Furthermore, the inner tuple's encoding will,
|
|
33
|
+
itself, contain a tail section with the encoding for ``[3,3]``. All
|
|
34
|
+
together, the encoded value of ``(1,(2,[3,3]))`` would look like this (the
|
|
35
|
+
data values are normally 32 bytes wide but have been truncated to remove the
|
|
36
|
+
redundant zeros at the beginnings of their encodings)::
|
|
37
|
+
|
|
38
|
+
offset data
|
|
39
|
+
--------------------------
|
|
40
|
+
^ 0 0x01
|
|
41
|
+
| 32 0x40 <-- Offset of object A in global frame (64)
|
|
42
|
+
-----|--------------------
|
|
43
|
+
Global frame ^ 64 0x02 <-- Beginning of object A (64 w/offset 0 = 64)
|
|
44
|
+
| | 96 0x40 <-- Offset of object B in frame of object A (64)
|
|
45
|
+
-----|-Object A's frame---
|
|
46
|
+
| | 128 0x02 <-- Beginning of object B (64 w/offset 64 = 128)
|
|
47
|
+
| | 160 0x03
|
|
48
|
+
v v 192 0x03
|
|
49
|
+
--------------------------
|
|
50
|
+
|
|
51
|
+
Note that the offset of object B is encoded as 64 which only specifies the
|
|
52
|
+
beginning of its encoded value relative to the beginning of object A's
|
|
53
|
+
encoding. Globally, object B is located at offset 128. In order to make
|
|
54
|
+
sense out of object B's offset, it needs to be positioned in the context of
|
|
55
|
+
its enclosing object's frame (object A).
|
|
56
|
+
"""
|
|
57
|
+
|
|
58
|
+
def __init__(self, initial_bytes: bytes | bytearray):
|
|
59
|
+
# NOTE: Non-contiguous buffers are intentionally unsupported (regression from
|
|
60
|
+
# BytesIO) because they are rare in our decode flow and we want to keep the
|
|
61
|
+
# compiled hot path fast. Callers with sliced memoryviews should wrap with
|
|
62
|
+
# bytes(...) if needed.
|
|
63
|
+
self._buffer = memoryview(initial_bytes)
|
|
64
|
+
self._position = 0
|
|
65
|
+
self._frames: Final[list[tuple[int, int]]] = []
|
|
66
|
+
self._total_offset = 0
|
|
67
|
+
|
|
68
|
+
def read(self, size: int = -1) -> bytes:
|
|
69
|
+
"""
|
|
70
|
+
Read up to ``size`` bytes from the stream. If ``size`` is negative,
|
|
71
|
+
read until EOF.
|
|
72
|
+
"""
|
|
73
|
+
remaining = len(self._buffer) - self._position
|
|
74
|
+
if size is None or size < 0:
|
|
75
|
+
size = remaining
|
|
76
|
+
elif size > remaining:
|
|
77
|
+
size = remaining
|
|
78
|
+
|
|
79
|
+
if size <= 0:
|
|
80
|
+
return b""
|
|
81
|
+
|
|
82
|
+
start = self._position
|
|
83
|
+
end = start + size
|
|
84
|
+
self._position = end
|
|
85
|
+
return self._buffer[start:end].tobytes()
|
|
86
|
+
|
|
87
|
+
def tell(self) -> int:
|
|
88
|
+
return self._position
|
|
89
|
+
|
|
90
|
+
def seek(self, pos: int, whence: int = 0) -> int:
|
|
91
|
+
if whence == 0:
|
|
92
|
+
new_pos = pos
|
|
93
|
+
elif whence == 1:
|
|
94
|
+
new_pos = self._position + pos
|
|
95
|
+
elif whence == 2:
|
|
96
|
+
new_pos = len(self._buffer) + pos
|
|
97
|
+
else:
|
|
98
|
+
raise ValueError(f"invalid whence ({whence}, should be 0, 1 or 2)")
|
|
99
|
+
|
|
100
|
+
if new_pos < 0:
|
|
101
|
+
raise ValueError("negative seek position")
|
|
102
|
+
|
|
103
|
+
self._position = new_pos
|
|
104
|
+
return new_pos
|
|
105
|
+
|
|
106
|
+
def getbuffer(self) -> memoryview:
|
|
107
|
+
return self._buffer
|
|
108
|
+
|
|
109
|
+
def seek_in_frame(self, pos: int, *args: Any, **kwargs: Any) -> None:
|
|
110
|
+
"""
|
|
111
|
+
Seeks relative to the total offset of the current contextual frames.
|
|
112
|
+
"""
|
|
113
|
+
self.seek(self._total_offset + pos, *args, **kwargs)
|
|
114
|
+
|
|
115
|
+
def push_frame(self, offset: int) -> None:
|
|
116
|
+
"""
|
|
117
|
+
Pushes a new contextual frame onto the stack with the given offset and a
|
|
118
|
+
return position at the current cursor position then seeks to the new
|
|
119
|
+
total offset.
|
|
120
|
+
"""
|
|
121
|
+
self._frames.append((offset, self.tell()))
|
|
122
|
+
self._total_offset += offset
|
|
123
|
+
|
|
124
|
+
self.seek_in_frame(0)
|
|
125
|
+
|
|
126
|
+
def pop_frame(self) -> None:
|
|
127
|
+
"""
|
|
128
|
+
Pops the current contextual frame off of the stack and returns the
|
|
129
|
+
cursor to the frame's return position.
|
|
130
|
+
"""
|
|
131
|
+
try:
|
|
132
|
+
offset, return_pos = self._frames.pop()
|
|
133
|
+
except IndexError:
|
|
134
|
+
raise IndexError("no frames to pop")
|
|
135
|
+
self._total_offset -= offset
|
|
136
|
+
|
|
137
|
+
self.seek(return_pos)
|
|
Binary file
|
faster_eth_abi/packed.py
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
"""Helpers for packed ABI encoding.
|
|
2
|
+
|
|
3
|
+
Defines functions and registry for packed encoding and encodability checks.
|
|
4
|
+
"""
|
|
5
|
+
from typing import (
|
|
6
|
+
Final,
|
|
7
|
+
)
|
|
8
|
+
|
|
9
|
+
from .codec import (
|
|
10
|
+
ABIEncoder,
|
|
11
|
+
)
|
|
12
|
+
from .registry import (
|
|
13
|
+
registry_packed,
|
|
14
|
+
)
|
|
15
|
+
|
|
16
|
+
default_encoder_packed: Final = ABIEncoder(registry_packed)
|
|
17
|
+
|
|
18
|
+
encode_packed: Final = default_encoder_packed.encode
|
|
19
|
+
is_encodable_packed: Final = default_encoder_packed.is_encodable
|
faster_eth_abi/py.typed
ADDED
|
File without changes
|