main.py 3.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128
  1. import sys
  2. from typing import (
  3. Any,
  4. Dict,
  5. Union,
  6. cast,
  7. )
  8. from eth_utils import (
  9. keccak,
  10. )
  11. from eth_utils.toolz import (
  12. pipe,
  13. )
  14. from hexbytes import (
  15. HexBytes,
  16. )
  17. import rlp
  18. if sys.version_info >= (3, 11):
  19. from typing import (
  20. Self,
  21. )
  22. else:
  23. from typing_extensions import (
  24. Self,
  25. )
  26. class HashableRLP(rlp.Serializable): # type: ignore
  27. r"""
  28. An extension of :class:`rlp.Serializable`. In addition to the below
  29. functions, the class is iterable.
  30. Use like:
  31. ::
  32. class MyRLP(HashableRLP):
  33. fields = (
  34. ('name1', rlp.sedes.big_endian_int),
  35. ('name2', rlp.sedes.binary),
  36. etc...
  37. )
  38. my_obj = MyRLP(name2=b'\xff', name1=1)
  39. list(my_obj) == [1, b'\xff']
  40. # note that the iteration order is always in RLP-defined order
  41. """
  42. @classmethod
  43. def from_dict(cls, field_dict: Dict[str, Any]) -> Self:
  44. r"""
  45. In addition to the standard initialization of.
  46. ::
  47. my_obj = MyRLP(name1=1, name2=b'\xff')
  48. This method enables initialization with.
  49. ::
  50. my_obj = MyRLP.from_dict({'name1': 1, 'name2': b'\xff'})
  51. In general, the standard initialization is preferred, but
  52. some approaches might favor this API, like when using
  53. :meth:`toolz.functoolz.pipe`.
  54. ::
  55. return eth_utils.toolz.pipe(
  56. my_dict,
  57. normalize,
  58. validate,
  59. MyRLP.from_dict,
  60. )
  61. :param dict field_dict: the dictionary of values to initialize with
  62. :returns: the new rlp object
  63. :rtype: HashableRLP
  64. """
  65. return cls(**field_dict)
  66. @classmethod
  67. def from_bytes(cls, serialized_bytes: Union[bytes, bytearray]) -> Self:
  68. """
  69. Shorthand invocation for :meth:`rlp.decode` using this class.
  70. :param bytes serialized_bytes: the byte string to decode
  71. :return: the decoded object
  72. :rtype: HashableRLP
  73. """
  74. decoded = rlp.decode(serialized_bytes, cls)
  75. return cast(Self, decoded)
  76. def hash(self) -> HexBytes:
  77. """
  78. :returns: the hash of the encoded bytestring
  79. :rtype: ~hexbytes.main.HexBytes
  80. """
  81. return HexBytes(
  82. pipe(
  83. self,
  84. rlp.encode,
  85. keccak,
  86. )
  87. )
  88. def __iter__(self) -> Any:
  89. if hasattr(self, "fields"):
  90. return iter(getattr(self, field) for field, _ in self.fields)
  91. else:
  92. return super().__iter__()
  93. def as_dict(self) -> Dict[str, Any]:
  94. """
  95. Convert rlp object to a dict
  96. :returns: mapping of RLP field names to field values
  97. :rtype: dict
  98. """
  99. try:
  100. _as_dict = super().as_dict()
  101. return cast(Dict[str, Any], _as_dict)
  102. except AttributeError:
  103. _as_dict = vars(self)
  104. return cast(Dict[str, Any], _as_dict)