grammar.py 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427
  1. import functools
  2. import re
  3. import parsimonious
  4. from parsimonious import (
  5. expressions,
  6. )
  7. from eth_abi.exceptions import (
  8. ABITypeError,
  9. ParseError,
  10. )
  11. grammar = parsimonious.Grammar(
  12. r"""
  13. type = tuple_type / basic_type
  14. tuple_type = components arrlist?
  15. components = non_zero_tuple
  16. non_zero_tuple = "(" type next_type* ")"
  17. next_type = "," type
  18. basic_type = base sub? arrlist?
  19. base = alphas
  20. sub = two_size / digits
  21. two_size = (digits "x" digits)
  22. arrlist = (const_arr / dynam_arr)+
  23. const_arr = "[" digits "]"
  24. dynam_arr = "[]"
  25. alphas = ~"[A-Za-z]+"
  26. digits = ~"[1-9][0-9]*"
  27. """
  28. )
  29. class NodeVisitor(parsimonious.NodeVisitor): # type: ignore[misc] # subclasses Any
  30. """
  31. Parsimonious node visitor which performs both parsing of type strings and
  32. post-processing of parse trees. Parsing operations are cached.
  33. """
  34. def __init__(self):
  35. self.parse = functools.lru_cache(maxsize=None)(self._parse_uncached)
  36. grammar = grammar
  37. def visit_non_zero_tuple(self, node, visited_children):
  38. # Ignore left and right parens
  39. _, first, rest, _ = visited_children
  40. return (first,) + rest
  41. def visit_tuple_type(self, node, visited_children):
  42. components, arrlist = visited_children
  43. return TupleType(components, arrlist, node=node)
  44. def visit_next_type(self, node, visited_children):
  45. # Ignore comma
  46. _, abi_type = visited_children
  47. return abi_type
  48. def visit_basic_type(self, node, visited_children):
  49. base, sub, arrlist = visited_children
  50. return BasicType(base, sub, arrlist, node=node)
  51. def visit_two_size(self, node, visited_children):
  52. # Ignore "x"
  53. first, _, second = visited_children
  54. return first, second
  55. def visit_const_arr(self, node, visited_children):
  56. # Ignore left and right brackets
  57. _, int_value, _ = visited_children
  58. return (int_value,)
  59. def visit_dynam_arr(self, node, visited_children):
  60. return tuple()
  61. def visit_alphas(self, node, visited_children):
  62. return node.text
  63. def visit_digits(self, node, visited_children):
  64. return int(node.text)
  65. def generic_visit(self, node, visited_children):
  66. expr = node.expr
  67. if isinstance(expr, expressions.OneOf):
  68. # Unwrap value chosen from alternatives
  69. return visited_children[0]
  70. if isinstance(expr, expressions.Quantifier) and expr.min == 0 and expr.max == 1:
  71. # Unwrap optional value or return `None`
  72. if len(visited_children) != 0:
  73. return visited_children[0]
  74. return None
  75. return tuple(visited_children)
  76. def _parse_uncached(self, type_str, **kwargs):
  77. """
  78. Parses a type string into an appropriate instance of
  79. :class:`~eth_abi.grammar.ABIType`. If a type string cannot be parsed,
  80. throws :class:`~eth_abi.exceptions.ParseError`.
  81. :param type_str: The type string to be parsed.
  82. :returns: An instance of :class:`~eth_abi.grammar.ABIType` containing
  83. information about the parsed type string.
  84. """
  85. if not isinstance(type_str, str):
  86. raise TypeError(f"Can only parse string values: got {type(type_str)}")
  87. try:
  88. return super().parse(type_str, **kwargs)
  89. except parsimonious.ParseError as e:
  90. # This is a good place to add some better messaging around the type string.
  91. # If this logic grows any bigger, we should abstract it to its own function.
  92. if "()" in type_str:
  93. # validate against zero-sized tuple types
  94. raise ValueError('Zero-sized tuple types "()" are not supported.')
  95. raise ParseError(e.text, e.pos, e.expr)
  96. visitor = NodeVisitor()
  97. class ABIType:
  98. """
  99. Base class for results of type string parsing operations.
  100. """
  101. __slots__ = ("arrlist", "node")
  102. def __init__(self, arrlist=None, node=None):
  103. self.arrlist = arrlist
  104. """
  105. The list of array dimensions for a parsed type. Equal to ``None`` if
  106. type string has no array dimensions.
  107. """
  108. self.node = node
  109. """
  110. The parsimonious ``Node`` instance associated with this parsed type.
  111. Used to generate error messages for invalid types.
  112. """
  113. def __repr__(self): # pragma: no cover
  114. return f"<{type(self).__qualname__} {repr(self.to_type_str())}>"
  115. def __eq__(self, other):
  116. # Two ABI types are equal if their string representations are equal
  117. return type(self) is type(other) and self.to_type_str() == other.to_type_str()
  118. def to_type_str(self): # pragma: no cover
  119. """
  120. Returns the string representation of an ABI type. This will be equal to
  121. the type string from which it was created.
  122. """
  123. raise NotImplementedError("Must implement `to_type_str`")
  124. @property
  125. def item_type(self):
  126. """
  127. If this type is an array type, equal to an appropriate
  128. :class:`~eth_abi.grammar.ABIType` instance for the array's items.
  129. """
  130. raise NotImplementedError("Must implement `item_type`")
  131. def validate(self): # pragma: no cover
  132. """
  133. Validates the properties of an ABI type against the solidity ABI spec:
  134. https://solidity.readthedocs.io/en/develop/abi-spec.html
  135. Raises :class:`~eth_abi.exceptions.ABITypeError` if validation fails.
  136. """
  137. raise NotImplementedError("Must implement `validate`")
  138. def invalidate(self, error_msg):
  139. # Invalidates an ABI type with the given error message. Expects that a
  140. # parsimonious node was provided from the original parsing operation
  141. # that yielded this type.
  142. node = self.node
  143. raise ABITypeError(
  144. f"For '{node.text}' type at column {node.start + 1} "
  145. f"in '{node.full_text}': {error_msg}"
  146. )
  147. @property
  148. def is_array(self):
  149. """
  150. Equal to ``True`` if a type is an array type (i.e. if it has an array
  151. dimension list). Otherwise, equal to ``False``.
  152. """
  153. return self.arrlist is not None
  154. @property
  155. def is_dynamic(self):
  156. """
  157. Equal to ``True`` if a type has a dynamically sized encoding.
  158. Otherwise, equal to ``False``.
  159. """
  160. raise NotImplementedError("Must implement `is_dynamic`")
  161. @property
  162. def _has_dynamic_arrlist(self):
  163. return self.is_array and any(len(dim) == 0 for dim in self.arrlist)
  164. class TupleType(ABIType):
  165. """
  166. Represents the result of parsing a tuple type string e.g. "(int,bool)".
  167. """
  168. __slots__ = ("components",)
  169. def __init__(self, components, arrlist=None, *, node=None):
  170. super().__init__(arrlist, node)
  171. self.components = components
  172. """
  173. A tuple of :class:`~eth_abi.grammar.ABIType` instances for each of the
  174. tuple type's components.
  175. """
  176. def to_type_str(self):
  177. arrlist = self.arrlist
  178. if isinstance(arrlist, tuple):
  179. arrlist = "".join(repr(list(a)) for a in arrlist)
  180. else:
  181. arrlist = ""
  182. return f"({','.join(c.to_type_str() for c in self.components)}){arrlist}"
  183. @property
  184. def item_type(self):
  185. if not self.is_array:
  186. raise ValueError(
  187. f"Cannot determine item type for non-array type '{self.to_type_str()}'"
  188. )
  189. return type(self)(
  190. self.components,
  191. self.arrlist[:-1] or None,
  192. node=self.node,
  193. )
  194. def validate(self):
  195. for c in self.components:
  196. c.validate()
  197. @property
  198. def is_dynamic(self):
  199. if self._has_dynamic_arrlist:
  200. return True
  201. return any(c.is_dynamic for c in self.components)
  202. class BasicType(ABIType):
  203. """
  204. Represents the result of parsing a basic type string e.g. "uint", "address",
  205. "ufixed128x19[][2]".
  206. """
  207. __slots__ = ("base", "sub")
  208. def __init__(self, base, sub=None, arrlist=None, *, node=None):
  209. super().__init__(arrlist, node)
  210. self.base = base
  211. """The base of a basic type e.g. "uint" for "uint256" etc."""
  212. self.sub = sub
  213. """
  214. The sub type of a basic type e.g. ``256`` for "uint256" or ``(128, 18)``
  215. for "ufixed128x18" etc. Equal to ``None`` if type string has no sub
  216. type.
  217. """
  218. def to_type_str(self):
  219. sub, arrlist = self.sub, self.arrlist
  220. if isinstance(sub, int):
  221. sub = str(sub)
  222. elif isinstance(sub, tuple):
  223. sub = "x".join(str(s) for s in sub)
  224. else:
  225. sub = ""
  226. if isinstance(arrlist, tuple):
  227. arrlist = "".join(repr(list(a)) for a in arrlist)
  228. else:
  229. arrlist = ""
  230. return self.base + sub + arrlist
  231. @property
  232. def item_type(self):
  233. if not self.is_array:
  234. raise ValueError(
  235. f"Cannot determine item type for non-array type '{self.to_type_str()}'"
  236. )
  237. return type(self)(
  238. self.base,
  239. self.sub,
  240. self.arrlist[:-1] or None,
  241. node=self.node,
  242. )
  243. @property
  244. def is_dynamic(self):
  245. if self._has_dynamic_arrlist:
  246. return True
  247. if self.base == "string":
  248. return True
  249. if self.base == "bytes" and self.sub is None:
  250. return True
  251. return False
  252. def validate(self):
  253. base, sub = self.base, self.sub
  254. # Check validity of string type
  255. if base == "string":
  256. if sub is not None:
  257. self.invalidate("string type cannot have suffix")
  258. # Check validity of bytes type
  259. elif base == "bytes":
  260. if not (sub is None or isinstance(sub, int)):
  261. self.invalidate(
  262. "bytes type must have either no suffix or a numerical suffix"
  263. )
  264. if isinstance(sub, int) and sub > 32:
  265. self.invalidate("maximum 32 bytes for fixed-length bytes")
  266. # Check validity of integer type
  267. elif base in ("int", "uint"):
  268. if not isinstance(sub, int):
  269. self.invalidate("integer type must have numerical suffix")
  270. if sub < 8 or 256 < sub:
  271. self.invalidate("integer size out of bounds (max 256 bits)")
  272. if sub % 8 != 0:
  273. self.invalidate("integer size must be multiple of 8")
  274. # Check validity of fixed type
  275. elif base in ("fixed", "ufixed"):
  276. if not isinstance(sub, tuple):
  277. self.invalidate(
  278. "fixed type must have suffix of form <bits>x<exponent>, "
  279. "e.g. 128x19",
  280. )
  281. bits, minus_e = sub
  282. if bits < 8 or 256 < bits:
  283. self.invalidate("fixed size out of bounds (max 256 bits)")
  284. if bits % 8 != 0:
  285. self.invalidate("fixed size must be multiple of 8")
  286. if minus_e < 1 or 80 < minus_e:
  287. self.invalidate(
  288. f"fixed exponent size out of bounds, {minus_e} must be in 1-80"
  289. )
  290. # Check validity of hash type
  291. elif base == "hash":
  292. if not isinstance(sub, int):
  293. self.invalidate("hash type must have numerical suffix")
  294. # Check validity of address type
  295. elif base == "address":
  296. if sub is not None:
  297. self.invalidate("address cannot have suffix")
  298. TYPE_ALIASES = {
  299. "int": "int256",
  300. "uint": "uint256",
  301. "fixed": "fixed128x18",
  302. "ufixed": "ufixed128x18",
  303. "function": "bytes24",
  304. "byte": "bytes1",
  305. }
  306. TYPE_ALIAS_RE = re.compile(
  307. rf"\b({'|'.join(re.escape(a) for a in TYPE_ALIASES.keys())})\b"
  308. )
  309. def normalize(type_str):
  310. """
  311. Normalizes a type string into its canonical version e.g. the type string
  312. 'int' becomes 'int256', etc.
  313. :param type_str: The type string to be normalized.
  314. :returns: The canonical version of the input type string.
  315. """
  316. return TYPE_ALIAS_RE.sub(
  317. lambda match: TYPE_ALIASES[match.group(0)],
  318. type_str,
  319. )
  320. parse = visitor.parse