Skip to content

Commit

Permalink
Refactor smell to be in kering and uses in Serder Serdery prelim to v…
Browse files Browse the repository at this point in the history
…ersion the version string.
  • Loading branch information
SmithSamuelM committed Feb 22, 2024
1 parent 616c05d commit f4bb5a9
Show file tree
Hide file tree
Showing 3 changed files with 81 additions and 68 deletions.
15 changes: 8 additions & 7 deletions src/keri/core/coring.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@
ShortageError, UnexpectedCodeError, DeserializeError,
UnexpectedCountCodeError, UnexpectedOpCodeError)
from ..kering import (Versionage, Version, VERRAWSIZE, VERFMT, VERFULLSIZE,
versify, deversify, Rever)
versify, deversify, Rever, smell)
from ..kering import Serials, Serialage, Protos, Protocolage, Ilkage, Ilks
from ..kering import (ICP_LABELS, DIP_LABELS, ROT_LABELS, DRT_LABELS, IXN_LABELS,
RPY_LABELS)
Expand Down Expand Up @@ -5363,6 +5363,7 @@ def compare(self, said=None):
else:
raise ValueError("Both said and saider may not be None.")


@staticmethod
def smell(raw):
"""
Expand All @@ -5373,14 +5374,14 @@ def smell(raw):
raw is bytes of serialized event
"""
if len(raw) < Sadder.SmellSize:
raise ShortageError("Need more bytes.")
#if len(raw) < Sadder.SmellSize:
#raise ShortageError("Need more bytes.")

match = Rever.search(raw) # Rever's regex takes bytes
if not match or match.start() > 12:
raise VersionError("Invalid version string in raw = {}".format(raw))
#match = Rever.search(raw) # Rever's regex takes bytes
#if not match or match.start() > 12:
#raise VersionError("Invalid version string in raw = {}".format(raw))

proto, major, minor, kind, size = match.group("proto", "major", "minor", "kind", "size")
proto, major, minor, kind, size = smell(raw)
version = Versionage(major=int(major, 16), minor=int(minor, 16))
kind = kind.decode("utf-8")
proto = proto.decode("utf-8")
Expand Down
81 changes: 20 additions & 61 deletions src/keri/core/serdering.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,8 @@
ShortageError, VersionError, ProtocolError, KindError,
DeserializeError, FieldError, SerializeError)
from ..kering import (Versionage, Version, Vrsn_1_0, Vrsn_1_1,
VERRAWSIZE, VERFMT, VERFULLSIZE)
VERRAWSIZE, VERFMT, VERFULLSIZE,
SMELLSIZE, Smellage, smell)
from ..kering import Protos, Serials, Rever, versify, deversify, Ilks
from ..core import coring
from .coring import MtrDex, DigDex, PreDex, Saids, Digestage
Expand All @@ -42,17 +43,6 @@
Fieldage = namedtuple("Fieldage", "saids alls") #values are dicts


"""
Reapage
proto (str): protocol type value of Protos examples 'KERI', 'ACDC'
major (str): single char hex string of major version number
minor (str): single char hex string of minor version number
kind (str): serialization value of Serials examples 'JSON', 'CBOR', 'MGPK'
"""
Reapage = namedtuple("Reapage", "proto major minor kind size")


class Serdery:
"""Serder factory class for generating serder instances by protocol type
from an incoming message stream.
Expand Down Expand Up @@ -86,28 +76,14 @@ def reap(self, ims, *, version=None):
"""
version = version if version is not None else self.version

if len(ims) < Serder.SmellSize:
raise ShortageError(f"Need more raw bytes for Serdery to reap.")

match = Rever.search(ims) # Rever regex takes bytes/bytearray not str
if not match or match.start() > Serder.MaxVSOffset:
raise VersionError(f"Invalid version string for Serder raw = "
f"{ims[: Serder.SmellSize]}.")

reaped = Reapage(*match.group("proto", "major", "minor", "kind", "size"))
smellage = smell(ims, version=version)

