validation.py 6.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239
  1. import os
  2. from typing import (
  3. Any,
  4. Optional,
  5. cast,
  6. )
  7. from eth_keyfile.keyfile import (
  8. KDFType,
  9. )
  10. from eth_utils import (
  11. is_binary_address,
  12. is_checksum_address,
  13. is_dict,
  14. is_hexstr,
  15. )
  16. from eth_utils.curried import (
  17. apply_one_of_formatters,
  18. hexstr_if_str,
  19. is_0x_prefixed,
  20. is_address,
  21. is_bytes,
  22. is_integer,
  23. is_list_like,
  24. is_string,
  25. to_bytes,
  26. to_int,
  27. )
  28. from eth_utils.toolz import (
  29. curry,
  30. identity,
  31. )
  32. from hexbytes import (
  33. HexBytes,
  34. )
  35. VALID_EMPTY_ADDRESSES = {None, b"", ""}
  36. def is_none(val: Any) -> bool:
  37. return val is None
  38. def is_valid_address(value: Any) -> bool:
  39. return is_binary_address(value) or is_checksum_address(value)
  40. def is_int_or_prefixed_hexstr(val: Any) -> bool:
  41. if is_integer(val):
  42. return True
  43. elif isinstance(val, str) and is_0x_prefixed(val):
  44. return True
  45. else:
  46. return False
  47. def is_empty_or_checksum_address(val: Any) -> bool:
  48. if val in VALID_EMPTY_ADDRESSES:
  49. return True
  50. else:
  51. return is_valid_address(val)
  52. def is_rpc_structured_access_list(val: Any) -> bool:
  53. """Returns true if 'val' is a valid JSON-RPC structured access list."""
  54. if not is_list_like(val):
  55. return False
  56. for d in val:
  57. if not is_dict(d):
  58. return False
  59. if len(d) != 2:
  60. return False
  61. address = d.get("address")
  62. storage_keys = d.get("storageKeys")
  63. if any(_ is None for _ in (address, storage_keys)):
  64. return False
  65. if not is_address(address):
  66. return False
  67. if not is_list_like(storage_keys):
  68. return False
  69. for storage_key in storage_keys:
  70. if not is_int_or_prefixed_hexstr(storage_key):
  71. return False
  72. return True
  73. def is_rlp_structured_access_list(val: Any) -> bool:
  74. """Returns true if 'val' is a valid rlp-structured access list."""
  75. if not is_list_like(val):
  76. return False
  77. for item in val:
  78. if not is_list_like(item):
  79. return False
  80. if len(item) != 2:
  81. return False
  82. address, storage_keys = item
  83. if not is_address(address):
  84. return False
  85. for storage_key in storage_keys:
  86. if not is_int_or_prefixed_hexstr(storage_key):
  87. return False
  88. return True
  89. def is_rpc_structured_authorization_list(val: Any) -> bool:
  90. """Returns true if 'val' is a valid JSON-RPC structured access list."""
  91. if not is_list_like(val):
  92. return False
  93. if len(val) == 0:
  94. return False
  95. for d in val:
  96. if not is_dict(d):
  97. return False
  98. if len(d) != 6:
  99. return False
  100. chain_id = d.get("chainId")
  101. address = d.get("address")
  102. nonce = d.get("nonce")
  103. y_parity = d.get("yParity")
  104. signer_r = d.get("r")
  105. signer_s = d.get("s")
  106. if chain_id is None:
  107. return False
  108. if not is_int_or_prefixed_hexstr(chain_id):
  109. return False
  110. if nonce is None:
  111. return False
  112. if not is_int_or_prefixed_hexstr(nonce):
  113. return False
  114. if not is_address(address):
  115. return False
  116. if y_parity is None:
  117. return False
  118. if y_parity not in (0, 1, "0x0", "0x1"):
  119. return False
  120. if signer_r is None:
  121. return False
  122. if not is_int_or_prefixed_hexstr(signer_r):
  123. return False
  124. if signer_s is None:
  125. return False
  126. if not is_int_or_prefixed_hexstr(signer_s):
  127. return False
  128. return True
  129. def is_rlp_structured_authorization_list(val: Any) -> bool:
  130. """Returns true if 'val' is a valid rlp-structured access list."""
  131. if not is_list_like(val):
  132. return False
  133. for item in val:
  134. if not is_list_like(item):
  135. return False
  136. if len(item) != 6:
  137. return False
  138. chain_id, address, nonce, y_parity, signer_r, signer_s = item
  139. if chain_id is None:
  140. return False
  141. if not is_int_or_prefixed_hexstr(chain_id):
  142. return False
  143. if nonce is None:
  144. return False
  145. if not is_int_or_prefixed_hexstr(nonce):
  146. return False
  147. if not is_address(address):
  148. return False
  149. if y_parity is None:
  150. return False
  151. if y_parity not in ("0x0", "0x1", 0, 1):
  152. return False
  153. if signer_r is None:
  154. return False
  155. if not is_int_or_prefixed_hexstr(signer_r):
  156. return False
  157. if signer_s is None:
  158. return False
  159. if not is_int_or_prefixed_hexstr(signer_s):
  160. return False
  161. return True
  162. # type ignored because curry doesn't preserve typing
  163. @curry # type: ignore[misc]
  164. def is_sequence_of_bytes_or_hexstr(
  165. value: Any, item_bytes_size: Optional[int] = None, can_be_empty: bool = False
  166. ) -> bool:
  167. if not is_list_like(value):
  168. return False
  169. if not can_be_empty and len(value) == 0:
  170. return False
  171. if not all(is_bytes(item) or is_hexstr(item) for item in value):
  172. return False
  173. if item_bytes_size is not None and not all(
  174. len(HexBytes(item)) == item_bytes_size for item in value
  175. ):
  176. return False
  177. return True
  178. LEGACY_TRANSACTION_FORMATTERS = {
  179. "nonce": hexstr_if_str(to_int),
  180. "gasPrice": hexstr_if_str(to_int),
  181. "gas": hexstr_if_str(to_int),
  182. "to": apply_one_of_formatters(
  183. (
  184. (is_string, hexstr_if_str(to_bytes)),
  185. (is_bytes, identity),
  186. (is_none, lambda val: b""),
  187. )
  188. ),
  189. "value": hexstr_if_str(to_int),
  190. "data": hexstr_if_str(to_bytes),
  191. "v": hexstr_if_str(to_int),
  192. "r": hexstr_if_str(to_int),
  193. "s": hexstr_if_str(to_int),
  194. }
  195. LEGACY_TRANSACTION_VALID_VALUES = {
  196. "nonce": is_int_or_prefixed_hexstr,
  197. "gasPrice": is_int_or_prefixed_hexstr,
  198. "gas": is_int_or_prefixed_hexstr,
  199. "to": is_empty_or_checksum_address,
  200. "value": is_int_or_prefixed_hexstr,
  201. "data": lambda val: isinstance(val, (int, str, bytes, bytearray)),
  202. "chainId": lambda val: val is None or is_int_or_prefixed_hexstr(val),
  203. }
  204. def validate_and_set_default_kdf() -> KDFType:
  205. os_kdf = os.getenv("ETH_ACCOUNT_KDF", "scrypt")
  206. if os_kdf not in ("pbkdf2", "scrypt"):
  207. raise ValueError(
  208. f"Invalid KDF type: {os_kdf}. Must be one of 'pbkdf2' or 'scrypt'"
  209. )
  210. return cast(KDFType, os_kdf)