Source code for datatrails_receipt_scitt.attribute_decoder

"""
Module for decoding on chain attributes into the format returned from the event api
"""

from __future__ import annotations

from enum import Enum

from rlp.sedes import binary, List

from rlp import decode, decode_lazy, peek, DeserializationError

from .exceptions import InvalidAttributeType

VALUE_TYPE_LIST = "listv2"
VALUE_TYPE_DICT = "dictv2"


[docs] class AttributeType(Enum): """ Attribute type is the type of attributes supported by DataTrails events """ ASSET = 1 EVENT = 2
[docs] def decode_attribute_value(attrvalue: bytes) -> str | list | dict: """ decode an rlp encoded attribute value :param str hex: an rlp encoded hexadecimal string, e.g. 0xe5866c6973747632cecd87676972... :returns: one of: * a string value * a dictionary value * a list of dictionary values the rlp encoded list value is of the shape:: [][][]string { [ "listv2" ], [ [ [ "giraffe", <- key "tall" <- value ], ], ], [ [ [ "elephant", <- key "big" <- value ], ], ] } the rlp encoded dict value is of the shape:: [][]string { [ [ "dictv2", ], [ "giraffe", <- key "tall" <- value ], [ "elephant", <- key "big" <- value ] ] } """ # first see if its a string value try: value = decode(attrvalue, binary).decode("utf-8") return value except DeserializationError: # if we have a deserialization error here, it means we are not dealing with a string value pass # if its not a string value it must be a list or a dictionary listv2_attribute_value = List([List([binary, binary])]) dictv2_attribute_value = List([binary, binary]) # if we lazily decode the rlp without a sedes, we can iterate through # each list element decoding them one at a time decoded_value = decode_lazy(attrvalue, None) value_type = None list_value = [] dict_value = {} for index, element in enumerate(decoded_value): # first index determines which value type we have, # list or dict if index == 0: value_type = element.decode("utf-8") continue # attribute value is a list if value_type == VALUE_TYPE_LIST: value = peek(element.rlp, index, listv2_attribute_value) value_dict = {value[0][0].decode("utf-8"): value[0][1].decode("utf-8")} list_value.append(value_dict) continue # attribute value is a dict if value_type == VALUE_TYPE_DICT: value = peek(element.rlp, index, dictv2_attribute_value) dict_value[value[0].decode("utf-8")] = value[1].decode("utf-8") continue # determine whether we should return a list or dict if len(list_value) == 0: return dict_value return list_value
[docs] def decode_attribute_key(kind_name: bytes) -> tuple[AttributeType, str]: """Decodes the attribute kind<->name pairing into the attribute kind and keys :param str kind_name: the rlp encoded attribute kind concatenated with the attribute key encoded as a hex string, e.g. 0x8767697261666665... :return: a tuple of (attribute type, attribute key) """ # """ # :param kind_name: str the rlp encoded attribute kind concatenated with the attribute key encoded as a hex string, e.g. 0x8767697261666665... # :return: a tuple of (attribute type, attribute key) # """ kind_name_sedes = List([binary, binary]) decoded_kind_name = decode(kind_name, kind_name_sedes) if decoded_kind_name[0].decode("utf-8") == AttributeType.ASSET.name.lower(): kind = AttributeType.ASSET key = decoded_kind_name[1].decode("utf-8") elif decoded_kind_name[0].decode("utf-8") == AttributeType.EVENT.name.lower(): kind = AttributeType.EVENT key = decoded_kind_name[1].decode("utf-8") else: raise InvalidAttributeType return (kind, key)