Skip to content

Commit

Permalink
preliminary basic support for cesr native serialization of keri event…
Browse files Browse the repository at this point in the history
… messages. With tests for inception
  • Loading branch information
SmithSamuelM committed Apr 12, 2024
1 parent 1968bbe commit 18cf918
Show file tree
Hide file tree
Showing 5 changed files with 211 additions and 72 deletions.
2 changes: 1 addition & 1 deletion src/keri/core/coring.py
Original file line number Diff line number Diff line change
Expand Up @@ -4285,7 +4285,7 @@ class Tholder:
when unweighted is size of int thold since don't have anyway
to know size of keys list in this case
.limen is qualified b64 signing threshold suitable for CESR serialization.
.limen is qualified b64b signing threshold suitable for CESR serialization.
either Number.qb64b or Bexter.qb64b.
The b64 portion of limen with code stripped (Bexter.bext) of
[["1/2", "1/2", "1/4", "1/4", "1/4"], ["1", "1"]]
Expand Down
41 changes: 20 additions & 21 deletions src/keri/core/counting.py
Original file line number Diff line number Diff line change
Expand Up @@ -212,25 +212,25 @@ class Counter:
.version (Versionage): current CESR code table protocol genus version
.codes (CounterCodex_1_0 | CounterCodex_1_0): version specific codex
.sizes (dict): version specific sizes table
.code (str) derivation code to indicate cypher suite
.raw is bytes crypto material only without code
.pad is int number of pad chars given raw
.count is int count of quadlets/triplets of following framed material
(not including code)
.qb64 is str in Base64 fully qualified with derivation code + crypto mat
.qb64b is bytes in Base64 fully qualified with derivation code + crypto mat
.qb2 is bytes in binary with derivation code + crypto material
.code (str): hard part of derivation code to indicate cypher suite
.raw (bytes): crypto material only without code
.pad (int): number of pad chars given raw
.count (int): count of quadlets/triplets of following framed material
(not including code)
.qb64 (str | bytes | bytearray): in Base64 fully qualified with
derivation code + crypto mat
.qb64b (bytes | bytearray): in Base64 fully qualified with
derivation code + crypto mat
.qb2 (bytes | bytearray): in binary with derivation code +
crypto material
Hidden:
._version (Versionage): value for .version property
._codes (CounterCodex_1_0 | CounterCodex_1_0): version specific codex
._sizes (dict): version specific sizes table
._code is str value for .code property
._raw is bytes value for .raw property
._pad is method to compute .pad property
._count is int value for .count property
._infil is method to compute fully qualified Base64 from .raw and .code
._exfil is method to extract .code and .raw from fully qualified Base64
._code (str): value for .code property
._raw (bytes): value for .raw property
._count (int): value for .count property
Versioning:
Expand Down Expand Up @@ -423,13 +423,12 @@ def __init__(self, tag=None, *, code = None, count=None, countB64=None,
code (str | None): stable (hard) part of derivation code
if tag provided lookup code from tag
else if tag is None and code provided use code
count (int | None): count of framed material for composition
Count does not include code.
Count represents quadlets/triplets
When both count and countB64 are None then count defaults to 1
countB64 (str | None): count of framed material for composition
as Base64
countB64 represents quadlets/triplets
count (int | None): count of framed material in quadlets/triplets
for composition. Count does not include code.
When both count and countB64 are None then count
defaults to 1
countB64 (str | None): count of framed material in quadlets/triplets
for composition as Base64 representation of int.
qb64b (bytes | bytearray | None): fully qualified crypto material text domain
if code nor tag is provided
qb64 (str | None) fully qualified crypto material text domain
Expand Down
3 changes: 2 additions & 1 deletion src/keri/core/eventing.py
Original file line number Diff line number Diff line change
Expand Up @@ -643,7 +643,8 @@ def state(pre,
)
return ksr # return KeyStateRecord use asdict(ksr) to get dict version


# should remove intive as its not standard KERI so confusing and leads to errors
# this is an old feature that is now deprecated.

def incept(keys,
*,
Expand Down
86 changes: 46 additions & 40 deletions src/keri/core/serdering.py
Original file line number Diff line number Diff line change
Expand Up @@ -1199,14 +1199,14 @@ def dumps(self, sad, kind=Serials.json):
return raw


def _dumps(self, sad):
def _dumps(self, sad=None):
"""CESR native serialization of sad
Returns:
raw (bytes): CESR native serialization of sad dict
Parameters:
sad (dict | list)): serializable dict or list to serialize
sad (dict | None)): serializable dict to serialize
Versioning:
CESR native serialization includes in its fixed version field
Expand All @@ -1220,6 +1220,8 @@ def _dumps(self, sad):
tables are backwards compatible across major versions.
"""
sad = sad if sad else self.sad

if (self.gvrsn.major < Vrsn_2_0.major or
self.vrsn.major < Vrsn_2_0.major):
raise SerializeError(f"Invalid major genus version={self.gvrsn}"
Expand All @@ -1234,9 +1236,10 @@ def _dumps(self, sad):
f"genus={self.genus}.")


fixed = True # True = use fixed field, False= use field map

raw = bytearray()

raw = bytearray() # message as qb64
bdy = bytearray() # message body as qb64
ilks = self.Fields[self.proto][self.vrsn] # get fields keyed by ilk

ilk = sad.get('t') # returns None if missing message type (ilk)
Expand All @@ -1249,18 +1252,19 @@ def _dumps(self, sad):

if fields.opts or not fields.strict: # optional or extra fields allowed
fixed = False # so must use field map
else:
fixed = True #fixed field


# assumes that sad's field ordering and field inclusion is correct
# so can serialize in order to compute saidive fields
# need to fix ._verify and .makify to account for CESR native serialization

if self.proto == Protocols.keri:
for l, v in sad.items(): # assumes valid field order & presence
if not fixed: # prepend label
pass
if not fixed: # prepend label
pass # raise error

# should dispatch or use match instead of big if else
for l, v in sad.items(): # assumes valid field order & presence
match l: # label
case "v": # protocol+version do not use version string itself
val = Verser(proto=self.proto, vrsn=self.vrsn).qb64b
Expand All @@ -1283,67 +1287,69 @@ def _dumps(self, sad):
frame.extend(e.encode("utf-8"))

val = bytearray(Counter(tag=AllTags.GenericListGroup,
count=len(frame) % 4).qb64b)
count=len(frame) // 4).qb64b)
val.extend(frame)

case "c": # list of config traits strings
frame = bytearray()
for e in v: # list
frame.extend(Traitor(trait=e).qb64n)
frame.extend(Traitor(trait=e).qb64b)

val = bytearray(Counter(tag=AllTags.GenericListGroup,
count=len(frame) % 4).qb64b)
count=len(frame) // 4).qb64b)
val.extend(frame)

case "a": # list of seals or field map of attributes
frame = bytearray() # whole list
gctr = None # counter for consecutive same type seals
gcode = None # code for counter for consecutive same type seals
gframe = bytearray() # consecutive same type seals
for e in v: # list of seal dicts
# need support for grouping consecutive seals of same type with same counter

try:
sealer = Sealer(crew=e)
frame.extend(sealer.qb64b)
# lookup counter type by sealer.clan.name
except kering.InvalidValueError:
pass
#unknown seal type so serialize as field map
code = self.ClanCodes[sealer.name]
if gcode and gcode == code:
gframe.extend(sealer.qb64b)
else:
if gframe: # not same so close off and rotate group
counter = Counter(code=gcode, count=len(gframe) // 4)
frame.extend(counter.qb64b + gframe)
gframe = bytearray() # new group
gcode = code # new group or keep same group
gframe.extend(sealer.qb64b) # extend in new group

except kering.InvalidValueError:
if gframe:
counter = Counter(code=gcode, count=len(gframe) // 4)
frame.extend(counter.qb64b + gframe)
gframe = bytearray()
gcode = None

#if tuple(v) == eventing.SealEvent._fields:
#eseal = eventing.SealEvent(**v) # convert to namedtuple
#SealSourceCouples: str = '-Q' # Seal Source Couple(s), snu+dig of source sealing or sealed event.
#SealSourceTriples: str = '-R' # Seal Source Triple(s), pre+snu+dig of source sealing or sealed event.
#DigestSealSingles: str = '-V' # Digest Seal Single(s), dig of sealed data.
#MerkleRootSealSingles: str = '-W' # Merkle Tree Root Digest Seal Single(s), dig of sealed data.
#BackerRegistrarSealCouples: str = '-X' # Backer Registrar Seal Couple(s), brid+dig of sealed data.

# SealMark == tuple of seal dict field names tuple(dict)
#d = dict(a=1, b=2)
#tuple(d)
#('a', 'b')

#frame.extend(Sealer(seal=e).qb64b)
# else: generic seal no count type (v, Mapping):
#unknown seal type so serialize as field map
#generic seal no count type (v, Mapping):
#for l, e in v.items():
#pass
#val = bytearray(Counter(tag=AllTags.GenericMapGroup,
# count=len(frame) % 4).qb64b)
# count=len(frame) // 4).qb64b)
#val.extend(mapframe)

if gframe: # close off last group if any
counter = Counter(code=gcode, count=len(gframe) // 4)
frame.extend(counter.qb64b + gframe)
gframe = bytearray()
gcode = None

val = bytearray(Counter(tag=AllTags.GenericListGroup,
count=len(frame) % 4).qb64b)
count=len(frame) // 4).qb64b)
val.extend(frame)


case _: # if extra fields this is where logic would be
raise SerializeError(f"Unsupported protocol field label"
f"='{l}' for protocol={self.proto}"
f" version={self.vrsn}.")


raw.extend(val)
bdy.extend(val)


elif self.protocol == Protocols.acdc:
Expand All @@ -1360,9 +1366,9 @@ def _dumps(self, sad):
# prepend count code for message
if fixed:

val = bytearray(Counter(tag=AllTags.FixedMessageBodyGroup,
count=len(raw) % 4).qb64b)
val.extend(raw)
raw = bytearray(Counter(tag=AllTags.FixedMessageBodyGroup,
count=len(bdy) // 4).qb64b)
raw.extend(bdy)
else:
pass

Expand Down
Loading

0 comments on commit 18cf918

Please sign in to comment.