Ejemplo n.º 1
0
def decode(rlp, sedes=None, strict=True, recursive_cache=False, **kwargs):
    """Decode an RLP encoded object.

    If the deserialized result `obj` has an attribute :attr:`_cached_rlp` (e.g. if `sedes` is a
    subclass of :class:`rlp.Serializable`) it will be set to `rlp`, which will improve performance
    on subsequent :func:`rlp.encode` calls. Bear in mind however that `obj` needs to make sure that
    this value is updated whenever one of its fields changes or prevent such changes entirely
    (:class:`rlp.sedes.Serializable` does the latter).

    :param sedes: an object implementing a function ``deserialize(code)`` which will be applied
                  after decoding, or ``None`` if no deserialization should be performed
    :param \*\*kwargs: additional keyword arguments that will be passed to the deserializer
    :param strict: if false inputs that are longer than necessary don't cause an exception
    :returns: the decoded and maybe deserialized Python object
    :raises: :exc:`rlp.DecodingError` if the input string does not end after the root item and
             `strict` is true
    :raises: :exc:`rlp.DeserializationError` if the deserialization fails
    """
    if not is_bytes(rlp):
        raise DecodingError('Can only decode RLP bytes, got type %s' % type(rlp).__name__, rlp)
    try:
        item, per_item_rlp, end = consume_item(rlp, 0)
    except IndexError:
        raise DecodingError('RLP string too short', rlp)
    if end != len(rlp) and strict:
        msg = 'RLP string ends with {} superfluous bytes'.format(len(rlp) - end)
        raise DecodingError(msg, rlp)
    if sedes:
        obj = sedes.deserialize(item, **kwargs)
        if is_sequence(obj) or hasattr(obj, '_cached_rlp'):
            _apply_rlp_cache(obj, per_item_rlp, recursive_cache)
        return obj
    else:
        return item
Ejemplo n.º 2
0
def consume_length_prefix(rlp, start):
    """Read a length prefix from an RLP string.

    :param rlp: the rlp byte string to read from
    :param start: the position at which to start reading
    :returns: a tuple ``(type, length, end)``, where ``type`` is either ``str``
              or ``list`` depending on the type of the following payload,
              ``length`` is the length of the payload in bytes, and ``end`` is
              the position of the first payload byte in the rlp string
    """
    b0 = rlp[start]
    if b0 < 128:  # single byte
        return (bytes, 1, start)
    elif b0 < SHORT_STRING:  # short string
        if b0 - 128 == 1 and rlp[start + 1] < 128:
            raise DecodingError('Encoded as short string although single byte was possible', rlp)
        return (bytes, b0 - 128, start + 1)
    elif b0 < 192:  # long string
        ll = b0 - 183  # - (128 + 56 - 1)
        if rlp[start + 1:start + 2] == b'\x00':
            raise DecodingError('Length starts with zero bytes', rlp)
        l = big_endian_to_int(rlp[start + 1:start + 1 + ll])
        if l < 56:
            raise DecodingError('Long string prefix used for short string', rlp)
        return (bytes, l, start + 1 + ll)
    elif b0 < 192 + 56:  # short list
        return (list, b0 - 192, start + 1)
    else:  # long list
        ll = b0 - 192 - 56 + 1
        if rlp[start + 1:start + 2] == b'\x00':
            raise DecodingError('Length starts with zero bytes', rlp)
        l = big_endian_to_int(rlp[start + 1:start + 1 + ll])
        if l < 56:
            raise DecodingError('Long list prefix used for short list', rlp)
        return (list, l, start + 1 + ll)
Ejemplo n.º 3
0
    def decode_raw(item, strict, _):
        try:
            result, per_item_rlp, end = consume_item(item, 0)
        except IndexError:
            raise DecodingError('RLP string too short', item)
        if end != len(item) and strict:
            msg = 'RLP string ends with {} superfluous bytes'.format(
                len(item) - end)
            raise DecodingError(msg, item)

        return result, per_item_rlp
Ejemplo n.º 4
0
def consume_payload(rlp, start, type_, length):
    """Read the payload of an item from an RLP string.

    :param rlp: the rlp string to read from
    :param type_: the type of the payload (``bytes`` or ``list``)
    :param start: the position at which to start reading
    :param length: the length of the payload in bytes
    :returns: a tuple ``(item, end)``, where ``item`` is the read item and
              ``end`` is the position of the first unprocessed byte
    """
    if type_ is bytes:
        return (rlp[start:start + length], start + length)
    elif type_ is list:
        items = []
        next_item_start = start
        end = next_item_start + length
        while next_item_start < end:
            # item, next_item_start = consume_item(rlp, next_item_start)
            t, l, s = consume_length_prefix(rlp, next_item_start)
            item, next_item_start = consume_payload(rlp, s, t, l)
            items.append(item)
        if next_item_start > end:
            raise DecodingError('List length prefix announced a too small '
                                'length', rlp)
        return (items, next_item_start)
    else:
        raise TypeError('Type must be either list or bytes')
Ejemplo n.º 5
0
def consume_payload(rlp, prefix, start, type_, length):
    """Read the payload of an item from an RLP string.

    :param rlp: the rlp string to read from
    :param type_: the type of the payload (``bytes`` or ``list``)
    :param start: the position at which to start reading
    :param length: the length of the payload in bytes
    :returns: a tuple ``(item, per_item_rlp, end)``, where ``item`` is
              the read item, per_item_rlp is a list containing the RLP
              encoding of each item and ``end`` is the position of the
              first unprocessed byte
    """
    if type_ is bytes:
        item = rlp[start:start + length]
        return (item, [prefix + item], start + length)
    elif type_ is list:
        items = []
        per_item_rlp = []
        list_rlp = prefix
        next_item_start = start
        end = next_item_start + length
        while next_item_start < end:
            p, t, l, s = consume_length_prefix(rlp, next_item_start)
            item, item_rlp, next_item_start = consume_payload(rlp, p, s, t, l)
            per_item_rlp.append(item_rlp)
            # When the item returned above is a single element, item_rlp will also contain a
            # single element, but when it's a list, the first element will be the RLP of the
            # whole List, which is what we want here.
            list_rlp += item_rlp[0]
            items.append(item)
        per_item_rlp.insert(0, list_rlp)
        if next_item_start > end:
            raise DecodingError(
                'List length prefix announced a too small '
                'length', rlp)
        return (items, per_item_rlp, next_item_start)
    else:
        raise TypeError('Type must be either list or bytes')
Ejemplo n.º 6
0
def decode_raw(item, strict, preserve_per_item_rlp):
    try:
        return rusty_rlp.decode_raw(item, strict, preserve_per_item_rlp)
    except (TypeError, rusty_rlp.DecodingError) as e:
        raise DecodingError(e, item)