def serialize(val, typ=None): if typ is None and hasattr(val, 'fields'): typ = type(val) if typ in ('hash32', 'address'): assert len(val) == 20 if typ == 'address' else 32 return val elif isinstance(typ, str) and typ[:3] == 'int': length = int(typ[3:]) assert length % 8 == 0 return val.to_bytes(length // 8, 'big') elif typ == 'bytes': return len(val).to_bytes(4, 'big') + val elif isinstance(typ, list): assert len(typ) == 1 sub = b''.join([serialize(x, typ[0]) for x in val]) return len(sub).to_bytes(4, 'big') + sub elif isinstance(typ, type): sub = b''.join([serialize(getattr(val, k), typ.fields[k]) for k in sorted(typ.fields.keys())]) return len(sub).to_bytes(4, 'big') + sub raise Exception("Cannot serialize", val, typ) def _deserialize(data, start, typ): if typ in ('hash32', 'address'): length = 20 if typ == 'address' else 32 assert len(data) + start >= length return data[start: start+length], start+length elif isinstance(typ, str) and typ[:3] == 'int': length = int(typ[3:]) assert length % 8 == 0 assert len(data) + start >= length // 8 return int.from_bytes(data[start: start+length//8], 'big'), start+length//8 elif typ == 'bytes': length = int.from_bytes(data[start:start+4], 'big') assert len(data) + start >= 4+length return data[start+4: start+4+length], start+4+length elif isinstance(typ, list): assert len(typ) == 1 length = int.from_bytes(data[start:start+4], 'big') pos, o = start + 4, [] while pos < start + 4 + length: result, pos = _deserialize(data, pos, typ[0]) o.append(result) assert pos == start + 4 + length return o, pos elif isinstance(typ, type): length = int.from_bytes(data[start:start+4], 'big') values = {} pos = start + 4 for k in sorted(typ.fields.keys()): values[k], pos = _deserialize(data, pos, typ.fields[k]) assert pos == start + 4 + length return typ(**values), pos raise Exception("Cannot deserialize", typ) def deserialize(data, typ): return _deserialize(data, 0, typ)[0] def eq(x, y): if hasattr(x, 'fields') and hasattr(y, 'fields'): for f in x.fields: if not eq(getattr(x, f), getattr(y, f)): print('Unequal:', x, y, f, getattr(x, f), getattr(y, f)) return False return True else: return x == y def deepcopy(x): if hasattr(x, 'fields'): vals = {} for f in x.fields.keys(): vals[f] = deepcopy(getattr(x, f)) return x.__class__(**vals) elif isinstance(x, list): return [deepcopy(y) for y in x] else: return x def to_dict(x): if hasattr(x, 'fields'): vals = {} for f in x.fields.keys(): vals[f] = to_dict(getattr(x, f)) return vals elif isinstance(x, list): return [to_dict(y) for y in x] else: return x