encoding.py 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696
  1. import abc
  2. import codecs
  3. import decimal
  4. from itertools import (
  5. accumulate,
  6. )
  7. from typing import (
  8. Any,
  9. Optional,
  10. Type,
  11. )
  12. from eth_utils import (
  13. int_to_big_endian,
  14. is_address,
  15. is_boolean,
  16. is_bytes,
  17. is_integer,
  18. is_list_like,
  19. is_number,
  20. is_text,
  21. to_canonical_address,
  22. )
  23. from eth_abi.base import (
  24. BaseCoder,
  25. parse_tuple_type_str,
  26. parse_type_str,
  27. )
  28. from eth_abi.exceptions import (
  29. EncodingTypeError,
  30. IllegalValue,
  31. ValueOutOfBounds,
  32. )
  33. from eth_abi.utils.numeric import (
  34. TEN,
  35. abi_decimal_context,
  36. ceil32,
  37. compute_signed_fixed_bounds,
  38. compute_signed_integer_bounds,
  39. compute_unsigned_fixed_bounds,
  40. compute_unsigned_integer_bounds,
  41. )
  42. from eth_abi.utils.padding import (
  43. fpad,
  44. zpad,
  45. zpad_right,
  46. )
  47. from eth_abi.utils.string import (
  48. abbr,
  49. )
  50. class BaseEncoder(BaseCoder, metaclass=abc.ABCMeta):
  51. """
  52. Base class for all encoder classes. Subclass this if you want to define a
  53. custom encoder class. Subclasses must also implement
  54. :any:`BaseCoder.from_type_str`.
  55. """
  56. @abc.abstractmethod
  57. def encode(self, value: Any) -> bytes: # pragma: no cover
  58. """
  59. Encodes the given value as a sequence of bytes. Should raise
  60. :any:`exceptions.EncodingError` if ``value`` cannot be encoded.
  61. """
  62. @abc.abstractmethod
  63. def validate_value(self, value: Any) -> None: # pragma: no cover
  64. """
  65. Checks whether or not the given value can be encoded by this encoder.
  66. If the given value cannot be encoded, must raise
  67. :any:`exceptions.EncodingError`.
  68. """
  69. @classmethod
  70. def invalidate_value(
  71. cls,
  72. value: Any,
  73. exc: Type[Exception] = EncodingTypeError,
  74. msg: Optional[str] = None,
  75. ) -> None:
  76. """
  77. Throws a standard exception for when a value is not encodable by an
  78. encoder.
  79. """
  80. raise exc(
  81. f"Value `{abbr(value)}` of type {type(value)} cannot be encoded by "
  82. f"{cls.__name__}{'' if msg is None else (': ' + msg)}"
  83. )
  84. def __call__(self, value: Any) -> bytes:
  85. return self.encode(value)
  86. class TupleEncoder(BaseEncoder):
  87. encoders = None
  88. def __init__(self, **kwargs):
  89. super().__init__(**kwargs)
  90. self.is_dynamic = any(getattr(e, "is_dynamic", False) for e in self.encoders)
  91. def validate(self):
  92. super().validate()
  93. if self.encoders is None:
  94. raise ValueError("`encoders` may not be none")
  95. def validate_value(self, value):
  96. if not is_list_like(value):
  97. self.invalidate_value(
  98. value,
  99. msg="must be list-like object such as array or tuple",
  100. )
  101. if len(value) != len(self.encoders):
  102. self.invalidate_value(
  103. value,
  104. exc=ValueOutOfBounds,
  105. msg=f"value has {len(value)} items when {len(self.encoders)} "
  106. "were expected",
  107. )
  108. for item, encoder in zip(value, self.encoders):
  109. try:
  110. encoder.validate_value(item)
  111. except AttributeError:
  112. encoder(item)
  113. def encode(self, values):
  114. self.validate_value(values)
  115. raw_head_chunks = []
  116. tail_chunks = []
  117. for value, encoder in zip(values, self.encoders):
  118. if getattr(encoder, "is_dynamic", False):
  119. raw_head_chunks.append(None)
  120. tail_chunks.append(encoder(value))
  121. else:
  122. raw_head_chunks.append(encoder(value))
  123. tail_chunks.append(b"")
  124. head_length = sum(32 if item is None else len(item) for item in raw_head_chunks)
  125. tail_offsets = (0,) + tuple(accumulate(map(len, tail_chunks[:-1])))
  126. head_chunks = tuple(
  127. encode_uint_256(head_length + offset) if chunk is None else chunk
  128. for chunk, offset in zip(raw_head_chunks, tail_offsets)
  129. )
  130. encoded_value = b"".join(head_chunks + tuple(tail_chunks))
  131. return encoded_value
  132. @parse_tuple_type_str
  133. def from_type_str(cls, abi_type, registry):
  134. encoders = tuple(
  135. registry.get_encoder(c.to_type_str()) for c in abi_type.components
  136. )
  137. return cls(encoders=encoders)
  138. class FixedSizeEncoder(BaseEncoder):
  139. value_bit_size = None
  140. data_byte_size = None
  141. encode_fn = None
  142. type_check_fn = None
  143. is_big_endian = None
  144. def validate(self):
  145. super().validate()
  146. if self.value_bit_size is None:
  147. raise ValueError("`value_bit_size` may not be none")
  148. if self.data_byte_size is None:
  149. raise ValueError("`data_byte_size` may not be none")
  150. if self.encode_fn is None:
  151. raise ValueError("`encode_fn` may not be none")
  152. if self.is_big_endian is None:
  153. raise ValueError("`is_big_endian` may not be none")
  154. if self.value_bit_size % 8 != 0:
  155. raise ValueError(
  156. f"Invalid value bit size: {self.value_bit_size}. "
  157. "Must be a multiple of 8"
  158. )
  159. if self.value_bit_size > self.data_byte_size * 8:
  160. raise ValueError("Value byte size exceeds data size")
  161. def validate_value(self, value):
  162. raise NotImplementedError("Must be implemented by subclasses")
  163. def encode(self, value):
  164. self.validate_value(value)
  165. if self.encode_fn is None:
  166. raise AssertionError("`encode_fn` is None")
  167. base_encoded_value = self.encode_fn(value)
  168. if self.is_big_endian:
  169. padded_encoded_value = zpad(base_encoded_value, self.data_byte_size)
  170. else:
  171. padded_encoded_value = zpad_right(base_encoded_value, self.data_byte_size)
  172. return padded_encoded_value
  173. class Fixed32ByteSizeEncoder(FixedSizeEncoder):
  174. data_byte_size = 32
  175. class BooleanEncoder(Fixed32ByteSizeEncoder):
  176. value_bit_size = 8
  177. is_big_endian = True
  178. @classmethod
  179. def validate_value(cls, value):
  180. if not is_boolean(value):
  181. cls.invalidate_value(value)
  182. @classmethod
  183. def encode_fn(cls, value):
  184. if value is True:
  185. return b"\x01"
  186. elif value is False:
  187. return b"\x00"
  188. else:
  189. raise ValueError("Invariant")
  190. @parse_type_str("bool")
  191. def from_type_str(cls, abi_type, registry):
  192. return cls()
  193. class PackedBooleanEncoder(BooleanEncoder):
  194. data_byte_size = 1
  195. class NumberEncoder(Fixed32ByteSizeEncoder):
  196. is_big_endian = True
  197. bounds_fn = None
  198. illegal_value_fn = None
  199. type_check_fn = None
  200. def validate(self):
  201. super().validate()
  202. if self.bounds_fn is None:
  203. raise ValueError("`bounds_fn` cannot be null")
  204. if self.type_check_fn is None:
  205. raise ValueError("`type_check_fn` cannot be null")
  206. def validate_value(self, value):
  207. if self.type_check_fn is None:
  208. raise AssertionError("`type_check_fn` is None")
  209. if not self.type_check_fn(value):
  210. self.invalidate_value(value)
  211. illegal_value = self.illegal_value_fn is not None and self.illegal_value_fn(
  212. value
  213. )
  214. if illegal_value:
  215. self.invalidate_value(value, exc=IllegalValue)
  216. lower_bound, upper_bound = self.bounds_fn(self.value_bit_size)
  217. if value < lower_bound or value > upper_bound:
  218. self.invalidate_value(
  219. value,
  220. exc=ValueOutOfBounds,
  221. msg=f"Cannot be encoded in {self.value_bit_size} bits. Must be bounded "
  222. f"between [{lower_bound}, {upper_bound}].",
  223. )
  224. class UnsignedIntegerEncoder(NumberEncoder):
  225. encode_fn = staticmethod(int_to_big_endian)
  226. bounds_fn = staticmethod(compute_unsigned_integer_bounds)
  227. type_check_fn = staticmethod(is_integer)
  228. @parse_type_str("uint")
  229. def from_type_str(cls, abi_type, registry):
  230. return cls(value_bit_size=abi_type.sub)
  231. encode_uint_256 = UnsignedIntegerEncoder(value_bit_size=256, data_byte_size=32)
  232. class PackedUnsignedIntegerEncoder(UnsignedIntegerEncoder):
  233. @parse_type_str("uint")
  234. def from_type_str(cls, abi_type, registry):
  235. return cls(
  236. value_bit_size=abi_type.sub,
  237. data_byte_size=abi_type.sub // 8,
  238. )
  239. class SignedIntegerEncoder(NumberEncoder):
  240. bounds_fn = staticmethod(compute_signed_integer_bounds)
  241. type_check_fn = staticmethod(is_integer)
  242. def encode_fn(self, value):
  243. return int_to_big_endian(value % (2**self.value_bit_size))
  244. def encode(self, value):
  245. self.validate_value(value)
  246. base_encoded_value = self.encode_fn(value)
  247. if value >= 0:
  248. padded_encoded_value = zpad(base_encoded_value, self.data_byte_size)
  249. else:
  250. padded_encoded_value = fpad(base_encoded_value, self.data_byte_size)
  251. return padded_encoded_value
  252. @parse_type_str("int")
  253. def from_type_str(cls, abi_type, registry):
  254. return cls(value_bit_size=abi_type.sub)
  255. class PackedSignedIntegerEncoder(SignedIntegerEncoder):
  256. @parse_type_str("int")
  257. def from_type_str(cls, abi_type, registry):
  258. return cls(
  259. value_bit_size=abi_type.sub,
  260. data_byte_size=abi_type.sub // 8,
  261. )
  262. class BaseFixedEncoder(NumberEncoder):
  263. frac_places = None
  264. @staticmethod
  265. def type_check_fn(value):
  266. return is_number(value) and not isinstance(value, float)
  267. @staticmethod
  268. def illegal_value_fn(value):
  269. if isinstance(value, decimal.Decimal):
  270. return value.is_nan() or value.is_infinite()
  271. return False
  272. def validate_value(self, value):
  273. super().validate_value(value)
  274. with decimal.localcontext(abi_decimal_context):
  275. residue = value % (TEN**-self.frac_places)
  276. if residue > 0:
  277. self.invalidate_value(
  278. value,
  279. exc=IllegalValue,
  280. msg=f"residue {repr(residue)} outside allowed fractional precision of "
  281. f"{self.frac_places}",
  282. )
  283. def validate(self):
  284. super().validate()
  285. if self.frac_places is None:
  286. raise ValueError("must specify `frac_places`")
  287. if self.frac_places <= 0 or self.frac_places > 80:
  288. raise ValueError("`frac_places` must be in range (0, 80]")
  289. class UnsignedFixedEncoder(BaseFixedEncoder):
  290. def bounds_fn(self, value_bit_size):
  291. return compute_unsigned_fixed_bounds(self.value_bit_size, self.frac_places)
  292. def encode_fn(self, value):
  293. with decimal.localcontext(abi_decimal_context):
  294. scaled_value = value * TEN**self.frac_places
  295. integer_value = int(scaled_value)
  296. return int_to_big_endian(integer_value)
  297. @parse_type_str("ufixed")
  298. def from_type_str(cls, abi_type, registry):
  299. value_bit_size, frac_places = abi_type.sub
  300. return cls(
  301. value_bit_size=value_bit_size,
  302. frac_places=frac_places,
  303. )
  304. class PackedUnsignedFixedEncoder(UnsignedFixedEncoder):
  305. @parse_type_str("ufixed")
  306. def from_type_str(cls, abi_type, registry):
  307. value_bit_size, frac_places = abi_type.sub
  308. return cls(
  309. value_bit_size=value_bit_size,
  310. data_byte_size=value_bit_size // 8,
  311. frac_places=frac_places,
  312. )
  313. class SignedFixedEncoder(BaseFixedEncoder):
  314. def bounds_fn(self, value_bit_size):
  315. return compute_signed_fixed_bounds(self.value_bit_size, self.frac_places)
  316. def encode_fn(self, value):
  317. with decimal.localcontext(abi_decimal_context):
  318. scaled_value = value * TEN**self.frac_places
  319. integer_value = int(scaled_value)
  320. unsigned_integer_value = integer_value % (2**self.value_bit_size)
  321. return int_to_big_endian(unsigned_integer_value)
  322. def encode(self, value):
  323. self.validate_value(value)
  324. base_encoded_value = self.encode_fn(value)
  325. if value >= 0:
  326. padded_encoded_value = zpad(base_encoded_value, self.data_byte_size)
  327. else:
  328. padded_encoded_value = fpad(base_encoded_value, self.data_byte_size)
  329. return padded_encoded_value
  330. @parse_type_str("fixed")
  331. def from_type_str(cls, abi_type, registry):
  332. value_bit_size, frac_places = abi_type.sub
  333. return cls(
  334. value_bit_size=value_bit_size,
  335. frac_places=frac_places,
  336. )
  337. class PackedSignedFixedEncoder(SignedFixedEncoder):
  338. @parse_type_str("fixed")
  339. def from_type_str(cls, abi_type, registry):
  340. value_bit_size, frac_places = abi_type.sub
  341. return cls(
  342. value_bit_size=value_bit_size,
  343. data_byte_size=value_bit_size // 8,
  344. frac_places=frac_places,
  345. )
  346. class AddressEncoder(Fixed32ByteSizeEncoder):
  347. value_bit_size = 20 * 8
  348. encode_fn = staticmethod(to_canonical_address)
  349. is_big_endian = True
  350. @classmethod
  351. def validate_value(cls, value):
  352. if not is_address(value):
  353. cls.invalidate_value(value)
  354. def validate(self):
  355. super().validate()
  356. if self.value_bit_size != 20 * 8:
  357. raise ValueError("Addresses must be 160 bits in length")
  358. @parse_type_str("address")
  359. def from_type_str(cls, abi_type, registry):
  360. return cls()
  361. class PackedAddressEncoder(AddressEncoder):
  362. data_byte_size = 20
  363. class BytesEncoder(Fixed32ByteSizeEncoder):
  364. is_big_endian = False
  365. def validate_value(self, value):
  366. if not is_bytes(value):
  367. self.invalidate_value(value)
  368. byte_size = self.value_bit_size // 8
  369. if len(value) > byte_size:
  370. self.invalidate_value(
  371. value,
  372. exc=ValueOutOfBounds,
  373. msg=f"exceeds total byte size for bytes{byte_size} encoding",
  374. )
  375. @staticmethod
  376. def encode_fn(value):
  377. return value
  378. @parse_type_str("bytes")
  379. def from_type_str(cls, abi_type, registry):
  380. return cls(value_bit_size=abi_type.sub * 8)
  381. class PackedBytesEncoder(BytesEncoder):
  382. @parse_type_str("bytes")
  383. def from_type_str(cls, abi_type, registry):
  384. return cls(
  385. value_bit_size=abi_type.sub * 8,
  386. data_byte_size=abi_type.sub,
  387. )
  388. class ByteStringEncoder(BaseEncoder):
  389. is_dynamic = True
  390. @classmethod
  391. def validate_value(cls, value):
  392. if not is_bytes(value):
  393. cls.invalidate_value(value)
  394. @classmethod
  395. def encode(cls, value):
  396. cls.validate_value(value)
  397. value_length = len(value)
  398. encoded_size = encode_uint_256(value_length)
  399. padded_value = zpad_right(value, ceil32(value_length))
  400. return encoded_size + padded_value
  401. @parse_type_str("bytes")
  402. def from_type_str(cls, abi_type, registry):
  403. return cls()
  404. class PackedByteStringEncoder(ByteStringEncoder):
  405. is_dynamic = False
  406. @classmethod
  407. def encode(cls, value):
  408. cls.validate_value(value)
  409. return value
  410. class TextStringEncoder(BaseEncoder):
  411. is_dynamic = True
  412. @classmethod
  413. def validate_value(cls, value):
  414. if not is_text(value):
  415. cls.invalidate_value(value)
  416. @classmethod
  417. def encode(cls, value):
  418. cls.validate_value(value)
  419. value_as_bytes = codecs.encode(value, "utf8")
  420. value_length = len(value_as_bytes)
  421. encoded_size = encode_uint_256(value_length)
  422. padded_value = zpad_right(value_as_bytes, ceil32(value_length))
  423. return encoded_size + padded_value
  424. @parse_type_str("string")
  425. def from_type_str(cls, abi_type, registry):
  426. return cls()
  427. class PackedTextStringEncoder(TextStringEncoder):
  428. is_dynamic = False
  429. @classmethod
  430. def encode(cls, value):
  431. cls.validate_value(value)
  432. return codecs.encode(value, "utf8")
  433. class BaseArrayEncoder(BaseEncoder):
  434. item_encoder = None
  435. def validate(self):
  436. super().validate()
  437. if self.item_encoder is None:
  438. raise ValueError("`item_encoder` may not be none")
  439. def validate_value(self, value):
  440. if not is_list_like(value):
  441. self.invalidate_value(
  442. value,
  443. msg="must be list-like such as array or tuple",
  444. )
  445. for item in value:
  446. self.item_encoder.validate_value(item)
  447. def encode_elements(self, value):
  448. self.validate_value(value)
  449. item_encoder = self.item_encoder
  450. if item_encoder is None:
  451. raise AssertionError("`item_encoder` is None")
  452. tail_chunks = tuple(item_encoder(i) for i in value)
  453. items_are_dynamic = getattr(item_encoder, "is_dynamic", False)
  454. if not items_are_dynamic or len(value) == 0:
  455. return b"".join(tail_chunks)
  456. head_length = 32 * len(value)
  457. tail_offsets = (0,) + tuple(accumulate(map(len, tail_chunks[:-1])))
  458. head_chunks = tuple(
  459. encode_uint_256(head_length + offset) for offset in tail_offsets
  460. )
  461. return b"".join(head_chunks + tail_chunks)
  462. @parse_type_str(with_arrlist=True)
  463. def from_type_str(cls, abi_type, registry):
  464. item_encoder = registry.get_encoder(abi_type.item_type.to_type_str())
  465. array_spec = abi_type.arrlist[-1]
  466. if len(array_spec) == 1:
  467. # If array dimension is fixed
  468. return SizedArrayEncoder(
  469. array_size=array_spec[0],
  470. item_encoder=item_encoder,
  471. )
  472. else:
  473. # If array dimension is dynamic
  474. return DynamicArrayEncoder(item_encoder=item_encoder)
  475. class PackedArrayEncoder(BaseArrayEncoder):
  476. array_size = None
  477. def validate_value(self, value):
  478. super().validate_value(value)
  479. if self.array_size is not None and len(value) != self.array_size:
  480. self.invalidate_value(
  481. value,
  482. exc=ValueOutOfBounds,
  483. msg=f"value has {len(value)} items when {self.array_size} were "
  484. "expected",
  485. )
  486. def encode(self, value):
  487. encoded_elements = self.encode_elements(value)
  488. return encoded_elements
  489. @parse_type_str(with_arrlist=True)
  490. def from_type_str(cls, abi_type, registry):
  491. item_encoder = registry.get_encoder(abi_type.item_type.to_type_str())
  492. array_spec = abi_type.arrlist[-1]
  493. if len(array_spec) == 1:
  494. return cls(
  495. array_size=array_spec[0],
  496. item_encoder=item_encoder,
  497. )
  498. else:
  499. return cls(item_encoder=item_encoder)
  500. class SizedArrayEncoder(BaseArrayEncoder):
  501. array_size = None
  502. def __init__(self, **kwargs):
  503. super().__init__(**kwargs)
  504. self.is_dynamic = self.item_encoder.is_dynamic
  505. def validate(self):
  506. super().validate()
  507. if self.array_size is None:
  508. raise ValueError("`array_size` may not be none")
  509. def validate_value(self, value):
  510. super().validate_value(value)
  511. if len(value) != self.array_size:
  512. self.invalidate_value(
  513. value,
  514. exc=ValueOutOfBounds,
  515. msg=f"value has {len(value)} items when {self.array_size} were "
  516. "expected",
  517. )
  518. def encode(self, value):
  519. encoded_elements = self.encode_elements(value)
  520. return encoded_elements
  521. class DynamicArrayEncoder(BaseArrayEncoder):
  522. is_dynamic = True
  523. def encode(self, value):
  524. encoded_size = encode_uint_256(len(value))
  525. encoded_elements = self.encode_elements(value)
  526. encoded_value = encoded_size + encoded_elements
  527. return encoded_value