vrsn = Versionage(major=int(reaped.major, 16), minor=int(reaped.minor, 16))
if version: # test here for compatible cod version with message vrsn
if (vrsn.major > version.major or
(vrsn.major == version.major and vrsn.minor > version.minor)):
pass # raise error here?

if reaped.proto == Protos.keri.encode("utf-8"):
return SerderKERI(raw=ims, strip=True, version=version, reaped=reaped)
elif reaped.proto == Protos.acdc.encode("utf-8"):
return SerderACDC(raw=ims, strip=True, version=version, reaped=reaped)
if smellage.proto == Protos.keri.encode("utf-8"):
return SerderKERI(raw=ims, strip=True, version=version, smellage=smellage)
elif smellage.proto == Protos.acdc.encode("utf-8"):
return SerderACDC(raw=ims, strip=True, version=version, smellage=smellage)
else:
raise ProtocolError(f"Unsupported protocol type = {reaped.proto}.")
raise ProtocolError(f"Unsupported protocol type = {smellage.proto}.")



Expand Down Expand Up @@ -138,8 +114,6 @@ class Serder:
generation and verification in addition to the required fields.
Class Attributes:
MaxVSOffset (int): Maximum Version String Offset in bytes/chars
InhaleSize (int): Minimum raw buffer size needed to inhale
Labels (dict): Protocol specific dict of field labels keyed by ilk
(packet type string value). None is default key when no ilk needed.
Each entry is a
Expand Down Expand Up @@ -185,10 +159,6 @@ class Serder:
loads and jumps of json use str whereas cbor and msgpack use bytes
"""

MaxVSOffset = 12
SmellSize = MaxVSOffset + VERFULLSIZE # min buffer size to inhale

Dummy = "#" # dummy spaceholder char for said. Must not be a valid Base64 char

# should be same set of codes as in coring.DigestCodex coring.DigDex so
Expand Down Expand Up @@ -357,7 +327,7 @@ class Serder:


def __init__(self, *, raw=b'', sad=None, strip=False, version=Version,
reaped=None, verify=True, makify=False,
smellage=None, verify=True, makify=False,
proto=None, vrsn=None, kind=None, ilk=None, saids=None):
"""Deserialize raw if provided. Update properties from deserialized raw.
Verifies said(s) embedded in sad as given by labels.
Expand All @@ -377,10 +347,10 @@ def __init__(self, *, raw=b'', sad=None, strip=False, version=Version,
Assumes that raw is bytearray when strip is True.
version (Versionage | None): instance supported protocol version
None means do not enforce a supported version
reaped (Reapage | None): instance of deconstructed version string
smellage (Smellage | None): instance of deconstructed version string
elements. If none or empty ignore otherwise assume that raw
already had its version string extracted (reaped) into the
elements of reaped.
elements of smellage.
verify (bool): True means verify said(s) of given raw or sad.
Raises ValidationError if verification fails
Ignore when raw not provided or when raw and saidify is True
Expand Down Expand Up @@ -410,7 +380,7 @@ def __init__(self, *, raw=b'', sad=None, strip=False, version=Version,
# self._inhale works because it only references class attributes
sad, proto, vrsn, kind, size = self._inhale(raw=raw,
version=version,
reaped=reaped)
smellage=smellage)
self._raw = bytes(raw[:size]) # crypto ops require bytes not bytearray
self._sad = sad
self._proto = proto
Expand Down Expand Up @@ -757,7 +727,7 @@ def makify(self, sad, *, version=None,


@classmethod
def _inhale(clas, raw, version=Version, reaped=None):
def _inhale(clas, raw, version=Version, smellage=None):
"""Deserializes raw.
Parses serilized event ser of serialization kind and assigns to
instance attributes and returns tuple of associated elements.
Expand All @@ -776,32 +746,21 @@ def _inhale(clas, raw, version=Version, reaped=None):
Parameters:
raw (bytes): serialized sad message
version (Versionage): instance supported protocol version
reaped (Reapage | None): instance of deconstructed version string
smellage (Smellage | None): instance of deconstructed version string
elements. If none or empty ignore otherwise assume that raw
already had its version string extracted (reaped) into the
elements of reaped.
elements of smellage.
Note:
loads and jumps of json use str whereas cbor and msgpack use bytes
Assumes only supports Version
"""
if reaped:
proto, major, minor, kind, size = reaped # tuple unpack
else:
if len(raw) < clas.SmellSize:
raise ShortageError(f"Need more raw bytes for Serder to inhale.")

match = Rever.search(raw) # Rever regex takes bytes/bytearray not str
if not match or match.start() > clas.MaxVSOffset:
raise VersionError(f"Invalid version string in raw = "
f"{raw[:clas.SmellSize]}.")
if smellage: # passed in so don't need to smell raw again
proto, major, minor, kind, size = smellage # tuple unpack
else: # not passed in so smell raw
proto, major, minor, kind, size = smell(raw, version=version)

proto, major, minor, kind, size = match.group("proto",
"major",
"minor",
"kind",
"size")

proto = proto.decode("utf-8")
if proto not in Protos:
Expand All @@ -822,7 +781,7 @@ def _inhale(clas, raw, version=Version, reaped=None):

sad = clas.loads(raw=raw, size=size, kind=kind)

if "v" not in sad:
if "v" not in sad: # Regex does not check for version string label itself
raise FieldError(f"Missing version string field in {sad}.")

return sad, proto, vrsn, kind, size
Expand Down
53 changes: 53 additions & 0 deletions src/keri/kering.py
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,57 @@ def deversify(vs, version=None):

raise ValueError("Invalid version string = {}".format(vs))



MAXVSOFFSET = 12
SMELLSIZE = MAXVSOFFSET + VERFULLSIZE # min buffer size to inhale

"""
Smellage (results of smelling a version string such as in a Serder)
proto (str): protocol type value of Protos examples 'KERI', 'ACDC'
major (str): single char hex string of major version number
minor (str): single char hex string of minor version number
kind (str): serialization value of Serials examples 'JSON', 'CBOR', 'MGPK'
size (str): hex string of size of raw serialization
"""
Smellage = namedtuple("Smellage", "proto major minor kind size")

def smell(raw, *, version=None):
"""Extract and return Smellage from version string inside serialized raw.
Returns:
smellage (Smellage): named Tuple of proto, major, minor, kind, size
Parameters:
raw (bytearray) of serialized incoming message stream. Assumes start
of stream is JSON, CBOR, or MGPK field map with first field
is labeled 'v' and value is version string.
version (Versionage | None): instance supported protocol version
None means do not enforce a supported version
"""
if len(raw) < SMELLSIZE:
raise ShortageError(f"Need more raw bytes to smell full version string.")

match = Rever.search(raw) # Rever regex takes bytes/bytearray not str
if not match or match.start() > MAXVSOFFSET:
raise VersionError(f"Invalid version string from smelled raw = "
f"{raw[: SMELLSIZE]}.")

smellage = Smellage(*match.group("proto", "major", "minor", "kind", "size"))

# Global version compatibility check. Serder instances also peform version check
vrsn = Versionage(major=int(smellage.major, 16), minor=int(smellage.minor, 16))
if version: # test here for compatible code version with message vrsn
if (vrsn.major > version.major or
(vrsn.major == version.major and vrsn.minor > version.minor)):
pass # raise error here?

return smellage




"""
ilk is short for packet or message type for a given protocol
icp = incept, inception
Expand Down Expand Up @@ -166,6 +217,8 @@ def deversify(vs, version=None):
CRED_TSN_LABELS = ["v", "i", "s", "d", "ri", "a", "ra"]



# Exception Subclasses
class KeriError(Exception):
"""
Base Class for keri exceptions
Expand Down

0 comments on commit f4bb5a9

Please sign in to comment.