Skip to content

Commit

Permalink
xmr: simplify serialization, remove Archive
Browse files Browse the repository at this point in the history
  • Loading branch information
jpochyla committed Oct 9, 2018
1 parent 896cdeb commit 24c5251
Show file tree
Hide file tree
Showing 9 changed files with 211 additions and 596 deletions.
22 changes: 9 additions & 13 deletions src/apps/monero/controller/misc.py
Original file line number Diff line number Diff line change
Expand Up @@ -58,36 +58,32 @@ async def monero_get_creds(ctx, address_n=None, network_type=None):
return creds


def parse_msg(bts, msg):
from apps.monero.xmr.serialize import xmrserialize
def parse_msg(bts, msg_type):
from apps.monero.xmr.serialize.readwriter import MemoryReaderWriter

reader = MemoryReaderWriter(memoryview(bts))
ar = xmrserialize.Archive(reader, False)
return ar.message(msg)
return msg_type.load(reader)


def dump_msg(msg, preallocate=None, msg_type=None, prefix=None):
from apps.monero.xmr.serialize import xmrserialize
def dump_msg(msg, preallocate=None, prefix=None):
from apps.monero.xmr.serialize.readwriter import MemoryReaderWriter

writer = MemoryReaderWriter(preallocate=preallocate)
if prefix:
writer.write(prefix)
ar = xmrserialize.Archive(writer, True)
ar.message(msg, msg_type=msg_type)
msg_type = msg.__class__
msg_type.dump(writer, msg)
return writer.get_buffer()


def dump_msg_gc(msg, preallocate=None, msg_type=None, del_msg=False):
b = dump_msg(msg, preallocate=preallocate, msg_type=msg_type)
if del_msg:
del msg
def dump_msg_gc(msg, preallocate=None, prefix=None):
buf = dump_msg(msg, preallocate=preallocate, prefix=None)
del msg

import gc

gc.collect()
return b
return buf


def dump_rsig_bp(rsig):
Expand Down
2 changes: 1 addition & 1 deletion src/apps/monero/protocol/signing/step_06_set_out1.py
Original file line number Diff line number Diff line change
Expand Up @@ -209,7 +209,7 @@ def _range_proof(state, idx, amount, rsig_data=None):
_get_out_mask(state, 1 + idx - batch_size + ix) for ix in range(batch_size)
]

bp_obj = misc.parse_msg(rsig_data.rsig, Bulletproof())
bp_obj = misc.parse_msg(rsig_data.rsig, Bulletproof)
rsig_data.rsig = None

# BP is hashed with raw=False as hash does not contain L, R
Expand Down
2 changes: 1 addition & 1 deletion src/apps/monero/protocol/signing/step_09_sign_input.py
Original file line number Diff line number Diff line change
Expand Up @@ -173,5 +173,5 @@ async def sign_input(
)

return MoneroTransactionSignInputAck(
signature=misc.dump_msg_gc(mgs[0], preallocate=488, del_msg=True), cout=cout
signature=misc.dump_msg_gc(mgs[0], preallocate=488), cout=cout
)
26 changes: 23 additions & 3 deletions src/apps/monero/xmr/serialize/base_types.py
Original file line number Diff line number Diff line change
@@ -1,15 +1,35 @@
from apps.monero.xmr.serialize.int_serialize import (
dump_uint,
dump_uvarint,
load_uint,
load_uvarint,
)


class XmrType:
pass


class UVarintType(XmrType):
pass
@staticmethod
def load(reader) -> int:
return load_uvarint(reader)

@staticmethod
def dump(writer, n: int):
return dump_uvarint(writer, n)


class IntType(XmrType):
WIDTH = 0
SIGNED = 0
VARIABLE = 0

@classmethod
def load(cls, reader) -> int:
return load_uint(reader, cls.WIDTH)

@classmethod
def dump(cls, writer, n: int):
return dump_uint(writer, n, cls.WIDTH)


class UInt8(IntType):
Expand Down
199 changes: 123 additions & 76 deletions src/apps/monero/xmr/serialize/message_types.py
Original file line number Diff line number Diff line change
@@ -1,116 +1,163 @@
from trezor.utils import obj_eq, obj_repr

