You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
trezor-firmware/core/src/apps/monero/xmr/serialize/message_types.py

164 lines
3.9 KiB

from trezor.utils import obj_eq, obj_repr
from apps.monero.xmr.serialize.base_types import XmrType
from apps.monero.xmr.serialize.int_serialize import (
dump_uint,
dump_uvarint,
load_uint,
load_uvarint,
)
class UnicodeType(XmrType):
"""
Unicode data in UTF-8 encoding.
"""
@staticmethod
def dump(writer, s):
dump_uvarint(writer, len(s))
writer.write(bytes(s))
@staticmethod
def load(reader):
ivalue = load_uvarint(reader)
fvalue = bytearray(ivalue)
reader.readinto(fvalue)
return str(fvalue)
class BlobType(XmrType):
"""
Binary data, represented as bytearray. BlobType is only a scheme
descriptor. Behaves in the same way as primitive types.
"""
FIX_SIZE = 0
SIZE = 0
@classmethod
def dump(cls, writer, elem: bytes):
if cls.FIX_SIZE:
if cls.SIZE != len(elem):
raise ValueError("Size mismatch")
else:
dump_uvarint(writer, len(elem))
writer.write(elem)
@classmethod
def load(cls, reader) -> bytearray:
if cls.FIX_SIZE:
size = cls.SIZE
else:
size = load_uvarint(reader)
elem = bytearray(size)
reader.readinto(elem)
return elem
class ContainerType(XmrType):
"""
Array of elements, represented as a list of items. ContainerType is only a
scheme descriptor.
"""
FIX_SIZE = 0
SIZE = 0
ELEM_TYPE = None
@classmethod
def dump(cls, writer, elems, elem_type=None):
if elem_type is None:
elem_type = cls.ELEM_TYPE
if cls.FIX_SIZE:
if cls.SIZE != len(elems):
raise ValueError("Size mismatch")
else:
dump_uvarint(writer, len(elems))
for elem in elems:
elem_type.dump(writer, elem)
@classmethod
def load(cls, reader, elem_type=None):
if elem_type is None:
elem_type = cls.ELEM_TYPE
if cls.FIX_SIZE:
size = cls.SIZE
else:
size = load_uvarint(reader)
elems = []
for _ in range(size):
elem = elem_type.load(reader)
elems.append(elem)
return elems
class VariantType(XmrType):
"""
Union of types, differentiated by variant tags. VariantType is only a scheme
descriptor.
"""
@classmethod
def dump(cls, writer, elem):
for field in cls.f_specs():
ftype = field[1]
if isinstance(elem, ftype):
break
else:
raise ValueError(f"Unrecognized variant: {elem}")
dump_uint(writer, ftype.VARIANT_CODE, 1)
ftype.dump(writer, elem)
@classmethod
def load(cls, reader):
tag = load_uint(reader, 1)
for field in cls.f_specs():
ftype = field[1]
if ftype.VARIANT_CODE == tag:
fvalue = ftype.load(reader)
break
else:
raise ValueError(f"Unknown tag: {tag}")
return fvalue
@classmethod
def f_specs(cls):
return ()
class MessageType(XmrType):
"""
Message composed of fields with specific types.
"""
def __init__(self, **kwargs):
for kw in kwargs: # pylint: disable=consider-using-dict-items
setattr(self, kw, kwargs[kw])
__eq__ = obj_eq
__repr__ = obj_repr
@classmethod
def dump(cls, writer, msg):
defs = cls.f_specs()
for field in defs:
fname, ftype, *fparams = field
fvalue = getattr(msg, fname, None)
ftype.dump(writer, fvalue, *fparams)
@classmethod
def load(cls, reader):
msg = cls()
defs = cls.f_specs()
for field in defs:
fname, ftype, *fparams = field
fvalue = ftype.load(reader, *fparams)
setattr(msg, fname, fvalue)
return msg
@classmethod
def f_specs(cls):
return ()