Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions src/keri/peer/exchanging.py
Original file line number Diff line number Diff line change
Expand Up @@ -150,10 +150,18 @@ def processEvent(self, serder, tsgs=None, cigars=None, **kwargs):
logger.info(f"Behavior for {route} missing or does not have verify for said={serder.said}")
logger.debug(f"event=\n{serder.pretty()}\n")

# A re-parse of an exn we already handled (endpoint retry, escrow release, mailbox
# replay, another group member's copy) must not run the behavior again: notices are
# keyed by a random rid, so handling twice notifies twice.
handled = self.hby.db.exns.get(keys=(serder.said,)) is not None

# Always persist events
self.logEvent(serder, pathed, tsgs, cigars, essrs)
self.cues.append(dict(kin="saved", said=serder.said))

if handled:
return

# Execute any behavior specific handling, not sure if this should be different than verify
try:
behavior.handle(serder=serder, **kwargs)
Expand Down
10 changes: 9 additions & 1 deletion src/keri/vdr/eventing.py
Original file line number Diff line number Diff line change
Expand Up @@ -1579,6 +1579,10 @@ def processEvent(self, serder, seqner=None, saider=None, wigers=None):
if ilk in (Ilks.vcp,):
# we don't have multiple signatures to verify so this
# is already first seen and then lifely duplicitious
dig = self.reger.getTel(snKey(pre=regk, sn=0))
if dig is not None and bytes(dig) == serder.saidb:
return # event is a duplicate but not duplicitous

raise LikelyDuplicitousError("Likely Duplicitous event={}.".format(ked))

tever = self.tevers[regk]
Expand All @@ -1604,7 +1608,11 @@ def processEvent(self, serder, seqner=None, saider=None, wigers=None):
# actually, lets not because the Kevery has no idea what to do with them!
# self.cues.append(dict(kin="receipt", serder=serder))
pass
else: # duplicitious
else: # maybe duplicitous
dig = self.reger.getTel(snKey(pre=pre, sn=sn))
if dig is not None and bytes(dig) == serder.saidb:
return # event is a duplicate but not duplicitous

raise LikelyDuplicitousError("Likely Duplicitous event={} with sn {}.".format(ked, sn))

def processQuery(self, serder, source=None, sigers=None, cigars=None):
Expand Down
42 changes: 10 additions & 32 deletions tests/app/test_agenting.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,9 @@
from hio.base import doing, tyming

from keri import kering, core
from keri.core import coring, serdering
from keri.core.coring import Seqner
from keri.help import nowIso8601
from keri.app import habbing, indirecting, agenting, directing
from keri.db import dbing
from keri.vdr import eventing, viring
from keri.help import nowIso8601


def test_withness_receiptor(seeder):
Expand Down Expand Up @@ -131,12 +128,12 @@ def __init__(self, wanHby, wilHby, wesHby, palHby, seeder):
wilDoers = indirecting.setupWitness(alias="wil", hby=wilHby, tcpPort=5633, httpPort=5643)
wesDoers = indirecting.setupWitness(alias="wes", hby=wesHby, tcpPort=5634, httpPort=5644)

wanHab = wanHby.habByName(name="wan")
wilHab = wilHby.habByName(name="wil")
wesHab = wesHby.habByName(name="wes")
seeder.seedWitEnds(palHby.db, witHabs=[wanHab, wilHab, wesHab], protocols=[kering.Schemes.tcp])
self.wanHab = wanHby.habByName(name="wan")
self.wilHab = wilHby.habByName(name="wil")
self.wesHab = wesHby.habByName(name="wes")
seeder.seedWitEnds(palHby.db, witHabs=[self.wanHab, self.wilHab, self.wesHab], protocols=[kering.Schemes.tcp])

self.palHab = palHby.makeHab(name="pal", wits=[wanHab.pre, wilHab.pre, wesHab.pre], transferable=True)
self.palHab = palHby.makeHab(name="pal", wits=[self.wanHab.pre, self.wilHab.pre, self.wesHab.pre], transferable=True)

self.witDoer = agenting.WitnessPublisher(hby=palHby)
doers = wanDoers + wilDoers + wesDoers + [self.witDoer]
Expand All @@ -151,32 +148,13 @@ def testDo(self, tymth, tock=0.0):
self.tock = tock
yield self.tock

