Exemplo n.º 1
0
def test_witness():
    """
    Test event validation logic with witnesses

    cam is controller
    van is validator
    wes is a witness
    wok is a witness
    wam is a witness

    """
    salt = coring.Salter(raw=b'abcdef0123456789').qb64

    with dbing.openDB(name="cam") as camDB, keeping.openKS(name="cam") as camKS, \
         dbing.openDB(name="van") as vanDB, keeping.openKS(name="van") as vanKS, \
         dbing.openDB(name="wes") as wesDB, keeping.openKS(name="wes") as wesKS, \
         dbing.openDB(name="wok") as wokDB, keeping.openKS(name="wok") as wokKS, \
         dbing.openDB(name="wam") as wamDB, keeping.openKS(name="wam") as wamKS, \
         dbing.openDB(name="wil") as wilDB, keeping.openKS(name="wil") as wilKS:

        # witnesses first so can setup inception event for cam
        wsith = 1
        # setup Wes's habitat nontrans
        # Wes's receipts will be rcts with a receipt couple attached

        wesHab = directing.Habitat(name='wes',
                                   ks=wesKS,
                                   db=wesDB,
                                   isith=wsith,
                                   icount=1,
                                   salt=salt,
                                   transferable=False,
                                   temp=True)  # stem is .name
        assert wesHab.ks == wesKS
        assert wesHab.db == wesDB
        assert not wesHab.kever.prefixer.transferable
        # create non-local kevery for Wes to process nonlocal msgs
        wesKvy = eventing.Kevery(kevers=wesHab.kevers,
                                 db=wesHab.db,
                                 framed=True,
                                 opre=wesHab.pre,
                                 local=False)

        # setup Wok's habitat nontrans
        # Wok's receipts will be rcts with a receipt couple attached
        wokHab = directing.Habitat(name='wok',
                                   ks=wokKS,
                                   db=wokDB,
                                   isith=wsith,
                                   icount=1,
                                   salt=salt,
                                   transferable=False,
                                   temp=True)  # stem is .name
        assert wokHab.ks == wokKS
        assert wokHab.db == wokDB
        assert not wokHab.kever.prefixer.transferable
        # create non-local kevery for Wok to process nonlocal msgs
        wokKvy = eventing.Kevery(kevers=wokHab.kevers,
                                 db=wokHab.db,
                                 framed=True,
                                 opre=wokHab.pre,
                                 local=False)

        # setup Wam's habitat nontrans
        # Wams's receipts will be rcts with a receipt couple attached
        wamHab = directing.Habitat(name='wam',
                                   ks=wamKS,
                                   db=wamDB,
                                   isith=wsith,
                                   icount=1,
                                   salt=salt,
                                   transferable=False,
                                   temp=True)  # stem is .name
        assert wamHab.ks == wamKS
        assert wamHab.db == wamDB
        assert not wamHab.kever.prefixer.transferable
        # create non-local kevery for Wam to process nonlocal msgs
        wamKvy = eventing.Kevery(kevers=wamHab.kevers,
                                 db=wamHab.db,
                                 framed=True,
                                 opre=wamHab.pre,
                                 local=False)

        # setup Wil's habitat nontrans
        # Wil's receipts will be rcts with a receipt couple attached
        wilHab = directing.Habitat(name='wil',
                                   ks=wilKS,
                                   db=wilDB,
                                   isith=wsith,
                                   icount=1,
                                   salt=salt,
                                   transferable=False,
                                   temp=True)  # stem is .name
        assert wilHab.ks == wilKS
        assert wilHab.db == wilDB
        assert not wilHab.kever.prefixer.transferable
        # create non-local kevery for Wam to process nonlocal msgs
        wilKvy = eventing.Kevery(kevers=wilHab.kevers,
                                 db=wilHab.db,
                                 framed=True,
                                 opre=wilHab.pre,
                                 local=False)

        # setup Cam's habitat trans multisig
        wits = [wesHab.pre, wokHab.pre, wamHab.pre]
        csith = 2  # hex str of threshold int
        camHab = directing.Habitat(name='cam',
                                   ks=camKS,
                                   db=camDB,
                                   isith=csith,
                                   icount=3,
                                   toad=2,
                                   wits=wits,
                                   salt=salt,
                                   temp=True)  # stem is .name
        assert camHab.ks == camKS
        assert camHab.db == camDB
        assert camHab.kever.prefixer.transferable
        for werfer in camHab.iserder.werfers:
            assert werfer.qb64 in wits
        assert camHab.kever.wits == wits
        assert camHab.kever.toad == 2
        assert camHab.kever.sn == 0

        # create non-local kevery for Cam to process onlocal msgs
        camKvy = eventing.Kevery(kevers=camHab.kevers,
                                 db=camHab.db,
                                 framed=True,
                                 opre=camHab.pre,
                                 local=False)

        # setup Van's habitat trans multisig
        vsith = 2  # two of three signing threshold
        vanHab = directing.Habitat(name='van',
                                   ks=vanKS,
                                   db=vanDB,
                                   isith=vsith,
                                   icount=3,
                                   salt=salt,
                                   temp=True)  # stem is .name
        assert vanHab.ks == vanKS
        assert vanHab.db == vanDB
        assert vanHab.kever.prefixer.transferable
        # create non-local kevery for Van to process nonlocal msgs
        vanKvy = eventing.Kevery(kevers=vanHab.kevers,
                                 db=vanHab.db,
                                 framed=True,
                                 opre=vanHab.pre,
                                 local=False)

        # make list so easier to batch
        camWitKvys = [wesKvy, wokKvy, wamKvy]
        camWitHabs = [wesHab, wokHab, wamHab]

        # Create Cam inception and send to each of Cam's witnesses
        camIcpMsg = camHab.makeOwnInception()
        rctMsgs = []  # list of receipts from each witness
        for i in range(len(camWitKvys)):
            kvy = camWitKvys[i]
            kvy.process(ims=bytearray(
                camIcpMsg))  # send copy of cam icp msg to witness
            assert kvy.kevers[camHab.pre].sn == 0  # accepted event
            assert len(kvy.cues) == 1  # queued receipt cue
            hab = camWitHabs[i]
            rctMsg = hab.processCues(kvy.cues)  # process cue returns rct msg
            assert len(rctMsg) == 559
            rctMsgs.append(rctMsg)

        for msg in rctMsgs:  # process rct msgs from all witnesses
            camKvy.process(ims=bytearray(msg))  # make copy
        for hab in camWitHabs:
            assert hab.pre in camKvy.kevers

        # get from Cam database copies of witness receipts received by Cam
        # and send to witnesses so all witnesses have full set of receipts
        # from all other witnesses
        # reply one event or receipt one event with all witness attachments
        dgkey = dbing.dgKey(pre=camHab.pre, dig=camHab.kever.serder.dig)
        wigs = camHab.db.getWigs(dgkey)
        assert len(wigs) == 3
        wigers = [coring.Siger(qb64b=bytes(wig)) for wig in wigs]
        rserder = eventing.receipt(pre=camHab.pre,
                                   sn=camHab.kever.sn,
                                   dig=camHab.kever.serder.dig)
        camIcpWitRctMsg = eventing.messagize(serder=rserder, wigers=wigers)
        assert len(camIcpWitRctMsg) == 413
        for i in range(len(camWitKvys)):
            kvy = camWitKvys[i]
            kvy.process(
                ims=bytearray(camIcpWitRctMsg))  # send copy of witness rcts
            assert len(kvy.db.getWigs(dgkey)) == 3  # fully witnessed
            assert len(kvy.cues) == 0  # no cues

        # send Cam icp and witness rcts to Van
        vanKvy.process(
            ims=bytearray(camIcpMsg))  # should escrow since not witnesses
        assert camHab.pre not in vanKvy.kevers
        vanKvy.process(ims=bytearray(camIcpWitRctMsg))
        vanKvy.processEscrows()
        assert camHab.pre in vanKvy.kevers
        vcKvr = vanKvy.kevers[camHab.pre]
        assert vcKvr.sn == 0
        assert vcKvr.wits == wits

        # Create Cam ixn and send to each of Cam's witnesses
        camIxnMsg = camHab.interact()
        rctMsgs = []  # list of receipts from each witness
        for i in range(len(camWitKvys)):
            kvy = camWitKvys[i]
            kvy.process(ims=bytearray(
                camIxnMsg))  # send copy of cam icp msg to witness
            assert kvy.kevers[camHab.pre].sn == 1  # accepted event
            assert len(kvy.cues) == 1  # queued receipt cue
            hab = camWitHabs[i]
            rctMsg = hab.processCues(kvy.cues)  # process cue returns rct msg
            assert len(rctMsg) == 281
            rctMsgs.append(rctMsg)

        for msg in rctMsgs:  # process rct msgs from all witnesses
            camKvy.process(ims=bytearray(msg))  # make copy
        for hab in camWitHabs:
            assert hab.pre in camKvy.kevers

        # get from Cam database copies of witness receipts received by Cam
        # and send to witnesses so all witnesses have full set of receipts
        # from all other witnesses
        # reply one event or receipt one event with all witness attachments
        dgkey = dbing.dgKey(pre=camHab.pre, dig=camHab.kever.serder.dig)
        wigs = camHab.db.getWigs(dgkey)
        assert len(wigs) == 3
        wigers = [coring.Siger(qb64b=bytes(wig)) for wig in wigs]
        rserder = eventing.receipt(pre=camHab.pre,
                                   sn=camHab.kever.sn,
                                   dig=camHab.kever.serder.dig)
        camIxnWitRctMsg = eventing.messagize(serder=rserder, wigers=wigers)
        assert len(camIxnWitRctMsg) == 413
        for i in range(len(camWitKvys)):
            kvy = camWitKvys[i]
            kvy.process(
                ims=bytearray(camIxnWitRctMsg))  # send copy of witness rcts
            assert len(kvy.db.getWigs(dgkey)) == 3  # fully witnessed
            assert len(kvy.cues) == 0  # no cues

        # send Cam ixn's witness rcts to Van first then send Cam ixn
        vanKvy.process(ims=bytearray(camIxnWitRctMsg))
        vanKvy.processEscrows()
        assert vcKvr.sn == 0
        vanKvy.process(
            ims=bytearray(camIxnMsg))  # should escrow since not witnesses
        assert vcKvr.sn == 0
        vanKvy.processEscrows()
        assert vcKvr.sn == 1

        # Cam replace Wok with Wil as a witness.
        # Cam update Wil all event witnessed events for Cam by replay
        # Cam update itself with Wil receipts including Wils inception
        camReplayMsg = camHab.replay()
        assert len(camReplayMsg) == 1824
        wilKvy.process(ims=bytearray(camReplayMsg))
        assert camHab.pre in wilKvy.kevers
        assert wilKvy.kevers[camHab.pre].sn == 1  # asscepted both events
        assert len(wilKvy.cues) == 2
        wilRctMsg = wilHab.processCues(
            wilKvy.cues)  # process cue returns rct msg
        assert len(wilKvy.cues) == 0
        camKvy.process(ims=bytearray(wilRctMsg))  # make copy
        assert wilHab.pre in camKvy.kevers

        # Cam rotation with witness rotation
        camRotMsg = camHab.rotate(toad=2, cuts=[wokHab.pre], adds=[wilHab.pre])
        assert camHab.kever.wits == [wesHab.pre, wamHab.pre, wilHab.pre]
        assert camHab.kever.toad == 2
        assert camHab.kever.sn == 2

        # update lists of witness kvys and habs
        camWitKvys = [wesKvy, wamKvy, wilKvy]
        camWitHabs = [wesHab, wamHab, wilHab]

        rctMsgs = []  # list of receipt msgs from each witness
        for i in range(len(camWitKvys)):
            kvy = camWitKvys[i]
            kvy.process(
                ims=bytearray(camRotMsg))  # send copy of cam msg to witness
            assert kvy.kevers[camHab.pre].sn == 2  # accepted event
            assert len(kvy.cues) == 1  # queued receipt cue
            hab = camWitHabs[i]
            rctMsg = hab.processCues(kvy.cues)  # process cue returns rct msg
            assert len(rctMsg) == 281
            rctMsgs.append(rctMsg)

        for msg in rctMsgs:  # process rct msgs from all witnesses
            camKvy.process(ims=bytearray(msg))  # make copy
        for hab in camWitHabs:
            assert hab.pre in camKvy.kevers

        # get from Cam database copies of witness receipts received by Cam
        # and send to witnesses so all witnesses have full set of receipts
        # from all other witnesses
        # reply one event or receipt one event with all witness attachments
        dgkey = dbing.dgKey(pre=camHab.pre, dig=camHab.kever.serder.dig)
        wigs = camHab.db.getWigs(dgkey)
        assert len(wigs) == 3
        wigers = [coring.Siger(qb64b=bytes(wig)) for wig in wigs]
        rserder = eventing.receipt(pre=camHab.pre,
                                   sn=camHab.kever.sn,
                                   dig=camHab.kever.serder.dig)
        camRotWitRctMsg = eventing.messagize(serder=rserder, wigers=wigers)
        assert len(camRotWitRctMsg) == 413
        for i in range(len(camWitKvys)):
            kvy = camWitKvys[i]
            kvy.process(
                ims=bytearray(camRotWitRctMsg))  # send copy of witness rcts
            assert len(kvy.db.getWigs(dgkey)) == 3  # fully witnessed
            assert len(kvy.cues) == 0  # no cues

        # send Cam's rot and wit receipts to Van
        #vanKvy.process(ims=bytearray(camRotMsg))  # should escrow since not witnesses
        #vanKvy.process(ims=bytearray(camRotWitRctMsg))
        #vanKvy.processEscrows()
        #assert vcKvr.sn == 2
        #assert vcKvr.wits == camHab.kever.wits

        # send Cam rot's witness rcts to Van first then send Cam rot
        vanKvy.process(ims=bytearray(camRotWitRctMsg))
        vanKvy.processEscrows()
        assert vcKvr.sn == 1
        vanKvy.process(
            ims=bytearray(camRotMsg))  # should escrow since not witnesses
        assert vcKvr.sn == 1
        vanKvy.processEscrows()
        assert vcKvr.sn == 2
        assert vcKvr.wits == camHab.kever.wits

    assert not os.path.exists(wokKS.path)
    assert not os.path.exists(wokDB.path)
    assert not os.path.exists(wesKS.path)
    assert not os.path.exists(wesDB.path)
    assert not os.path.exists(vanKS.path)
    assert not os.path.exists(vanDB.path)
    assert not os.path.exists(camKS.path)
    assert not os.path.exists(camDB.path)
    """End Test"""
