Skip to content

Commit

Permalink
finished refactor of ksn to use dataclass KeyStateRecord
Browse files Browse the repository at this point in the history
  • Loading branch information
SmithSamuelM committed Jun 18, 2023
1 parent 8c3b003 commit ade36ab
Show file tree
Hide file tree
Showing 9 changed files with 175 additions and 152 deletions.
10 changes: 5 additions & 5 deletions src/keri/app/cli/commands/local/watch.py
Original file line number Diff line number Diff line change
Expand Up @@ -219,11 +219,11 @@ def diffState(wit, preksn, witksn):

witstate = WitnessState()
witstate.wit = wit
mysn = preksn.sner.num
mydig = preksn.ked['d']
witstate.sn = coring.Number(num=witksn.ked["f"]).num
witstate.dig = witksn.ked['d']
mysn = int(preksn.s, 16)
mydig = preksn.d
witstate.sn = int(witksn.f, 16)
witstate.dig = witksn.d

# At the same sequence number, check the DIGs
if mysn == witstate.sn:
if mydig == witstate.dig:
Expand Down
2 changes: 1 addition & 1 deletion src/keri/app/kiwiing.py
Original file line number Diff line number Diff line change
Expand Up @@ -768,7 +768,7 @@ def on_get(self, _, rep, prefix):

res = dict(
pre=pre,
state=kever.state().ked
state=kever.state()._asdict()
)

kel = []
Expand Down
12 changes: 6 additions & 6 deletions src/keri/app/querying.py
Original file line number Diff line number Diff line change
Expand Up @@ -43,10 +43,10 @@ def recur(self, tyme, deeds=None):
match cue['kin']:
case "keyStateSaved":
kcue = cue
ksn = kcue['serder']
match ksn.pre:
ksn = kcue['serder'] # key state notice dict
match ksn["i"]:
case self.pre:
if kever.sn < ksn.sn:
if kever.sn < int(ksn["s"], 16):
# Add new doer here instead of cueing to a while loop
self.extend([LogQuerier(hby=self.hby, hab=self.hab, ksn=ksn)])
self.remove([self.witq])
Expand All @@ -70,7 +70,7 @@ def __init__(self, hby, hab, ksn, **opts):
self.hab = hab
self.ksn = ksn
self.witq = agenting.WitnessInquisitor(hby=self.hby)
self.witq.query(src=self.hab.pre, pre=self.ksn.pre)
self.witq.query(src=self.hab.pre, pre=self.ksn["i"])
super(LogQuerier, self).__init__(doers=[self.witq], **opts)

def recur(self, tyme, deeds=None):
Expand All @@ -79,8 +79,8 @@ def recur(self, tyme, deeds=None):
Usage:
add result of doify on this method to doers list
"""
kever = self.hab.kevers[self.ksn.pre]
if kever.sn >= self.ksn.sn:
kever = self.hab.kevers[self.ksn["i"]]
if kever.sn >= int(self.ksn['s'], 16):
self.remove([self.witq])
return True

Expand Down
91 changes: 51 additions & 40 deletions src/keri/core/eventing.py
Original file line number Diff line number Diff line change
Expand Up @@ -2046,9 +2046,7 @@ def update(self, serder, sigers, wigers=None, delseqner=None, delsaider=None,
if fn is not None: # first is non-idempotent for fn check mode fn is None
self.fner = Number(num=fn)
self.dater = Dater(dts=dts)
self.db.states.pin(keys=self.prefixer.qb64,
val=helping.datify(basing.KeyStateRecord,
self.state().ked))
self.db.states.pin(keys=self.prefixer.qb64, val=self.state())


elif ilk == Ilks.ixn: # subsequent interaction event
Expand Down Expand Up @@ -2095,9 +2093,7 @@ def update(self, serder, sigers, wigers=None, delseqner=None, delsaider=None,
if fn is not None: # first is non-idempotent for fn check mode fn is None
self.fner = Number(num=fn)
self.dater = Dater(dts=dts)
self.db.states.pin(keys=self.prefixer.qb64,
val=helping.datify(basing.KeyStateRecord,
self.state().ked))
self.db.states.pin(keys=self.prefixer.qb64, val=self.state())

else: # unsupported event ilk so discard
raise ValidationError("Unsupported ilk = {} for evt = {}.".format(ilk, ked))
Expand Down Expand Up @@ -3482,6 +3478,7 @@ def removeStaleReplyEndRole(self, saider):
"""
pass