regser = eventing.incept(pre=self.palHab.pre, baks=[], code=coring.MtrDex.Blake3_256)
serder = eventing.issue(vcdig=regser.pre,
regk="EbA1o_bItVC9i6YB3hr2C3I_Gtqvz02vCmavJNoBA3Jg")
msg = bytearray(serder.raw)
msg.extend(core.Counter(core.Codens.SealSourceCouples, count=1,
gvrsn=kering.Vrsn_1_0).qb64b)
msg.extend(Seqner(sn=self.palHab.kever.sn).qb64b)
msg.extend(self.palHab.kever.serder.saidb)

msg = self.palHab.makeOwnEvent(sn=0)
self.witDoer.msgs.append(dict(pre=self.palHab.pre, msg=msg))

while not self.witDoer.cues:
yield self.tock

cue = self.witDoer.cues.popleft()
assert cue["pre"] == self.palHab.pre
assert cue["msg"] == msg

for name in ["wes", "wil", "wan"]:
reger = viring.Reger(name=name)
for hab in [self.wanHab, self.wilHab, self.wesHab]:
while True:
raw = reger.getTvt(dbing.dgKey(serder.preb, serder.saidb))
if raw:
found = serdering.SerderKERI(raw=bytes(raw))
if found and serder.pre == found.pre:
break
if self.palHab.pre in hab.kevers:
break
yield self.tock

self.remove(self.toRemove)
Expand Down
32 changes: 32 additions & 0 deletions tests/peer/test_exchanging.py
Original file line number Diff line number Diff line change
Expand Up @@ -134,6 +134,38 @@ def test_exchanger():
assert recHby.db.epse.get(keys=(fwd.said,)) is None


def test_exchanger_replay_does_not_rehandle():
with habbing.openHby(salt=core.Salter(raw=b'0123456789abcdef').qb64) as hby:
hab = hby.makeHab(name="test")

class CountingHandler:
resource = "/counting"

def __init__(self):
self.count = 0

def verify(self, serder, **kwargs):
return True

def handle(self, serder, **kwargs):
self.count += 1

handler = CountingHandler()
exc = exchanging.Exchanger(hby=hby, handlers=[handler])

msg = hab.exchange(route="/counting", recipient="", payload=dict(m="hello"))
said = serdering.SerderKERI(raw=msg).said

hby.psr.parseOne(ims=bytearray(msg), exc=exc)
assert handler.count == 1
assert hby.db.exns.get(keys=(said,)) is not None

# A re-parse still logs, so signatures from other members merge, but the behavior
# must not run again or the controller is notified twice.
hby.psr.parseOne(ims=bytearray(msg), exc=exc)
assert handler.count == 1


def test_exchange_ps_escrow_timeout():
with habbing.openHab(name="sid", base="test", salt=b'0123456789abcdef') as (hby, hab), \
habbing.openHab(name="rec", base="test", salt=b'0123456789abcdef') as (recHby, recHab):
Expand Down
20 changes: 17 additions & 3 deletions tests/vdr/test_eventing.py
Original file line number Diff line number Diff line change
Expand Up @@ -633,9 +633,9 @@ def test_tevery():
assert tev.prefixer.qb64 == vcp.pre
assert tev.sn == 0

# send vcp again, get error
with pytest.raises(LikelyDuplicitousError):
tvy.processEvent(serder=vcp, seqner=seqner, saider=saider)
# send vcp again, duplicate but not duplicitous
tvy.processEvent(serder=vcp, seqner=seqner, saider=saider)
assert tev.sn == 0

# process issue vc event
vcdig = b'EEBp64Aw2rsjdJpAR0e2qCq3jX7q7gLld3LjAwZgaLXU'
Expand Down Expand Up @@ -671,6 +671,20 @@ def test_tevery():
assert status.et == Ilks.rev
assert status.s == '1'

# send rev again, state unchanged
tvy.processEvent(serder=rev, seqner=seqner, saider=saider)
status = tev.vcState(vcdig.decode("utf-8"))
assert status.et == Ilks.rev
assert status.s == '1'
assert status.d == rev.said

# a different rev at the same sn is still duplicitous
other = eventing.revoke(vcdig=vcdig.decode("utf-8"), regk=regk, dig=iss.said,
dt="2021-01-01T00:00:00.000000+00:00")
assert other.said != rev.said
with pytest.raises(LikelyDuplicitousError):
tvy.processEvent(serder=other, seqner=seqner, saider=saider)


def test_tevery_process_escrow(mockCoringRandomNonce):
with basing.openDB() as db, keeping.openKS() as kpr, viring.openReger() as reg:
Expand Down
Loading