Exemplo n.º 2
0
def test_direct_mode_with_manager():
    """
    Test direct mode with transferable validator event receipts

    """
    # manual process to generate a list of secrets
    # root = pysodium.randombytes(pysodium.crypto_pwhash_SALTBYTES)
    # secrets = generateSecrets(root=root, count=8)

    # Direct Mode initiated by coe is controller, val is validator
    # but goes both ways once initiated.

    # set of secrets  (seeds for private keys)
    coeSalt = Salter(raw=b'0123456789abcdea').qb64

    # set of secrets (seeds for private keys)
    valSalt = Salter(raw=b'1123456789abcdea').qb64


    with openDB("controller") as coeLogger, openDB("validator") as valLogger, \
         openKS(name="controller") as coeKpr, openKS(name="validator") as valKpr:
        # Init key pair manager
        coeMgr = Manager(keeper=coeKpr, salt=coeSalt)
        coeVerfers, coeDigers, cst, nst = coeMgr.incept(icount=1, ncount=1)

        #  init Keverys
        coeKevery = eventing.Kevery(db=coeLogger)
        valKevery = eventing.Kevery(db=valLogger)

        coe_event_digs = []  # list of controller's own event log digs to verify against database
        val_event_digs = []  # list of validator's own event log digs to verify against database

        #  init sequence numbers for both controller and validator
        csn = cesn = 0  # sn and last establishment sn = esn
        vsn = vesn = 0  # sn and last establishment sn = esn

        # Controller Event 0  Inception Transferable (nxt digest not empty)
        coeSerder = incept(keys=[coeVerfers[0].qb64],
                           nxt=eventing.Nexter(digs=[coeDigers[0].qb64]).qb64,
                           code=MtrDex.Blake3_256)

        assert csn == int(coeSerder.ked["s"], 16) == 0
        coepre = coeSerder.ked["i"]

        coe_event_digs.append(coeSerder.dig)
        # sign serialization
        sigers = coeMgr.sign(ser=coeSerder.raw, verfers=coeVerfers)

        #  create serialized message
        cmsg = messagize(coeSerder, sigers=sigers)

        # create own Controller Kever in  Controller's Kevery
        parsing.Parser().parseOne(ims=bytearray(cmsg), kvy=coeKevery)
        # coeKevery.processOne(ims=bytearray(cmsg))  # send copy of cmsg
        coeKever = coeKevery.kevers[coepre]
        assert coeKever.prefixer.qb64 == coepre

        # Validator Event 0  Inception Transferable (nxt digest not empty)
        # Init key pair manager
        valMgr = Manager(keeper=valKpr, salt=valSalt)
        valVerfers, valDigers, cst, nst = valMgr.incept(icount=1, ncount=1)

        valSerder = incept(keys=[valVerfers[0].qb64],
                           nxt=eventing.Nexter(digs=[valDigers[0].qb64]).qb64,
                           code=MtrDex.Blake3_256)

        assert vsn == int(valSerder.ked["s"], 16) == 0
        valpre = valSerder.ked["i"]

        val_event_digs.append(valSerder.dig)

        # sign serialization
        sigers = valMgr.sign(valSerder.raw, verfers=valVerfers)  # return Siger if index

        #  create serialized message
        vmsg = messagize(valSerder, sigers=sigers)

        # create own Validator Kever in  Validator's Kevery
        parsing.Parser().parseOne(ims=bytearray(vmsg), kvy=valKevery)
        # valKevery.processOne(ims=bytearray(vmsg))  # send copy of vmsg
        valKever = valKevery.kevers[valpre]
        assert valKever.prefixer.qb64 == valpre

        # simulate sending of controller's inception message to validator
        parsing.Parser().parse(ims=bytearray(cmsg), kvy=valKevery)
        # valKevery.process(ims=bytearray(cmsg))  # make copy of msg
        assert coepre in valKevery.kevers  # creates Kever for controller in validator's .kevers

        # create receipt of controller's inception
        # create seal of validator's last establishment event
        seal = SealEvent(i=valpre,
                         s="{:x}".format(valKever.lastEst.s),
                         d=valKever.lastEst.d)
        coeK = valKevery.kevers[coepre]  # lookup coeKever from validator's .kevers
        # create trans receipt
        reserder = receipt(pre=coeK.prefixer.qb64,
                           sn=coeK.sn,
                           dig=coeK.serder.diger.qb64)
        #reserder = chit(pre=coeK.prefixer.qb64,
                        #sn=coeK.sn,
                        #dig=coeK.serder.diger.qb64,
                        #seal=seal)
        # Validate receipt

        # sign controller's event not receipt
        # look up event to sign from validator's kever for coe
        coeIcpDig = bytes(valKevery.db.getKeLast(key=snKey(pre=coepre, sn=csn)))
        assert coeIcpDig == coeK.serder.diger.qb64b
        coeIcpRaw = bytes(valKevery.db.getEvt(key=dgKey(pre=coepre, dig=coeIcpDig)))

        #counter = Counter(CtrDex.ControllerIdxSigs)
        #assert counter.qb64 == '-AAB'
        sigers = valMgr.sign(ser=coeIcpRaw, verfers=valVerfers)  # return Siger if index

        # process own validator receipt in validator's Kevery so have copy in own log
        rmsg = messagize(reserder, sigers=sigers, seal=seal)
        assert len(rmsg) == 353

        parsing.Parser().parseOne(ims=bytearray(rmsg), kvy=valKevery)
        # valKevery.processOne(ims=bytearray(rmsg))  # process copy of rmsg

        # attach receipt message to existing message with validators inception message
        # simulate streaming. validator first sends it's inception event, then sends a receipt to controller
        vmsg.extend(rmsg)

        # Simulate sending validator's inception event and receipt of
        # controller's inception message to controller
        parsing.Parser().parse(ims=vmsg, kvy=coeKevery)
        # coeKevery.process(ims=vmsg)  # controller process validator's inception and receipt

        # check if validator's Kever in controller's .kevers
        assert valpre in coeKevery.kevers
        #  check if receipt quadruple from validator in receipt database
        result = coeKevery.db.getVrcs(key=dgKey(pre=coeKever.prefixer.qb64,
                                                   dig=coeKever.serder.diger.qb64))
        assert bytes(result[0]) == (valKever.prefixer.qb64b +
                                    Seqner(sn=valKever.sn).qb64b +
                                    valKever.serder.diger.qb64b +
                                    sigers[0].qb64b)


        # create receipt to escrow use invalid digest and sequence number so not in controller's db
        fake = reserder.dig  # some other digest
        reserder = receipt(pre=coeK.prefixer.qb64,
                        sn=10,
                        dig=fake)
        # sign event not receipt
        sigers = valMgr.sign(ser=coeIcpRaw, verfers=valVerfers)  # return Siger if index
        # create receipt message
        vmsg = messagize(reserder, sigers=sigers, seal=seal)

        parsing.Parser().parse(ims=bytearray(vmsg), kvy=coeKevery)
        # coeKevery.process(ims=vmsg)  # controller process the escrow receipt from validator
        #  check if receipt quadruple in escrow database
        result = coeKevery.db.getVres(key=snKey(pre=coeKever.prefixer.qb64,
                                                   sn=10))
        assert bytes(result[0]) == (fake.encode("utf-8") +
                                    valKever.prefixer.qb64b +
                                    Seqner(sn=valKever.sn).qb64b +
                                    valKever.serder.diger.qb64b +
                                    sigers[0].qb64b)

        # Send receipt from controller to validator
        # create receipt of validator's inception
        # create seal of controller's last establishment event
        seal = SealEvent(i=coepre,
                         s="{:x}".format(coeKever.lastEst.s),
                         d=coeKever.lastEst.d)
        valK = coeKevery.kevers[valpre]  # lookup valKever from controller's .kevers
        # create trans receipt
        reserder = receipt(pre=valK.prefixer.qb64,
                        sn=valK.sn,
                        dig=valK.serder.diger.qb64,)
        # sign validator's event not receipt
        # look up event to sign from controller's kever for validator
        valIcpDig = bytes(coeKevery.db.getKeLast(key=snKey(pre=valpre, sn=vsn)))
        assert valIcpDig == valK.serder.diger.qb64b
        valIcpRaw = bytes(coeKevery.db.getEvt(key=dgKey(pre=valpre, dig=valIcpDig)))
        sigers = coeMgr.sign(ser=valIcpRaw, verfers=coeVerfers)  # return Siger if index
        # create receipt message
        cmsg = messagize(reserder, sigers=sigers, seal=seal)
        # controller process own receipt in own Kevery so have copy in own log
        parsing.Parser().parseOne(ims=bytearray(cmsg), kvy=coeKevery)
        # coeKevery.processOne(ims=bytearray(cmsg))  # make copy

        # Simulate sending controller's receipt of validator's inception message to validator
        parsing.Parser().parse(ims=cmsg, kvy=valKevery)
        # valKevery.process(ims=cmsg)  # controller process validator's inception and receipt

        #  check if receipt quadruple from controller in validator's receipt database
        result = valKevery.db.getVrcs(key=dgKey(pre=valKever.prefixer.qb64,
                                                   dig=valKever.serder.diger.qb64))
        assert bytes(result[0]) == (coeKever.prefixer.qb64b +
                                    Seqner(sn=coeKever.sn).qb64b +
                                    coeKever.serder.diger.qb64b +
                                    sigers[0].qb64b)

        # Controller Event 1 Rotation Transferable
        csn += 1
        cesn += 1
        assert csn == cesn == 1
        coeVerfers, coeDigers, cst, nst = coeMgr.rotate(pre=coeVerfers[0].qb64)
        coeSerder = rotate(pre=coeKever.prefixer.qb64,
                           keys=[coeVerfers[0].qb64],
                           dig=coeKever.serder.diger.qb64,
                           nxt=eventing.Nexter(digs=[coeDigers[0].qb64]).qb64,
                           sn=csn)
        coe_event_digs.append(coeSerder.dig)

        # sign serialization
        sigers = coeMgr.sign(coeSerder.raw, verfers=coeVerfers)  # returns sigers

        #  create serialized message
        cmsg = messagize(coeSerder, sigers=sigers)

        # update controller's key event verifier state
        parsing.Parser().parseOne(ims=bytearray(cmsg), kvy=coeKevery)
        # coeKevery.processOne(ims=bytearray(cmsg))  # make copy
        # verify controller's copy of controller's event stream is updated
        assert coeKever.sn == csn
        assert coeKever.serder.diger.qb64 == coeSerder.dig

        # simulate send message from controller to validator
        parsing.Parser().parse(ims=cmsg, kvy=valKevery)
        # valKevery.process(ims=cmsg)
        # verify validator's copy of controller's event stream is updated
        assert coeK.sn == csn
        assert coeK.serder.diger.qb64 == coeSerder.dig

        # create receipt of controller's rotation
        # create seal of validator's last establishment event
        seal = SealEvent(i=valpre,
                         s="{:x}".format(valKever.lastEst.s),
                         d=valKever.lastEst.d)
        # create validator receipt
        reserder = receipt(pre=coeK.prefixer.qb64,
                           sn=coeK.sn,
                           dig=coeK.serder.diger.qb64)
        # sign controller's event not receipt
        # look up event to sign from validator's kever for controller
        coeRotDig = bytes(valKevery.db.getKeLast(key=snKey(pre=coepre, sn=csn)))
        assert coeRotDig == coeK.serder.diger.qb64b
        coeRotRaw = bytes(valKevery.db.getEvt(key=dgKey(pre=coepre, dig=coeRotDig)))
        sigers = valMgr.sign(ser=coeRotRaw, verfers=valVerfers)
        # validator create receipt message
        vmsg = messagize(reserder, sigers=sigers, seal=seal)

        # validator process own receipt in own kevery so have copy in own log
        parsing.Parser().parseOne(ims=bytearray(vmsg), kvy=valKevery)
        # valKevery.processOne(ims=bytearray(vmsg))  # make copy

        # Simulate send to controller of validator's receipt of controller's rotation message
        parsing.Parser().parse(ims=vmsg, kvy=coeKevery)
        # coeKevery.process(ims=vmsg)  # controller process validator's incept and receipt

        # check if receipt quadruple from validator in receipt database
        result = coeKevery.db.getVrcs(key=dgKey(pre=coeKever.prefixer.qb64,
                                                   dig=coeKever.serder.diger.qb64))
        assert bytes(result[0]) == (valKever.prefixer.qb64b +
                                    Seqner(sn=valKever.sn).qb64b +
                                    valKever.serder.diger.qb64b +
                                    sigers[0].qb64b)

        # Next Event 2 Controller Interaction
        csn += 1  # do not increment esn
        assert csn == 2
        assert cesn == 1
        coeSerder = interact(pre=coeKever.prefixer.qb64,
                             dig=coeKever.serder.diger.qb64,
                             sn=csn)
        coe_event_digs.append(coeSerder.dig)

        # sign serialization
        sigers = coeMgr.sign(coeSerder.raw, verfers=coeVerfers)

        # create msg
        cmsg = messagize(coeSerder, sigers=sigers)

        # update controller's key event verifier state
        parsing.Parser().parseOne(ims=bytearray(cmsg), kvy=coeKevery)
        # coeKevery.processOne(ims=bytearray(cmsg))  # make copy
        # verify controller's copy of controller's event stream is updated
        assert coeKever.sn == csn
        assert coeKever.serder.diger.qb64 == coeSerder.dig

        # simulate send message from controller to validator
        parsing.Parser().parse(ims=cmsg, kvy=valKevery)
        # valKevery.process(ims=cmsg)
        # verify validator's copy of controller's event stream is updated
        assert coeK.sn == csn
        assert coeK.serder.diger.qb64 == coeSerder.dig

        # create receipt of controller's interaction
        # create seal of validator's last est event
        seal = SealEvent(i=valpre,
                         s="{:x}".format(valKever.lastEst.s),
                         d=valKever.lastEst.d)
        # create validator receipt
        reserder = receipt(pre=coeK.prefixer.qb64,
                        sn=coeK.sn,
                        dig=coeK.serder.diger.qb64)
        # sign controller's event not receipt
        # look up event to sign from validator's kever for controller
        coeIxnDig = bytes(valKevery.db.getKeLast(key=snKey(pre=coepre, sn=csn)))
        assert coeIxnDig == coeK.serder.diger.qb64b
        coeIxnRaw = bytes(valKevery.db.getEvt(key=dgKey(pre=coepre, dig=coeIxnDig)))
        sigers = valMgr.sign(ser=coeIxnRaw, verfers=valVerfers)
        # create receipt message
        vmsg = messagize(reserder, sigers=sigers, seal=seal)

        # validator process own receipt in own kevery so have copy in own log
        parsing.Parser().parseOne(ims=bytearray(vmsg), kvy=valKevery)
        # valKevery.processOne(ims=bytearray(vmsg))  # make copy

        # Simulate send to controller of validator's receipt of controller's rotation message
        parsing.Parser().parse(ims=bytearray(vmsg), kvy=coeKevery)
        # coeKevery.process(ims=vmsg)  # controller process validator's incept and receipt

        #  check if receipt quadruple from validator in receipt database
        result = coeKevery.db.getVrcs(key=dgKey(pre=coeKever.prefixer.qb64,
                                                   dig=coeKever.serder.diger.qb64))
        assert bytes(result[0]) == (valKever.prefixer.qb64b +
                                    Seqner(sn=valKever.sn).qb64b +
                                    valKever.serder.diger.qb64b +
                                    sigers[0].qb64b)


        #  verify final controller event state
        assert coeKever.sn == coeK.sn == csn

        db_digs = [bytes(v).decode("utf-8") for v in coeKever.baser.getKelIter(coepre)]
        assert len(db_digs) == len(coe_event_digs) == csn + 1
        assert db_digs == coe_event_digs

        db_digs = [bytes(v).decode("utf-8") for v in valKever.baser.getKelIter(coepre)]
        assert len(db_digs) == len(coe_event_digs) == csn + 1
        assert db_digs == coe_event_digs

        #  verify final validator event state
        assert valKever.sn == valK.sn == vsn

        db_digs = [bytes(v).decode("utf-8") for v in valKever.baser.getKelIter(valpre)]
        assert len(db_digs) == len(val_event_digs) == vsn + 1
        assert db_digs == val_event_digs

        db_digs = [bytes(v).decode("utf-8") for v in coeKever.baser.getKelIter(valpre)]
        assert len(db_digs) == len(val_event_digs) == vsn + 1
        assert db_digs == val_event_digs

    assert not os.path.exists(valKevery.db.path)
    assert not os.path.exists(coeKever.baser.path)