def removeStaleReplyLocScheme(self, saider):
"""
Process reply escrow at saider for route "/loc/scheme"
Expand All @@ -3500,6 +3497,7 @@ def registerReplyRoutes(self, router):
router.addRoute("/loc/scheme", self, suffix="LocScheme")
router.addRoute("/ksn/{aid}", self, suffix="KeyStateNotice")


def processReplyEndRole(self, *, serder, saider, route,
cigars=None, tsgs=None, **kwargs):
"""
Expand Down Expand Up @@ -3688,6 +3686,7 @@ def processReplyLocScheme(self, *, serder, saider, route,

self.updateLoc(keys=keys, saider=saider, url=url) # update .lans and .locs


def processReplyKeyStateNotice(self, *, serder, saider, route,
cigars=None, tsgs=None, **kwargs):
""" Process one reply message for key state = /ksn
Expand Down Expand Up @@ -3760,41 +3759,43 @@ def processReplyKeyStateNotice(self, *, serder, saider, route,
f"msg={serder.ked}.")
aid = kwargs["aid"]
data = serder.ked["a"]
kserder = coring.Serder(ked=data)
try:
ksr = KeyStateRecord._fromdict(d=data)
except Exception as ex:
raise ValidationError(f"Malformed key state notice = {data}.") from ex

for k in KSN_LABELS:
if k not in kserder.ked:
raise ValidationError("Missing element = {} from {} msg."
" ksn = {}.".format(k, Ilks.ksn,
serder.pretty()))
#for k in KSN_LABELS:
#if k not in ksr.ked:
#raise ValidationError("Missing element = {} from {} msg."
#" ksn = {}.".format(k, Ilks.ksn,
#serder.pretty()))
# fetch from serder to process
ked = kserder.ked
pre = kserder.pre
sn = kserder.sn
pre = ksr.i
sn = int(ksr.s, 16)

# check source and ensure we should accept it
baks = ked["b"]
baks = ksr.b
wats = set()
for _, habr in self.db.habs.getItemIter():
wats |= set(habr.watchers)

# not in promiscuous mode
if not self.lax:
if aid != kserder.pre and \
if aid != ksr.i and \
aid not in baks and \
aid not in wats:
raise kering.UntrustedKeyStateSource("key state notice for {} from untrusted source {} "
.format(kserder.pre, aid))
.format(ksr.pre, aid))

if kserder.pre in self.kevers:
kever = self.kevers[kserder.pre]
if kserder.sn < kever.sner.num:
if ksr.i in self.kevers:
kever = self.kevers[ksr.i]
if int(ksr.s, 16) < kever.sner.num:
raise ValidationError("Skipped stale key state at sn {} for {}."
"".format(kserder.sn, kserder.pre))
"".format(int(ksr.s, 16), ksr.i))

keys = (pre, aid,)
osaider = self.db.knas.get(keys=keys) # get old said if any
dater = coring.Dater(dts=serder.ked["dt"])
dater = coring.Dater(dts=ksr.dt)

# BADA Logic
accepted = self.rvy.acceptReply(serder=serder, saider=saider, route=route,
Expand All @@ -3804,7 +3805,7 @@ def processReplyKeyStateNotice(self, *, serder, saider, route,
raise UnverifiedReplyError(f"Unverified reply.")

ldig = self.db.getKeLast(key=snKey(pre=pre, sn=sn)) # retrieve dig of last event at sn.
diger = coring.Diger(qb64=ked["d"])
diger = coring.Diger(qb64=ksr.d)

# Only accept key state if for last seen version of event at sn
if ldig is not None: # escrow because event does not yet exist in database
Expand All @@ -3815,12 +3816,12 @@ def processReplyKeyStateNotice(self, *, serder, saider, route,
sserder = Serder(raw=bytes(sraw))

if not sserder.compare(said=diger.qb64b): # mismatch events problem with replay
raise ValidationError("Mismatch keystate at sn = {} with db."
"".format(ked["s"]))
raise ValidationError(f"Mismatch keystate at sn = {int(ksr.s,16)}"
f" with db.")

ksaider = coring.Saider(qb64=diger.qb64)
self.updateKeyState(aid=aid, serder=kserder, saider=ksaider, dater=dater)
self.cues.append(dict(kin="keyStateSaved", serder=kserder))
self.updateKeyState(aid=aid, ksr=ksr, saider=ksaider, dater=dater)
self.cues.append(dict(kin="keyStateSaved", serder=serder))


def updateEnd(self, keys, saider, allowed=None):
Expand Down Expand Up @@ -3858,14 +3859,14 @@ def updateLoc(self, keys, saider, url):

self.db.locs.pin(keys=keys, val=locer) # overwrite

def escrowKeyStateNotice(self, *, pre, aid, serder, saider, dater, cigars=None, tsgs=None):
def escrowKeyStateNotice(self, *, pre, aid, ksr, saider, dater, cigars=None, tsgs=None):
"""
Escrow reply by route
Parameters:
pre (str): identifier of key state
aid (str): identifier of authorizer of key state
serder (Serder): instance of reply msg (SAD)
ksr (KeyStateRecord): instance holds key state notice
saider (Saider): instance from said in serder (SAD)
dater (Dater): instance from date-time in serder (SAD)
cigars (list): of Cigar instances that contain nontrans signing couple
Expand All @@ -3879,7 +3880,7 @@ def escrowKeyStateNotice(self, *, pre, aid, serder, saider, dater, cigars=None,
"""
keys = (saider.qb64,)
self.db.kdts.put(keys=keys, val=dater) # first one idempotent
self.db.ksns.put(keys=keys, val=serder) # first one idempotent
self.db.ksns.put(keys=keys, val=ksr) # first one idempotent

for prefixer, seqner, diger, sigers in tsgs: # iterate over each tsg
quadkeys = (saider.qb64, prefixer.qb64, f"{seqner.sn:032x}", diger.qb64)
Expand All @@ -3889,25 +3890,27 @@ def escrowKeyStateNotice(self, *, pre, aid, serder, saider, dater, cigars=None,

return self.db.knes.put(keys=(pre, aid), vals=[saider]) # overwrite

def updateKeyState(self, aid, serder, saider, dater):

def updateKeyState(self, aid, ksr, saider, dater):
"""
Update Reply SAD in database given by by serder and associated databases
for attached cig couple or sig quadruple.
Overwrites val at key if already exists.
Parameters:
aid (str): identifier of key state
serder (Serder): instance of reply msg (SAD)
ksr (KeyStateRecord): converted from key state notice dict in reply msg
saider (Saider): instance from said in serder (SAD)
dater (Dater): instance from date-time in serder (SAD)
"""
keys = (saider.qb64,)

# Add source of ksn to the key for DATEs too... (source AID, ksn AID)
self.db.kdts.put(keys=keys, val=dater) # first one idempotent
self.db.ksns.pin(keys=keys, val=serder) # first one idempotent
# Add source of ksn to the key... (source AID, ksn AID)
self.db.knas.pin(keys=(serder.pre, aid), val=saider) # overwrite
self.db.ksns.pin(keys=keys, val=ksr) # first one idempotent
# Add source of ksr to the key... (ksr AID, source aid)
self.db.knas.pin(keys=(ksr.i, aid), val=saider) # overwrite


def removeKeyState(self, saider):
if saider:
Expand All @@ -3918,9 +3921,10 @@ def removeKeyState(self, saider):
self.db.ksns.rem(keys=keys)
self.db.kdts.rem(keys=keys)


def processEscrowKeyState(self):
"""
Process escrows for reply messages. Escrows are keyed by reply pre
Process escrows for key state reply messages. Escrows are keyed by reply pre
and val is reply said
triple (prefixer, seqner, diger)
Expand All @@ -3933,6 +3937,8 @@ def processEscrowKeyState(self):

keys = (saider.qb64,)
dater = self.db.kdts.get(keys=keys)
# following is wrong need the actual serder of the reply message not
# the embedded key state notice or key state record
serder = self.db.ksns.get(keys=keys)
vcigars = self.db.kcgs.get(keys=keys)

Expand All @@ -3956,7 +3962,10 @@ def processEscrowKeyState(self):

raise ValidationError(f"Stale key state escrow at pre = {pre}.")

self.processReplyKeyStateNotice(serder=serder, saider=saider, route=serder.ked["r"], cigars=cigars,
self.processReplyKeyStateNotice(serder=serder,
saider=saider,
route=serder.ked["r"],
cigars=cigars,
tsgs=tsgs, aid=aid)

except kering.OutOfOrderKeyStateError as ex:
Expand Down Expand Up @@ -3987,6 +3996,7 @@ def processEscrowKeyState(self):
else:
logger.error("Kevery unescrowed due to error: %s\n", ex.args[0])


def processQuery(self, serder, source=None, sigers=None, cigars=None):
"""
Process query mode replay message for collective or single element query.
Expand Down Expand Up @@ -4059,8 +4069,9 @@ def processQuery(self, serder, source=None, sigers=None, cigars=None):
self.escrowQueryNotFoundEvent(serder=serder, prefixer=source, sigers=sigers, cigars=cigars)
raise QueryNotFoundError("Query not found error={}.".format(ked))

ksn = reply(route=f"/ksn/{src}", data=kever.state().ked)
self.cues.push(dict(kin="reply", src=src, route="/ksn", serder=ksn, dest=source.qb64))
rserder = reply(route=f"/ksn/{src}", data=kever.state()._asdict())
self.cues.push(dict(kin="reply", src=src, route="/ksn", serder=rserder,
dest=source.qb64))

elif route == "mbx":
pre = qry["i"]
Expand Down
24 changes: 16 additions & 8 deletions src/keri/db/basing.py
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,13 @@ class RawRecord:
is to transform dataclass into dict or serialization of its transformation
into dict so that it can be included in messages or stored in a database.
"""

@classmethod
def _fromdict(cls, d: dict):
"""returns instance of clas initialized from dict d """
return helping.datify(cls, d)


def __iter__(self):
return iter(asdict(self))

Expand Down Expand Up @@ -167,8 +174,6 @@ class KeyStateRecord(RawRecord): # baser.state





@dataclass
class HabitatRecord: # baser.habs
"""
Expand Down Expand Up @@ -900,9 +905,12 @@ def reopen(self, **kwa):
self.kdts = subing.CesrSuber(db=self, subkey='kdts.', klas=coring.Dater)

# all key state messages. Maps key state said to serialization. ksns are
# versioned sads ( with version string) so use Serder to deserialize and
# KeyStateRecords so use ._asdict or ._asjson as appropriate
# use .kdts, .ksgs, and .kcgs for datetimes and signatures
self.ksns = subing.SerderSuber(db=self, subkey='ksns.')
self.ksns = koming.Komer(db=self,
schema=KeyStateRecord,
subkey='ksns.')
#self.ksns = subing.SerderSuber(db=self, subkey='ksns.')

# all key state ksgs (ksn indexed signature serializations) maps ksn quadkeys
# given by quadruple (saider.qb64, prefixer.qb64, seqner.q64, diger.qb64)
Expand Down Expand Up @@ -1019,9 +1027,9 @@ def reload(self):
"""
removes = []
for keys, data in self.habs.getItemIter():
if (ked := self.states.getDict(keys=data.hid)) is not None:
if (ksr := self.states.get(keys=data.hid)) is not None:
try:
kever = eventing.Kever(state=coring.Serder(ked=ked),
kever = eventing.Kever(state=ksr,
db=self,
prefixes=self.prefixes,
local=True)
Expand All @@ -1039,9 +1047,9 @@ def reload(self):
# Load namespaced Habs
removes = []
for keys, data in self.nmsp.getItemIter():
if (ked := self.states.getDict(keys=data.hid)) is not None:
if (ksr := self.states.get(keys=data.hid)) is not None:
try:
kever = eventing.Kever(state=coring.Serder(ked=ked),
kever = eventing.Kever(state=ksr,
db=self,
prefixes=self.prefixes,
local=True)
Expand Down
8 changes: 4 additions & 4 deletions tests/app/test_querying.py
Original file line number Diff line number Diff line change
Expand Up @@ -44,8 +44,8 @@ def test_querying():

# Cue up a saved key state equal to the one we have
hby.kvy.cues.clear()
ksn = subHab.kever.state()
cue = dict(kin="keyStateSaved", serder=ksn)
ksr = subHab.kever.state()
cue = dict(kin="keyStateSaved", serder=ksr._asdict())
hby.kvy.cues.append(cue)

doist.recur(deeds=deeds)
Expand All @@ -62,8 +62,8 @@ def test_querying():

# rotate AID and submit as a new keyStateSave
rot = subHab.rotate()
ksn = subHab.kever.state()
cue = dict(kin="keyStateSaved", serder=ksn)
ksr = subHab.kever.state()
cue = dict(kin="keyStateSaved", serder=ksr._asdict())
hby.kvy.cues.append(cue)
deeds = doist.enter(doers=[qdoer])
doist.recur(deeds=deeds)
Expand Down
Loading

0 comments on commit ade36ab

Please sign in to comment.