from apps.monero.xmr.serialize.base_types import XmrType
from apps.monero.xmr.serialize.obj_helper import eq_obj_contents, is_type, slot_obj_dict
from apps.monero.xmr.serialize.int_serialize import (
dump_uint,
dump_uvarint,
load_uint,
load_uvarint,
)


class BlobType(XmrType):
class UnicodeType(XmrType):
"""
Binary data
Represented as bytearray() or a list of values in data structures.
Not wrapped in the BlobType, the BlobType is only a scheme descriptor.
Behaves in the same way as primitive types
Supports also the wrapped version (__init__, DATA_ATTR, eq, repr...),
Unicode data in UTF-8 encoding.
"""

DATA_ATTR = "data"
FIX_SIZE = 0
SIZE = 0

def __eq__(self, rhs):
return eq_obj_contents(self, rhs)

def __repr__(self):
dct = slot_obj_dict(self) if hasattr(self, "__slots__") else self.__dict__
return "<%s: %s>" % (self.__class__.__name__, dct)
@staticmethod
def dump(writer, s):
dump_uvarint(writer, len(s))
writer.write(bytes(s))


class UnicodeType(XmrType):
pass
@staticmethod
def load(reader):
ivalue = load_uvarint(reader)
fvalue = bytearray(ivalue)
reader.readinto(fvalue)
return str(fvalue)


class VariantType(XmrType):
class BlobType(XmrType):
"""
Union of types, variant tags needed. is only one of the types. List in typedef, enum.
Wraps the variant type in order to unambiguously support variant of variants.
Supports also unwrapped value using type system to distinguish variants - simplifies the construction.
Binary data, represented as bytearray. BlobType is only a scheme
descriptor. Behaves in the same way as primitive types.
"""

WRAPS_VALUE = False

def __init__(self):
self.variant_elem = None
self.variant_elem_type = None
FIX_SIZE = 0
SIZE = 0

@classmethod
def f_specs(cls):
return ()
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)

def set_variant(self, fname, fvalue):
self.variant_elem = fname
self.variant_elem_type = fvalue.__class__
setattr(self, fname, fvalue)

def __eq__(self, rhs):
return eq_obj_contents(self, rhs)

def __repr__(self):
dct = slot_obj_dict(self) if hasattr(self, "__slots__") else self.__dict__
return "<%s: %s>" % (self.__class__.__name__, dct)
@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 real array in the data structures, not wrapped in the ContainerType.
The Container type is used only as a schema descriptor for serialization.
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)

class MessageType(XmrType):
def __init__(self, **kwargs):
for kw in kwargs:
setattr(self, kw, kwargs[kw])
@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("Unrecognized variant: %s" % elem)

def __eq__(self, rhs):
return eq_obj_contents(self, rhs)
dump_uint(writer, ftype.VARIANT_CODE, 1)
ftype.dump(writer, elem)

def __repr__(self):
dct = slot_obj_dict(self) if hasattr(self, "__slots__") else self.__dict__
return "<%s: %s>" % (self.__class__.__name__, dct)
@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("Unknown tag: %s" % tag)
return fvalue

@classmethod
def f_specs(cls):
return ()


def container_elem_type(container_type, params):
class MessageType(XmrType):
"""
Returns container element type
Message composed of fields with specific types.
"""
elem_type = params[0] if params else None
if elem_type is None:
elem_type = container_type.ELEM_TYPE
return elem_type

def __init__(self, **kwargs):
for kw in kwargs:
setattr(self, kw, kwargs[kw])

def gen_elem_array(size, elem_type=None):
"""
Generates element array of given size and initializes with given type.
Supports container type, used for pre-allocation before deserialization.
"""
if elem_type is None or not callable(elem_type):
return [elem_type] * size
if is_type(elem_type, ContainerType):
__eq__ = obj_eq
__repr__ = obj_repr

def elem_type():
return []
@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)

res = []
for _ in range(size):
res.append(elem_type())
return res
@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 ()
49 changes: 0 additions & 49 deletions src/apps/monero/xmr/serialize/obj_helper.py

This file was deleted.

Loading

0 comments on commit 24c5251

Please sign in to comment.