Skip to content

Commit

Permalink
Add processing method for delegables escrow in Kevers. (WebOfTrust#786)
Browse files Browse the repository at this point in the history
Signed-off-by: pfeairheller <[email protected]>
  • Loading branch information
pfeairheller authored May 21, 2024
1 parent 80e547c commit feff666
Show file tree
Hide file tree
Showing 3 changed files with 171 additions and 2 deletions.
4 changes: 2 additions & 2 deletions src/keri/app/cli/commands/delegate/confirm.py
Original file line number Diff line number Diff line change
Expand Up @@ -130,7 +130,7 @@ def confirmDo(self, tymth, tock=0.0):

if isinstance(hab, GroupHab):
aids = hab.smids
#seqner = coring.Seqner(sn=eserder.sn)

anchor = dict(i=eserder.ked["i"], s=eserder.snh, d=eserder.said)
if self.interact:
msg = hab.interact(data=[anchor])
Expand Down Expand Up @@ -166,7 +166,7 @@ def confirmDo(self, tymth, tock=0.0):

else:
cur = hab.kever.sner.num
#seqner = coring.Seqner(sn=eserder.sn)

anchor = dict(i=eserder.ked["i"], s=eserder.snh, d=eserder.said)
if self.interact:
hab.interact(data=[anchor])
Expand Down
118 changes: 118 additions & 0 deletions src/keri/core/eventing.py
Original file line number Diff line number Diff line change
Expand Up @@ -5714,6 +5714,124 @@ def processEscrowUnverNonTrans(self):
break
key = ekey # setup next while iteration, with key after ekey

def processEscrowDelegables(self):
"""
Process events escrowed by Kever that require delegation.
Escrowed items are indexed in database table keyed by prefix and
sn with duplicates given by different dig inserted in insertion order.
This allows FIFO processing of events with same prefix and sn but different
digest.
Uses .db.delegables.add(key, val) which is IOVal with dups.
Value is dgkey for event stored in .Evt where .Evt has serder.raw of event.
Steps:
Each pass (walk index table)
For each prefix,sn
For each escrow item dup at prefix,sn:
Get Event
Get and Attach Signatures
Process event as if it came in over the wire
If successful then remove from escrow table
"""

for (pre, sn), dig in self.db.delegables.getItemIter():
try:
edig = dig.encode("utf-8")
dgkey = dgKey(pre.encode("utf-8"), edig)
if not (esr := self.db.esrs.get(keys=dgkey)): # get event source, otherwise error
# no local sourde so raise ValidationError which unescrows below
raise ValidationError("Missing escrowed event source "
"at dig = {}.".format(bytes(edig)))

# check date if expired then remove escrow.
dtb = self.db.getDts(dgkey)
if dtb is None: # othewise is a datetime as bytes
# no date time so raise ValidationError which unescrows below
logger.info("Kevery unescrow error: Missing event datetime"
" at dig = %s", bytes(edig))

raise ValidationError("Missing escrowed event datetime "
"at dig = {}.".format(bytes(edig)))

# do date math here and discard if stale nowIso8601() bytes
dtnow = helping.nowUTC()
dte = helping.fromIso8601(bytes(dtb))
if (dtnow - dte) > datetime.timedelta(seconds=self.TimeoutOOE):
# escrow stale so raise ValidationError which unescrows below
logger.info("Kevery unescrow error: Stale event escrow "
" at dig = %s", bytes(edig))

raise ValidationError("Stale event escrow "
"at dig = {}.".format(bytes(edig)))

# get the escrowed event using edig
eraw = self.db.getEvt(dgKey(pre, bytes(edig)))
if eraw is None:
# no event so raise ValidationError which unescrows below
logger.info("Kevery unescrow error: Missing event at."
"dig = %s", bytes(edig))

raise ValidationError("Missing escrowed evt at dig = {}."
"".format(bytes(edig)))

eserder = serdering.SerderKERI(raw=bytes(eraw)) # escrowed event

# get sigs and attach
sigs = self.db.getSigs(dgKey(pre, bytes(edig)))
if not sigs: # otherwise its a list of sigs
# no sigs so raise ValidationError which unescrows below
logger.info("Kevery unescrow error: Missing event sigs at."
"dig = %s", bytes(edig))

raise ValidationError("Missing escrowed evt sigs at "
"dig = {}.".format(bytes(edig)))

sigers = [Siger(qb64b=bytes(sig)) for sig in sigs]

# get wigs
wigs = self.db.getWigs(dgKey(pre, bytes(edig))) # list of wigs
wigers = [Siger(qb64b=bytes(wig)) for wig in wigs]

# get delgate seal
couple = self.db.getAes(dgkey)
if couple is not None: # Only try to parse the event if we have the del seal
raw = bytearray(couple)
seqner = coring.Seqner(qb64b=raw, strip=True)
saider = coring.Saider(qb64b=raw)

# process event
self.processEvent(serder=eserder, sigers=sigers, wigers=wigers, delseqner=seqner,
delsaider=saider, local=esr.local)
else:
raise MissingDelegableApprovalError()

except MissingDelegableApprovalError as ex:
# still waiting on missing delegation approval
if logger.isEnabledFor(logging.DEBUG):
logger.exception("Kevery unescrow failed: %s", ex.args[0])
else:
logger.error("Kevery unescrow failed: %s", ex.args[0])

except Exception as ex: # log diagnostics errors etc
# error other than out of order so remove from OO escrow
self.db.delegables.rem(keys=(pre, sn,), val=edig) # removes one escrow at key val
if logger.isEnabledFor(logging.DEBUG):
logger.exception("Kevery unescrowed: %s", ex.args[0])
else:
logger.error("Kevery unescrowed: %s", ex.args[0])

else: # unescrow succeeded, remove from escrow
# We don't remove all escrows at pre,sn because some might be
# duplicitous so we process remaining escrows in spite of found
# valid event escrow.
self.db.delegables.rem(keys=(pre, sn,), val=edig) # removes one escrow at key val
logger.info("Kevery unescrow succeeded in valid event: "
"event=%s", eserder.said)
logger.debug(f"event=\n{eserder.pretty()}\n")

def processQueryNotFound(self):
"""
Process qry events escrowed by Kevery for KELs that have not yet met the criteria of the query.
Expand Down
51 changes: 51 additions & 0 deletions tests/core/test_delegating.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
from keri.app import keeping, habbing

from keri.db import dbing, basing
from keri.db.dbing import snKey

logger = help.ogler.getLogger()

Expand Down Expand Up @@ -723,6 +724,56 @@ def test_load_event(mockHelpingNowUTC):
"""End Test"""


def test_delegables_escrow():
gateSalt = core.Salter(raw=b'0123456789abcdef').qb64
torSalt = core.Salter(raw=b'0123456789defabc').raw

with habbing.openHby(name="delegate", temp=True, salt=gateSalt) as gateHby, \
habbing.openHab(name="delegator", temp=True, salt=torSalt) as (torHby, torHab):

gateHab = gateHby.makeHab(name="repTest", transferable=True, delpre=torHab.pre)
assert gateHab.pre == "EFqw1EgGdd2B6MgNLJaNO13_JoQpxAtasIjySDzGm9pd"

gateIcp = gateHab.makeOwnEvent(sn=0)
torKvy = eventing.Kevery(db=torHab.db, lax=False, local=False)
parsing.Parser().parse(ims=bytearray(gateIcp), kvy=torKvy, local=True)
assert gateHab.pre not in torKvy.kevers
assert len(torHab.db.delegables.get(keys=snKey(gateHab.kever.serder.preb, gateHab.kever.serder.sn))) == 1

# Now create delegating interaction event
seal = eventing.SealEvent(i=gateHab.pre,
s="0",
d=gateHab.pre)
ixn = torHab.interact(data=[seal._asdict()])
assert ixn == (b'{"v":"KERI10JSON00013a_","t":"ixn","d":"EPUCIjCibL-VeT3n6PYIkbyP'
b'qpioIFT79NRqxboFv0Os","i":"EJTtW40aDl0aKDZ09v-o6uDz_VwLJGplp6WTI'
b'BGCoVog","s":"1","p":"EJTtW40aDl0aKDZ09v-o6uDz_VwLJGplp6WTIBGCoV'
b'og","a":[{"i":"EFqw1EgGdd2B6MgNLJaNO13_JoQpxAtasIjySDzGm9pd","s"'
b':"0","d":"EFqw1EgGdd2B6MgNLJaNO13_JoQpxAtasIjySDzGm9pd"}]}-AABAA'
b'BRR9HDRx_7KdWJ7uokLzREP3c1Hg7Grq5fwoGl_EXA-reR05aYPjDdZ4CIZTnqDo'
b'EN2hqNbHfq4zMaDlR8Ja4D')

# Make sure that our anchoring ixn event is in our own KEL
assert torHab.kever.sn == 1

# Place the anchor seal in the database... this will be retrieved from the fully committed delegate event
serder = torHab.kever.serder
seqner = coring.Seqner(sn=serder.sn)
couple = seqner.qb64b + serder.saidb
dgkey = dbing.dgKey(gateHab.kever.prefixer.qb64b, gateHab.kever.serder.saidb)
torHab.db.setAes(dgkey, couple) # authorizer event seal (delegator/issuer)

# delegate still not kevers
assert gateHab.pre not in torKvy.kevers
assert len(torHab.db.delegables.get(keys=snKey(gateHab.kever.serder.preb, gateHab.kever.serder.sn))) == 1

# run the delegables escrow processor to make get delegate in our Kevers
torKvy.processEscrowDelegables()
assert len(torHab.db.delegables.get(keys=snKey(gateHab.kever.serder.preb, gateHab.kever.serder.sn))) == 0
assert gateHab.pre in torKvy.kevers


if __name__ == "__main__":
test_delegation()
test_delegation_supersede()

0 comments on commit feff666

Please sign in to comment.