@@ -749,6 +749,188 @@ def test_missing_delegator_escrow():
749749 """End Test"""
750750
751751
752+ def test_witness_anchor_escrow ():
753+ """
754+ Test pending witness delegation anchor escrow (pwde).
755+
756+ bob is the delegator
757+ del is the delegate
758+ wit is the delegate's witness
759+
760+ When a witness accepts a delegated inception event, validateDelegation
761+ short-circuits (returns (None, None)) for locallyWitnessed events to
762+ avoid a deadlock: the delegate needs witness receipts, but a strict
763+ witness would need the delegation anchor first. Instead, logEvent
764+ tracks the un-anchored delegated event in db.pwde.
765+
766+ Once the delegator's KEL arrives (containing the anchoring ixn with a
767+ SealEvent for the delegate's dip), processEscrowWitnessAnchors finds
768+ the seal via fetchLastSealingEventByEventSeal and stores the AES.
769+ """
770+
771+ bobSalt = core .Salter (raw = b'0123456789abcdef' ).qb64
772+ delSalt = core .Salter (raw = b'abcdef0123456789' ).qb64
773+ witSalt = core .Salter (raw = b'wxyzabcdefghijkl' ).qb64
774+
775+ psr = parsing .Parser ()
776+
777+ with (basing .openDB (name = "bob" ) as bobDB ,
778+ keeping .openKS (name = "bob" ) as bobKS ,
779+ basing .openDB (name = "del" ) as delDB ,
780+ keeping .openKS (name = "del" ) as delKS ,
781+ basing .openDB (name = "wit" ) as witDB ,
782+ keeping .openKS (name = "wit" ) as witKS ):
783+
784+ bobMgr = keeping .Manager (ks = bobKS , salt = bobSalt )
785+ delMgr = keeping .Manager (ks = delKS , salt = delSalt )
786+ witMgr = keeping .Manager (ks = witKS , salt = witSalt )
787+
788+ bobKvy = eventing .Kevery (db = bobDB )
789+ delKvy = eventing .Kevery (db = delDB )
790+ witKvy = eventing .Kevery (db = witDB )
791+
792+ # --- Setup Wit as a non-transferable identifier (required for witnesses) ---
793+ verfers , digers = witMgr .incept (stem = 'wit' , ncount = 0 ,
794+ transferable = False , temp = True )
795+ witSrdr = eventing .incept (keys = [verfer .qb64 for verfer in verfers ])
796+
797+ witPre = witSrdr .pre
798+ witMgr .move (old = verfers [0 ].qb64 , new = witPre )
799+ witDB .prefixes .add (witPre )
800+ assert witPre in witDB .prefixes
801+
802+ sigers = witMgr .sign (ser = witSrdr .raw , verfers = verfers )
803+ msg = bytearray (witSrdr .raw )
804+ counter = core .Counter (core .Codens .ControllerIdxSigs ,
805+ count = len (sigers ), gvrsn = kering .Vrsn_1_0 )
806+ msg .extend (counter .qb64b )
807+ for siger in sigers :
808+ msg .extend (siger .qb64b )
809+ witIcpMsg = msg
810+
811+ psr .parse (ims = bytearray (witIcpMsg ), kvy = witKvy , local = True )
812+ witK = witKvy .kevers [witPre ]
813+ assert witK .prefixer .qb64 == witPre
814+ assert witK .serder .said == witSrdr .said
815+
816+ # --- Setup Bob (delegator) with own inception event ---
817+ verfers , digers = bobMgr .incept (stem = 'bob' , temp = True )
818+ bobSrdr = eventing .incept (keys = [verfer .qb64 for verfer in verfers ],
819+ ndigs = [diger .qb64 for diger in digers ],
820+ code = coring .MtrDex .Blake3_256 )
821+
822+ bobPre = bobSrdr .pre
823+ bobMgr .move (old = verfers [0 ].qb64 , new = bobPre )
824+ bobDB .prefixes .add (bobPre )
825+ assert bobPre in bobDB .prefixes
826+
827+ sigers = bobMgr .sign (ser = bobSrdr .raw , verfers = verfers )
828+ msg = bytearray (bobSrdr .raw )
829+ counter = core .Counter (core .Codens .ControllerIdxSigs ,
830+ count = len (sigers ), gvrsn = kering .Vrsn_1_0 )
831+ msg .extend (counter .qb64b )
832+ for siger in sigers :
833+ msg .extend (siger .qb64b )
834+ bobIcpMsg = msg
835+
836+ psr .parse (ims = bytearray (bobIcpMsg ), kvy = bobKvy , local = True )
837+ bobK = bobKvy .kevers [bobPre ]
838+ assert bobK .prefixer .qb64 == bobPre
839+ assert bobK .serder .said == bobSrdr .said
840+ assert bobK .sn == 0
841+
842+ psr .parse (ims = bytearray (bobIcpMsg ), kvy = delKvy , local = True )
843+ assert bobPre in delKvy .kevers
844+
845+ # --- Create Del's delegated inception event ---
846+ verfers , digers = delMgr .incept (stem = 'del' , temp = True )
847+ delSrdr = eventing .delcept (keys = [verfer .qb64 for verfer in verfers ],
848+ delpre = bobPre ,
849+ ndigs = [diger .qb64 for diger in digers ],
850+ wits = [witPre ],
851+ toad = 1 )
852+
853+ delPre = delSrdr .pre
854+ delMgr .move (old = verfers [0 ].qb64 , new = delPre )
855+
856+ sigers = delMgr .sign (ser = delSrdr .raw , verfers = verfers )
857+ msg = bytearray (delSrdr .raw )
858+ counter = core .Counter (core .Codens .ControllerIdxSigs ,
859+ count = len (sigers ), gvrsn = kering .Vrsn_1_0 )
860+ msg .extend (counter .qb64b )
861+ for siger in sigers :
862+ msg .extend (siger .qb64b )
863+ delIcpMsg = msg
864+
865+ # --- Witness receives dip (no anchor exists yet) ---
866+ psr .parse (ims = bytearray (delIcpMsg ), kvy = witKvy , local = True )
867+
868+ assert delPre in witKvy .kevers
869+ witDelK = witKvy .kevers [delPre ]
870+ assert witDelK .delegated
871+ assert witDelK .serder .said == delSrdr .said
872+
873+ # AES not set — witness accepted without delegation anchor
874+ assert witKvy .db .getAes (dbing .dgKey (delPre , delSrdr .said )) is None
875+
876+ # Event tracked in pwde escrow
877+ escrows = witKvy .db .pwde .getOn (keys = delPre , on = delSrdr .sn )
878+ assert len (escrows ) == 1
879+ assert escrows [0 ] == delSrdr .said
880+
881+ # --- Escrow processing: delegator KEL unknown, nothing resolves ---
882+ witKvy .processEscrowWitnessAnchors ()
883+ assert witKvy .db .getAes (dbing .dgKey (delPre , delSrdr .said )) is None
884+ assert len (witKvy .db .pwde .getOn (keys = delPre , on = delSrdr .sn )) == 1
885+
886+ # --- Delegator creates ixn with seal (after witness accepted dip) ---
887+ seal = eventing .SealEvent (i = delPre ,
888+ s = delSrdr .ked ["s" ],
889+ d = delSrdr .said )
890+ bobIxnSrdr = eventing .interact (pre = bobK .prefixer .qb64 ,
891+ dig = bobK .serder .said ,
892+ sn = bobK .sn + 1 ,
893+ data = [seal ._asdict ()])
894+
895+ sigers = bobMgr .sign (ser = bobIxnSrdr .raw , verfers = bobK .verfers )
896+ msg = bytearray (bobIxnSrdr .raw )
897+ counter = core .Counter (core .Codens .ControllerIdxSigs ,
898+ count = len (sigers ), gvrsn = kering .Vrsn_1_0 )
899+ msg .extend (counter .qb64b )
900+ for siger in sigers :
901+ msg .extend (siger .qb64b )
902+ bobIxnMsg = msg
903+
904+ psr .parse (ims = bytearray (bobIxnMsg ), kvy = bobKvy , local = True )
905+ assert bobK .sn == 1
906+
907+ # --- Deliver Bob's full KEL (icp + ixn) to witness ---
908+ psr .parse (ims = bytearray (bobIcpMsg ), kvy = witKvy , local = False )
909+ assert bobPre in witKvy .kevers
910+
911+ psr .parse (ims = bytearray (bobIxnMsg ), kvy = witKvy , local = False )
912+ assert witKvy .kevers [bobPre ].sn == 1
913+
914+ # --- processEscrowWitnessAnchors resolves the AES ---
915+ witKvy .processEscrowWitnessAnchors ()
916+
917+ seqner = coring .Seqner (sn = bobIxnSrdr .sn )
918+ couple = witKvy .db .getAes (dbing .dgKey (delPre , delSrdr .said ))
919+ assert couple == seqner .qb64b + bobIxnSrdr .saidb
920+
921+ # pwde escrow cleared
922+ assert len (witKvy .db .pwde .getOn (keys = delPre , on = delSrdr .sn )) == 0
923+
924+ assert not os .path .exists (witKS .path )
925+ assert not os .path .exists (witDB .path )
926+ assert not os .path .exists (delKS .path )
927+ assert not os .path .exists (delDB .path )
928+ assert not os .path .exists (bobKS .path )
929+ assert not os .path .exists (bobDB .path )
930+
931+ """End Test"""
932+
933+
752934def test_misfit_escrow ():
753935 """
754936 Test misfit escrow
0 commit comments