# -*- encoding: utf-8 -*-
"""
KERI
keri.vdr.credentialing module
VC issuer support
"""
from hio.base import doing
from hio.help import decking
from keri.vdr import viring
from .. import kering, help
from ..app import agenting
from ..app.habbing import GroupHab
from ..core import parsing, coring, scheming, serdering
from ..core.coring import Seqner, MtrDex
from ..core.eventing import TraitDex
from ..db import dbing
from ..db.dbing import snKey, dgKey
from ..vc import proving
from ..vdr import eventing
from ..vdr.viring import Reger
logger = help.ogler.getLogger()
class Regery:
def __init__(self, hby, name="test", base="", reger=None, temp=False, cues=None):
self.hby = hby
self.name = name
self.base = base
self.temp = temp
self.cues = cues if cues is not None else decking.Deck()
self.reger = reger if reger is not None else Reger(name=self.name, base=base, db=self.hby.db, temp=temp,
reopen=True)
self.tvy = eventing.Tevery(reger=self.reger, db=self.hby.db, local=True, lax=True)
self.psr = parsing.Parser(framed=True, kvy=self.hby.kvy, tvy=self.tvy)
self.regs = {} # List of local registries
self.inited = False
if self.reger.opened:
self.setup()
def setup(self):
if not self.reger.opened:
raise kering.ClosedError("Attempt to setup Regery with closed "
"reger.")
self.loadRegistries()
self.inited = True
def loadRegistries(self):
""" Load Registry objects for each entry in the .regs database
"""
for name, regord in self.reger.regs.getItemIter():
name, = name
regk = regord.registryKey
pre = regord.prefix
hab = self.hby.habs[pre]
if hab is None:
raise kering.ConfigurationError(f"Unknown prefix {pre} for creating Registry {name}")
reg = Registry(hab=hab, reger=self.reger, tvy=self.tvy, psr=self.psr,
name=name, regk=regk, cues=self.cues)
reg.inited = True
self.regs[regk] = reg
self.reger.registries.add(regk)
def makeRegistry(self, name, prefix, **kwa):
hab = self.hby.habs[prefix]
if hab is None:
raise kering.ConfigurationError(f"Unknown prefix {prefix} for creating Registry {name}")
reg = Registry(hab=hab, name=name, reger=self.reger, tvy=self.tvy, psr=self.psr, cues=self.cues)
reg.make(**kwa)
self.regs[reg.regk] = reg
return reg
def makeSignifyRegistry(self, name, prefix, regser):
hab = self.hby.habs[prefix]
if hab is None:
raise kering.ConfigurationError(f"Unknown prefix {prefix} for creating Registry {name}")
reg = SignifyRegistry(hab=hab, name=name, reger=self.reger, tvy=self.tvy, psr=self.psr, cues=self.cues)
reg.make(regser=regser)
self.regs[reg.regk] = reg
return reg
def registryByName(self, name):
if regrec := self.reger.regs.get(name):
return self.regs[regrec.registryKey] if regrec.registryKey in self.regs else None
return None
@property
def tevers(self):
""" tevers property
Returns .reger.tevers
"""
return self.reger.tevers
def processEscrows(self):
""" Process escrows for each registry """
self.tvy.processEscrows()
def close(self):
if self.reger.opened:
self.reger.close()
class RegeryDoer(doing.Doer):
""" """
def __init__(self, rgy):
self.rgy = rgy
super(RegeryDoer, self).__init__()
def do(self, tymth, tock=0.0, **opts):
"""
Parameters:
tymth: is injected function wrapper closure returned by .tymen() of
Tymist instance. Calling tymth() returns associated Tymist .tyme.
tock: is injected initial tock value
**opts (dict): additional keyword arguments
Returns:
"""
self.wind(tymth)
self.tock = tock
_ = (yield self.tock)
while True:
self.rgy.processEscrows()
yield self.tock
[docs]
class BaseRegistry:
"""
Issuer provides encapsulation of creating a Verifiable Credential Registry with issuance
and revocation of VCs against that registry.
The Registry consists of 1 management TEL for maintaining the state of the registry wrt special
Backers that can act as witnesses of VC events, and 1 VC TEL for each VC issued that tracks the
issuance and revocation status of those VCs.
"""
[docs]
def __init__(self, hab, reger, tvy, psr, name="test", regk=None, cues=None):
""" Initialize Instance
Parameters:
hab (Habitat): instance of local controller's context
name (str): alias for this issuer
reger (Reger): database instance for controller's credentials
"""
self.hab = hab
self.name = name
self.reger = reger
self.tvy = tvy # injected
self.psr = psr # injected
self.cues = cues if cues is not None else decking.Deck()
self.regk = regk
self.regd = None
self.vcp = None
self.cnfg = []
self.inited = False
@property
def tevers(self):
""" tevers property
Returns .reger.tevers
"""
return self.reger.tevers
@property
def tever(self):
return self.reger.tevers[self.regk]
@property
def estOnly(self):
return self.tever.estOnly
@property
def noBackers(self):
return self.tever.noBackers
@property
def baks(self):
return self.tever.baks
@property
def regi(self):
return int(self.tever.serder.ked["s"], 16)
@property
def regser(self):
return self.tever.serder
@property
def registries(self):
return self.reger.registries
[docs]
def processEvent(self, serder):
""" Process registry events
Parameters:
serder (Serder): Registry TEL event to process
"""
try:
self.tvy.processEvent(serder=serder)
except kering.MissingAnchorError:
logger.info("Credential registry missing anchor for inception = {}".format(serder.ked))
[docs]
def anchorMsg(self, pre, regd, seqner, saider):
""" Create key event with seal to serder anchored as data.
Performs a rotation or interaction event for single sig or multiple sig identifier
to anchor the provide registry event. Inserts outbound cues for external processing
of resulting events or multisig handling.
Parameters:
pre (str): registry event identifier
regd (str): registry event SAID
seqner (Seqner): sequence number of anchoring event
saider (Saider): SAID of the anchoring event
"""
key = dgKey(pre, regd)
sealet = seqner.qb64b + saider.qb64b
self.reger.putAnc(key, sealet)
[docs]
class Registry(BaseRegistry):
"""
"""
[docs]
def make(self, *, nonce=None, noBackers=True, baks=None, toad=None, estOnly=False, vcp=None):
""" Delayed initialization of Issuer.
Actual initialization of Issuer from properties or loaded from .reger. Should
only be called after .hab is initied.
Parameters:
nonce (str) qb64 random seed for credential registries
noBackers (boolean): True to allow specification of TEL specific backers
baks (list): initial list of backer prefixes qb64 for VCs in the Registry
toad (str): hex of witness threshold
estOnly (boolean): True for forcing rotation events for every TEL event.
vcp (Serder): optional vcp event serder if configured outside the Registry
"""
pre = self.hab.pre
if vcp is None:
baks = baks if baks is not None else []
self.cnfg = [TraitDex.NoBackers] if noBackers else []
if estOnly:
self.cnfg.append(TraitDex.EstOnly)
self.vcp = eventing.incept(pre,
baks=baks,
toad=toad,
nonce=nonce,
cnfg=self.cnfg,
code=MtrDex.Blake3_256)
else:
self.vcp = vcp
self.regk = self.vcp.pre
self.regd = self.vcp.said
self.registries.add(self.regk)
self.reger.regs.put(keys=self.name,
val=viring.RegistryRecord(registryKey=self.regk, prefix=pre))
self.processEvent(serder=self.vcp)
self.inited = True
[docs]
def rotate(self, toad=None, cuts=None, adds=None):
""" Rotate backer list for registry
Parameters:
toad (int): or str hex of backer threshold after cuts and adds
cuts (list): of qb64 pre of backers to be removed from witness list
adds (list): of qb64 pre of backers to be added to witness list
Returns:
boolean: True if rotation is successful
"""
if self.noBackers:
raise ValueError("Attempt to rotate registry {} that does not support backers".format(self.regk))
serder = eventing.rotate(dig=self.regser.said,
regk=self.regk,
sn=self.regi + 1,
toad=toad,
baks=self.baks,
adds=adds,
cuts=cuts)
self.processEvent(serder=serder)
return serder
[docs]
def issue(self, said, dt=None):
""" Create and process an iss or bis message event
Parameters:
said (str): qb64 SAID of credential to issue
dt (str): iso8601 formatted date time string of issuance
Returns:
boolean: True if issuance is successful
"""
if self.noBackers:
serder = eventing.issue(vcdig=said, regk=self.regk, dt=dt)
else:
serder = eventing.backerIssue(vcdig=said,
regk=self.regk,
regsn=self.regi,
regd=self.regser.said,
dt=dt)
self.processEvent(serder=serder)
return serder
[docs]
def revoke(self, said, dt=None):
""" Perform revocation of credential
Create and process rev or brv message event
Parameters:
said (str): qb64 SAID of the credential to revoke
dt (str): iso8601 formatted date time string of revocation
Returns:
boolean: True if revocation is successful.
"""
vci = said
vcser = self.reger.getTel(snKey(pre=vci, sn=0))
if vcser is None:
raise kering.ValidationError("Invalid revoke of {} that has not been issued "
"pre={}.".format(vci, self.regk))
ievt = self.reger.getTvt(dgKey(pre=vci, dig=vcser))
iserder = serdering.SerderKERI(raw=bytes(ievt)) #Serder(raw=bytes(ievt))
if self.noBackers:
serder = eventing.revoke(vcdig=vci, regk=self.regk, dig=iserder.said, dt=dt)
else:
serder = eventing.backerRevoke(vcdig=vci,
regk=self.regk,
regsn=self.regi,
regd=self.regser.said,
dig=iserder.said, dt=dt)
self.processEvent(serder=serder)
return serder
[docs]
class SignifyRegistry(BaseRegistry):
[docs]
def make(self, *, regser):
""" Delayed initialization of Issuer.
Actual initialization of Issuer from properties or loaded from .reger. Should
only be called after .hab is initied.
Parameters:
regser (Serder): Regsitry inception event
"""
pre = self.hab.pre
self.regk = regser.pre
self.regd = regser.said
self.registries.add(self.regk)
self.reger.regs.put(keys=self.name,
val=viring.RegistryRecord(registryKey=self.regk, prefix=pre))
try:
self.processEvent(serder=regser)
except kering.LikelyDuplicitousError:
pass
self.inited = True
[docs]
def rotate(self, serder):
""" Rotate backer list for registry
Parameters:
serder (Serder): Regsitry inception event
Returns:
boolean: True if rotation is successful
"""
if self.noBackers:
raise ValueError("Attempt to rotate registry {} that does not support backers".format(self.regk))
if serder.ked['s'] != self.regi + 1:
raise ValueError(f"Invalid sequence number {serder.ked['s']}")
self.processEvent(serder=serder)
return serder
[docs]
def issue(self, said, dt=None):
""" Create and process an iss or bis message event
Parameters:
said (str): qb64 SAID of credential to issue
dt (str): iso8601 formatted date time string of issuance
Returns:
boolean: True if issuance is successful
"""
if self.noBackers:
serder = eventing.issue(vcdig=said, regk=self.regk, dt=dt)
else:
serder = eventing.backerIssue(vcdig=said, regk=self.regk, regsn=self.regi, regd=self.regser.said,
dt=dt)
self.processEvent(serder=serder)
return serder
[docs]
def revoke(self, said, dt=None):
""" Perform revocation of credential
Create and process rev or brv message event
Parameters:
said (str): qb64 SAID of the credential to revoke
dt (str): iso8601 formatted date time string of revocation
Returns:
boolean: True if revocation is successful.
"""
vci = said
vcser = self.reger.getTel(snKey(pre=vci, sn=0))
if vcser is None:
raise kering.ValidationError("Invalid revoke of {} that has not been issued "
"pre={}.".format(vci, self.regk))
ievt = self.reger.getTvt(dgKey(pre=vci, dig=vcser))
iserder = serdering.serderACDC(raw=bytes(ievt)) # Serder(raw=bytes(ievt))
if self.noBackers:
serder = eventing.revoke(vcdig=vci, regk=self.regk, dig=iserder.said, dt=dt)
else:
serder = eventing.backerRevoke(vcdig=vci, regk=self.regk, regsn=self.regi, regd=self.regser.said,
dig=iserder.said, dt=dt)
self.processEvent(serder=serder)
return serder
[docs]
class Registrar(doing.DoDoer):
def __init__(self, hby, rgy, counselor):
self.hby = hby
self.rgy = rgy
self.counselor = counselor
self.witDoer = agenting.WitnessReceiptor(hby=self.hby)
self.witPub = agenting.WitnessPublisher(hby=self.hby)
doers = [self.witDoer, self.witPub, doing.doify(self.escrowDo)]
super(Registrar, self).__init__(doers=doers)
[docs]
def incept(self, iserder, anc):
"""
Parameters:
iserder (SerderKERI): Serder object of TEL iss event
anc (SerderKERI): Serder object of anchoring event
Returns:
Registry: created registry
"""
registry = self.rgy.regs[iserder.pre]
hab = registry.hab
rseq = coring.Seqner(sn=0)
if not isinstance(hab, GroupHab): # not a multisig group
seqner = coring.Seqner(sn=hab.kever.sner.num)
saider = coring.Saider(qb64=hab.kever.serder.said)
registry.anchorMsg(pre=iserder.pre,
regd=iserder.said,
seqner=seqner,
saider=saider)
print("Waiting for TEL event witness receipts")
self.witDoer.msgs.append(dict(pre=anc.pre, sn=seqner.sn))
self.rgy.reger.tpwe.add(keys=(registry.regk, rseq.qb64), val=(hab.kever.prefixer, seqner, saider))
else:
sn = anc.sn
said = anc.said
prefixer = coring.Prefixer(qb64=hab.pre)
seqner = coring.Seqner(sn=sn)
saider = coring.Saider(qb64=said)
self.counselor.start(prefixer=prefixer, seqner=seqner, saider=saider, ghab=hab)
print("Waiting for TEL registry vcp event mulisig anchoring event")
self.rgy.reger.tmse.add(keys=(registry.regk, rseq.qb64, registry.regd), val=(prefixer, seqner, saider))
[docs]
def issue(self, creder, iserder, anc):
"""
Create and process the credential issuance TEL events on the given registry
Parameters:
creder (SerderACDC): credential to issue
iserder (SerderKERI): Serder object of TEL iss event
anc (SerderKERI): Serder object of anchoring event
"""
regk = creder.regi
registry = self.rgy.regs[regk]
hab = registry.hab
vcid = iserder.ked["i"]
rseq = coring.Seqner(snh=iserder.ked["s"])
if not isinstance(hab, GroupHab): # not a multisig group
seqner = coring.Seqner(sn=hab.kever.sner.num)
saider = coring.Saider(qb64=hab.kever.serder.said)
registry.anchorMsg(pre=vcid, regd=iserder.said, seqner=seqner, saider=saider)
print("Waiting for TEL event witness receipts")
self.witDoer.msgs.append(dict(pre=hab.pre, sn=seqner.sn))
self.rgy.reger.tpwe.add(keys=(vcid, rseq.qb64), val=(hab.kever.prefixer, seqner, saider))
else: # multisig group hab
sn = anc.sn
said = anc.said
prefixer = coring.Prefixer(qb64=hab.pre)
seqner = coring.Seqner(sn=sn)
saider = coring.Saider(qb64=said)
self.counselor.start(prefixer=prefixer, seqner=seqner, saider=saider, ghab=hab)
print(f"Waiting for TEL iss event mulisig anchoring event {seqner.sn}")
self.rgy.reger.tmse.add(keys=(vcid, rseq.qb64, iserder.said), val=(prefixer, seqner, saider))
[docs]
def revoke(self, creder, rserder, anc):
"""
Create and process the credential revocation TEL events on the given registry
Parameters:
creder (Creder): credential to issue
rserder (Serder): Serder object of TEL rev event
anc (Serder): Serder object of anchoring event
"""
regk = creder.regi
registry = self.rgy.regs[regk]
hab = registry.hab
vcid = rserder.ked["i"]
rseq = coring.Seqner(snh=rserder.ked["s"])
if not isinstance(hab, GroupHab): # not a multisig group
seqner = coring.Seqner(sn=hab.kever.sner.num)
saider = coring.Saider(qb64=hab.kever.serder.said)
registry.anchorMsg(pre=vcid, regd=rserder.said, seqner=seqner, saider=saider)
print("Waiting for TEL event witness receipts")
self.witDoer.msgs.append(dict(pre=hab.pre, sn=seqner.sn))
self.rgy.reger.tpwe.add(keys=(vcid, rseq.qb64), val=(hab.kever.prefixer, seqner, saider))
return vcid, rseq.sn
else:
sn = anc.sn
said = anc.said
prefixer = coring.Prefixer(qb64=hab.pre)
seqner = coring.Seqner(sn=sn)
saider = coring.Saider(qb64=said)
self.counselor.start(prefixer=prefixer, seqner=seqner, saider=saider, ghab=hab)
print(f"Waiting for TEL rev event mulisig anchoring event {seqner.sn}")
self.rgy.reger.tmse.add(keys=(vcid, rseq.qb64, rserder.said), val=(prefixer, seqner, saider))
return vcid, rseq.sn
@staticmethod
def multisigIxn(hab, rseal):
ixn = hab.interact(data=[rseal])
serder = serdering.SerderKERI(raw=bytes(ixn))
sn = serder.sn
said = serder.said
prefixer = coring.Prefixer(qb64=hab.pre)
seqner = coring.Seqner(sn=sn)
saider = coring.Saider(qb64=said)
return ixn, prefixer, seqner, saider
[docs]
def complete(self, pre, sn=0):
""" Determine if registry event (inception, issuance, revocation, etc.) is finished validation
Parameters:
pre (str): qb64 identifier of registry event
sn (int): integer sequence number of regsitry event
Returns:
bool: True means event has completed and is commited to database
"""
seqner = coring.Seqner(sn=sn)
said = self.rgy.reger.ctel.get(keys=(pre, seqner.qb64))
return said is not None and self.witPub.sent(said=pre)
[docs]
def escrowDo(self, tymth, tock=1.0):
""" Process escrows of group multisig identifiers waiting to be compeleted.
Steps involve:
1. Sending local event with sig to other participants
2. Waiting for signature threshold to be met.
3. If elected and delegated identifier, send complete event to delegator
4. If delegated, wait for delegator's anchor
5. If elected, send event to witnesses and collect receipts.
6. Otherwise, wait for fully receipted event
Parameters:
tymth (function): injected function wrapper closure returned by .tymen() of
Tymist instance. Calling tymth() returns associated Tymist .tyme.
tock (float): injected initial tock value. Default to 1.0 to slow down processing
"""
# enter context
self.wind(tymth)
self.tock = tock
_ = (yield self.tock)
while True:
self.processEscrows()
yield 0.5
[docs]
def processEscrows(self):
"""
Process credential registry anchors:
"""
self.processWitnessEscrow()
self.processMultisigEscrow()
self.processDiseminationEscrow()
[docs]
def processWitnessEscrow(self):
"""
Process escrow of group multisig events that do not have a full compliment of receipts
from witnesses yet. When receipting is complete, remove from escrow and cue up a message
that the event is complete.
"""
for (regk, snq), (prefixer, seqner, saider) in self.rgy.reger.tpwe.getItemIter(): # partial witness escrow
kever = self.hby.kevers[prefixer.qb64]
dgkey = dbing.dgKey(prefixer.qb64b, saider.qb64)
# Load all the witness receipts we have so far
wigs = self.hby.db.getWigs(dgkey)
if kever.wits:
if len(wigs) == len(kever.wits): # We have all of them, this event is finished
hab = self.hby.habs[prefixer.qb64]
witnessed = False
for cue in self.witDoer.cues:
if cue["pre"] == hab.pre and cue["sn"] == seqner.sn:
witnessed = True
if not witnessed:
continue
else:
continue
rseq = coring.Seqner(qb64=snq)
self.rgy.reger.tpwe.rem(keys=(regk, snq))
self.rgy.reger.tede.add(keys=(regk, rseq.qb64), val=(prefixer, seqner, saider))
[docs]
def processMultisigEscrow(self):
"""
Process escrow of group multisig events that do not have a full compliment of receipts
from witnesses yet. When receipting is complete, remove from escrow and cue up a message
that the event is complete.
"""
for (regk, snq, regd), (prefixer, seqner, saider) in self.rgy.reger.tmse.getItemIter(): # multisig escrow
try:
if not self.counselor.complete(prefixer, seqner, saider):
continue
except kering.ValidationError:
self.rgy.reger.tmse.rem(keys=(regk, snq, regd))
continue
rseq = coring.Seqner(qb64=snq)
# Anchor the message, registry or otherwise
key = dgKey(regk, regd)
sealet = seqner.qb64b + saider.qb64b
self.rgy.reger.putAnc(key, sealet)
self.rgy.reger.tmse.rem(keys=(regk, snq, regd))
self.rgy.reger.tede.add(keys=(regk, rseq.qb64), val=(prefixer, seqner, saider))
def processDiseminationEscrow(self):
for (regk, snq), (prefixer, seqner, saider) in self.rgy.reger.tede.getItemIter(): # group multisig escrow
rseq = coring.Seqner(qb64=snq)
dig = self.rgy.reger.getTel(key=snKey(pre=regk, sn=rseq.sn))
if dig is None:
continue
self.rgy.reger.tede.rem(keys=(regk, snq))
tevt = bytearray()
for msg in self.rgy.reger.clonePreIter(pre=regk, fn=rseq.sn):
tevt.extend(msg)
print(f"Sending TEL events to witnesses")
# Fire and forget the TEL event to the witnesses. Consumers will have to query
# to determine when the Witnesses have received the TEL events.
self.witPub.msgs.append(dict(pre=prefixer.qb64, said=regk, msg=tevt))
self.rgy.reger.ctel.put(keys=(regk, rseq.qb64), val=saider) # idempotent
[docs]
class Credentialer(doing.DoDoer):
def __init__(self, hby, rgy, registrar, verifier):
self.hby = hby
self.rgy = rgy
self.registrar = registrar
self.verifier = verifier
doers = [doing.doify(self.escrowDo)]
super(Credentialer, self).__init__(doers=doers)
[docs]
def create(self, regname, recp: str, schema, source, rules, data, private=False):
""" Create and validate a credential returning the fully populated Creder
Parameters:
regname:
recp (str):
schema:
source:
rules:
data:
private: add nonce for privacy preserving
Returns:
Creder: Creder class for the issued credential
"""
if recp is not None and recp not in self.hby.kevers:
raise kering.ConfigurationError("Unable to issue credential to {}. A connection to that identifier must "
"already be established".format(recp))
registry = self.rgy.registryByName(regname)
if registry is None:
raise kering.ConfigurationError("Credential registry {} does not exist. It must be created before issuing "
"credentials".format(regname))
creder = proving.credential(issuer=registry.hab.pre,
schema=schema,
recipient=recp,
data=data,
source=source,
private=private,
rules=rules,
status=registry.regk)
self.validate(creder)
return creder
[docs]
def validate(self, creder):
"""
Args:
creder (Creder): creder object representing the credential to validate
Returns:
bool: true if credential is valid against a known schema
"""
schema = creder.sad['s']
scraw = self.verifier.resolver.resolve(schema)
if not scraw:
raise kering.ConfigurationError("Credential schema {} not found. It must be loaded with data oobi before "
"issuing credentials".format(schema))
schemer = scheming.Schemer(raw=scraw)
try:
schemer.verify(creder.raw)
except kering.ValidationError as ex:
raise kering.ConfigurationError(f"Credential schema validation failed for {schema}: {ex}")
return True
[docs]
def issue(self, creder, serder):
""" Issue the credential creder and handle witness propagation and communication
Args:
creder (Creder): Credential object to issue
serder (Serder): KEL or TEL anchoring event
need to contribute digest of next rotating key
"""
# escrow waiting for other signatures
prefixer = coring.Prefixer(qb64=serder.pre)
seqner = coring.Seqner(sn=serder.sn)
self.rgy.reger.cmse.put(keys=(creder.said, seqner.qb64), val=creder)
try:
self.verifier.processCredential(creder=creder, prefixer=prefixer, seqner=seqner,
saider=coring.Saider(qb64=serder.said))
except kering.MissingRegistryError:
pass
def processCredentialMissingSigEscrow(self):
for (said, snq), creder in self.rgy.reger.cmse.getItemIter():
rseq = coring.Seqner(qb64=snq)
if not self.registrar.complete(pre=said, sn=rseq.sn):
continue
saider = self.rgy.reger.saved.get(keys=said)
if saider is None:
continue
# Remove from this escrow
self.rgy.reger.cmse.rem(keys=(said, snq))
# place in escrow to diseminate to other if witnesser and if there is an issuee
self.rgy.reger.ccrd.put(keys=(said,), val=creder)
def complete(self, said):
return self.rgy.reger.ccrd.get(keys=(said,)) is not None
[docs]
def escrowDo(self, tymth, tock=1.0):
""" Process escrows of group multisig identifiers waiting to be completed.
Steps involve:
1. Sending local event with sig to other participants
2. Waiting for signature threshold to be met.
3. If elected and delegated identifier, send complete event to delegator
4. If delegated, wait for delegator's anchor
5. If elected, send event to witnesses and collect receipts.
6. Otherwise, wait for fully receipted event
Parameters:
tymth (function): injected function wrapper closure returned by .tymen() of
Tymist instance. Calling tymth() returns associated Tymist .tyme.
tock (float): injected initial tock value. Default to 1.0 to slow down processing
"""
# enter context
self.wind(tymth)
self.tock = tock
_ = (yield self.tock)
while True:
self.processEscrows()
yield 0.5
[docs]
def processEscrows(self):
"""
Process credential registry anchors:
"""
self.processCredentialMissingSigEscrow()
[docs]
def sendCredential(hby, hab, reger, postman, creder, recp):
""" Stream credential and all cryptographic artifacts to recipient using postman
Parameters:
hby:
hab:
reger:
postman (StreamPoster): poster to stream credential with
creder:
recp:
Returns:
"""
if isinstance(hab, GroupHab):
sender = hab.mhab.pre
else:
sender = hab.pre
sendArtifacts(hby, reger, postman, creder, recp)
sources = reger.sources(hby.db, creder)
for source, atc in sources:
sendArtifacts(hby, reger, postman, source, recp)
postman.send(serder=source, attachment=atc)
serder, prefixer, seqner, saider = reger.cloneCred(creder.said)
atc = bytearray(coring.Counter(coring.CtrDex.SealSourceTriples, count=1).qb64b)
atc.extend(prefixer.qb64b)
atc.extend(seqner.qb64b)
atc.extend(saider.qb64b)
postman.send(serder=creder, attachment=atc)
[docs]
def sendArtifacts(hby, reger, postman, creder, recp):
""" Stream credential artifacts to recipient using postman
Parameters:
hby:
reger:
postman (StreamPoster): poster to stream credential with
creder:
recp:
Returns:
"""
issr = creder.issuer
isse = creder.attrib["i"] if "i" in creder.attrib else None
regk = creder.regi
ikever = hby.db.kevers[issr]
for msg in hby.db.cloneDelegation(ikever):
serder = serdering.SerderKERI(raw=msg)
atc = msg[serder.size:]
postman.send(serder=serder, attachment=atc)
for msg in hby.db.clonePreIter(pre=issr):
serder = serdering.SerderKERI(raw=msg)
atc = msg[serder.size:]
postman.send(serder=serder, attachment=atc)
if isse != recp:
ikever = hby.db.kevers[isse]
for msg in hby.db.cloneDelegation(ikever):
serder = serdering.SerderKERI(raw=msg)
atc = msg[serder.size:]
postman.send(serder=serder, attachment=atc)
for msg in hby.db.clonePreIter(pre=isse):
serder = serdering.SerderKERI(raw=msg)
atc = msg[serder.size:]
postman.send(serder=serder, attachment=atc)
if regk is not None:
for msg in reger.clonePreIter(pre=regk):
serder = serdering.SerderKERI(raw=msg)
atc = msg[serder.size:]
postman.send(serder=serder, attachment=atc)
for msg in reger.clonePreIter(pre=creder.said):
serder = serdering.SerderKERI(raw=msg) # coring.Serder(raw=msg)
atc = msg[serder.size:]
postman.send(serder=serder, attachment=atc)
def sendRegistry(hby, reger, postman, creder, sender, recp):
issr = creder.issuer
regk = creder.regi
if regk is None:
return
ikever = hby.db.kevers[issr]
for msg in hby.db.cloneDelegation(ikever):
serder = serdering.SerderKERI(raw=msg) # coring.Serder(raw=msg)
atc = msg[serder.size:]
postman.send(serder=serder, attachment=atc)
for msg in hby.db.clonePreIter(pre=issr):
serder = serdering.SerderKERI(raw=msg) # coring.Serder(raw=msg)
atc = msg[serder.size:]
postman.send(serder=serder, attachment=atc)
for msg in reger.clonePreIter(pre=regk):
serder = serdering.SerderKERI(raw=msg) # coring.Serder(raw=msg)
atc = msg[serder.size:]
postman.send(serder=serder, attachment=atc)