diff --git a/src/keri/app/cli/commands/escrow/__init__.py b/src/keri/app/cli/commands/escrow/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/src/keri/app/cli/commands/escrow/clear.py b/src/keri/app/cli/commands/escrow/clear.py new file mode 100644 index 000000000..e715a4427 --- /dev/null +++ b/src/keri/app/cli/commands/escrow/clear.py @@ -0,0 +1,57 @@ +# -*- encoding: utf-8 -*- +""" +KERI +keri.kli.commands.escrow module + +""" +import argparse + +from hio import help +from hio.base import doing +from keri.app.cli.common import existing +from keri.vdr import viring + +logger = help.ogler.getLogger() + +parser = argparse.ArgumentParser(description='Clear escrows') +parser.set_defaults(handler=lambda args: handler(args), + transferable=True) +parser.add_argument('--name', '-n', help='keystore name and file location of KERI keystore', required=True) +parser.add_argument('--base', '-b', help='additional optional prefix to file location of KERI keystore', + required=False, default="") +parser.add_argument('--passcode', '-p', help='21 character encryption passcode for keystore (is not saved)', + dest="bran", default=None) # passcode => bran +parser.add_argument('--force', '-f', action="store_true", required=False, + help='True means perform clear without prompting the user') + + +def handler(args): + if not args.force: + print() + print("This command will clear all escrows and is not reversible.") + print() + yn = input("Are you sure you want to continue? [y|N]: ") + + if yn not in ("y", "Y"): + print("...exiting") + return [] + + kwa = dict(args=args) + return [doing.doify(clear, **kwa)] + + +def clear(tymth, tock=0.0, **opts): + """ Command line clear handler + """ + _ = (yield tock) + args = opts["args"] + name = args.name + base = args.base + bran = args.bran + logger.setLevel("INFO") + + with existing.existingHby(name=name, base=base, bran=bran) as hby: + hby.db.clearEscrows() + reger = viring.Reger(name=hby.name, db=hby.db, temp=False) + reger.clearEscrows() + diff --git a/src/keri/app/cli/commands/escrow/list.py b/src/keri/app/cli/commands/escrow/list.py new file mode 100644 index 000000000..d0468923c --- /dev/null +++ b/src/keri/app/cli/commands/escrow/list.py @@ -0,0 +1,279 @@ +# -*- encoding: utf-8 -*- +""" +KERI +keri.kli.commands.escrow module + +""" +import argparse +import json + +from hio import help +from hio.base import doing + +from keri.core import eventing +from keri.app.cli.common import existing +from keri.db import dbing +from keri.kering import ConfigurationError +from keri.vdr import viring + +logger = help.ogler.getLogger() + +parser = argparse.ArgumentParser(description='Views events in escrow state.') +parser.set_defaults(handler=lambda args: handler(args), + transferable=True) +parser.add_argument('--name', '-n', help='keystore name and file location of KERI keystore', required=True) +parser.add_argument('--base', '-b', help='additional optional prefix to file location of KERI keystore', + required=False, default="") +parser.add_argument('--passcode', '-p', help='21 character encryption passcode for keystore (is not saved)', + dest="bran", default=None) # passcode => bran + +parser.add_argument("--escrow", "-e", help="show values for one specific escrow", default=None) + + +def handler(args): + """ Command line escrow handler + + """ + kwa = dict(args=args) + return [doing.doify(escrows, **kwa)] + + +def escrows(tymth, tock=0.0, **opts): + _ = (yield tock) + + args = opts["args"] + name = args.name + base = args.base + bran = args.bran + escrow = args.escrow + + try: + with existing.existingHby(name=name, base=base, bran=bran) as hby: + escrows = dict() + + # KEL / Baser escrows + + if (not escrow) or escrow == "unverified-receipts": + count = 0 + for key, _ in hby.db.getUreItemIter(): + count += 1 + escrows["unverified-receipts"] = count + + if (not escrow) or escrow == "verified-receipts": + count = 0 + for key, _ in hby.db.getVreItemIter(): + count += 1 + escrows["verified-receipts"] = count + + if (not escrow) or escrow == "partially-signed-events": + pses = list() + key = ekey = b'' # both start same. when not same means escrows found + while True: # break when done + for ekey, edig in hby.db.getPseItemIter(key=key): + pre, sn = dbing.splitSnKey(ekey) # get pre and sn from escrow item + + try: + pses.append(eventing.loadEvent(hby.db, pre, edig)) + except ValueError as e: + raise e + + if ekey == key: # still same so no escrows found on last while iteration + break + key = ekey # setup next while iteration, with key after ekey + + escrows["partially-signed-events"] = pses + + if (not escrow) or escrow == "partially-witnessed-events": + pwes = list() + key = ekey = b'' # both start same. when not same means escrows found + while True: # break when done + for ekey, edig in hby.db.getPweItemIter(key=key): + pre, sn = dbing.splitSnKey(ekey) # get pre and sn from escrow item + + try: + pwes.append(eventing.loadEvent(hby.db, pre, edig)) + except ValueError as e: + raise e + + if ekey == key: # still same so no escrows found on last while iteration + break + key = ekey # setup next while iteration, with key after ekey + + escrows["partially-witnessed-events"] = pwes + + if (not escrow) or escrow == "unverified-event-indexed-couples": + count = 0 + for key, _ in hby.db.getUweItemIter(): + count += 1 + escrows["unverified-event-indexed-couples"] = count + + if (not escrow) or escrow == "out-of-order-events": + oots = list() + key = ekey = b'' # both start same. when not same means escrows found + while True: + for ekey, edig in hby.db.getOoeItemIter(key=key): + pre, sn = dbing.splitSnKey(ekey) # get pre and sn from escrow item + + try: + oots.append(eventing.loadEvent(hby.db, pre, edig)) + except ValueError as e: + raise e + + if ekey == key: # still same so no escrows found on last while iteration + break + key = ekey # setup next while iteration, with key after ekey + + escrows["out-of-order-events"] = oots + + if (not escrow) or escrow == "likely-duplicitous-events": + ldes = list() + key = ekey = b'' # both start same. when not same means escrows found + while True: # break when done + for ekey, edig in hby.db.getLdeItemIter(key=key): + pre, sn = dbing.splitSnKey(ekey) # get pre and sn from escrow item + + try: + ldes.append(eventing.loadEvent(hby.db, pre, edig)) + except ValueError as e: + raise e + + if ekey == key: # still same so no escrows found on last while iteration + break + key = ekey # setup next while iteration, with key after ekey + + escrows["likely-duplicitous-events"] = ldes + + if (not escrow) or escrow == "query-not-found": + count = 0 + for key, _ in hby.db.getQnfItemsNextIter(): + count += 1 + escrows["query-not-found"] = count + + if (not escrow) or escrow == "partially-delegated-events": + count = 0 + for key, _ in hby.db.getPdesItemsNextIter(): + count += 1 + escrows["partially-delegated-events"] = count + + if (not escrow) or escrow == "reply": + count = 0 + for key, _ in hby.db.rpes.getItemIter(): + count += 1 + escrows["reply"] = count + + if (not escrow) or escrow == "failed-oobi": + count = 0 + for key, _ in hby.db.eoobi.getItemIter(): + count += 1 + escrows["failed-oobi"] = count + + if (not escrow) or escrow == 'group-partial-witness': + count = 0 + for key, _ in hby.db.gpwe.getItemIter(): + count += 1 + escrows["group-partial-witness"] = count + + if (not escrow) or escrow == 'group-delegate': + count = 0 + for key, _ in hby.db.gdee.getItemIter(): + count += 1 + escrows["group-delegate"] = count + + if (not escrow) or escrow == 'delegated-partial-witness': + count = 0 + for key, _ in hby.db.dpwe.getItemIter(): + count += 1 + escrows["delegated-partial-witness"] = count + + if (not escrow) or escrow == 'group-partial-signed': + count = 0 + for key, _ in hby.db.gpse.getItemIter(): + count += 1 + escrows["group-partial-signed"] = count + + if (not escrow) or escrow == 'exchange-partial-signed': + count = 0 + for key, _ in hby.db.epse.getItemIter(): + count += 1 + escrows["exchange-partial-signed"] = count + + if (not escrow) or escrow == 'delegated-unanchored': + count = 0 + for key, _ in hby.db.dune.getItemIter(): + count += 1 + escrows["delegated-unanchored"] = count + + # TEL / Reger escrows + reger = viring.Reger(name=hby.name, db=hby.db, temp=False) + + if (not escrow) or escrow == 'tel-out-of-order': + count = 0 + for key, _ in reger.getOotItemIter(): + count += 1 + escrows["tel-out-of-order"] = count + + if (not escrow) or escrow == 'tel-partially-witnessed': + count = 0 + for key, _ in reger.getAllItemIter(reger.twes): + count += 1 + escrows["tel-partially-witnessed"] = count + + if (not escrow) or escrow == 'tel-anchorless': + count = 0 + for key, _ in reger.getAllItemIter(reger.taes): + count += 1 + escrows["tel-anchorless"] = count + + if (not escrow) or escrow == "missing-registry-escrow": + creds = list() + for (said,), dater in reger.mre.getItemIter(): + creder, *_ = reger.cloneCred(said) + creds.append(creder.sad) + + escrows["missing-registry-escrow"] = creds + + if (not escrow) or escrow == "broken-chain-escrow": + creds = list() + for (said,), dater in reger.mce.getItemIter(): + creder, *_ = reger.cloneCred(said) + creds.append(creder.sad) + + escrows["broken-chain-escrow"] = creds + + if (not escrow) or escrow == "missing-schema-escrow": + creds = list() + for (said,), dater in reger.mse.getItemIter(): + creder, *_ = reger.cloneCred(said) + creds.append(creder.sad) + + escrows["missing-schema-escrow"] = creds + + if (not escrow) or escrow == 'tel-missing-signature': + count = 0 + for key, _ in reger.cmse.getItemIter(): + count += 1 + escrows["tel-missing-signature"] = count + + if (not escrow) or escrow == 'tel-partial-witness-escrow': + count = 0 + for (regk, snq), (prefixer, seqner, saider) in reger.tpwe.getItemIter(): + count += 1 + escrows["tel-partial-witness-escrow"] = count + + if (not escrow) or escrow == 'tel-multisig': + count = 0 + for key, _ in reger.tmse.getItemIter(): + count += 1 + escrows["tel-multisig"] = count + + if (not escrow) or escrow == 'tel-event-dissemination': + count = 0 + for key, _ in reger.tede.getItemIter(): + count += 1 + escrows["tel-event-dissemination"] = count + + print(json.dumps(escrows, indent=2)) + + except ConfigurationError as e: + print(f"identifier prefix for {name} does not exist, incept must be run first", ) + return -1 diff --git a/src/keri/app/cli/kli.py b/src/keri/app/cli/kli.py index a0bde194f..a3bd13631 100644 --- a/src/keri/app/cli/kli.py +++ b/src/keri/app/cli/kli.py @@ -4,7 +4,7 @@ """ import multicommand -from keri import help +from hio import help from keri.app import directing from keri.app.cli import commands diff --git a/src/keri/db/basing.py b/src/keri/db/basing.py index de29cefa1..a95fe9572 100644 --- a/src/keri/db/basing.py +++ b/src/keri/db/basing.py @@ -37,6 +37,7 @@ import keri from . import dbing, koming, subing +from .dbing import splitKey, dgKey from .. import kering from ..core import coring, eventing, parsing, serdering @@ -1155,6 +1156,80 @@ def migrate(self): self.version = keri.__version__ + def clearEscrows(self): + """ + Clear all escrows + """ + count = 0 + for (k, _) in self.getUreItemIter(): + count += 1 + self.delUres(key=k) + logger.info(f"KEL: Cleared {count} unverified receipt escrows") + + count = 0 + for (k, _) in self.getVreItemIter(): + count += 1 + self.delVres(key=k) + logger.info(f"KEL: Cleared {count} verified receipt escrows") + + count = 0 + for (k, _) in self.getPseItemIter(): + count += 1 + self.delPses(key=k) + logger.info(f"KEL: Cleared {count} partially signed escrows") + + count = 0 + for (k, _) in self.getPweItemIter(): + count += 1 + self.delPwes(key=k) + logger.info(f"KEL: Cleared {count} partially witnessed escrows") + + count = 0 + for (k, _) in self.getUweItemIter(): + count += 1 + self.delUwes(key=k) + logger.info(f"KEL: Cleared {count} unverified event indexed escrowed couples") + + count = 0 + for (k, _) in self.getOoeItemIter(): + count += 1 + self.delOoes(key=k) + logger.info(f"KEL: Cleared {count} out of order escrows") + + count = 0 + for (k, _) in self.getLdeItemIter(): + count += 1 + self.delLdes(key=k) + logger.info(f"KEL: Cleared {count} likely duplicitous escrows") + + count = 0 + for ekey, edig in self.getQnfItemsNextIter(): + count += 1 + pre, _ = splitKey(ekey) + self.delQnf(dgKey(pre, edig), edig) + logger.info(f"KEL: Cleared {count} query not found escrows") + + count = 0 + for (key, on, val) in self.getPdesItemsNextIter(): + count += 1 + self.delPde(key=key) + logger.info(f"KEL: Cleared {count} partially delegated key event escrows") + + for name, escrow, desc in [ + ('rpes', self.rpes, 'reply escrows'), + ('eoobi', self.eoobi, 'failed, retryable OOBI escrow'), + ('gpwe', self.gpwe, 'group partial witness escrow'), + ('gdee', self.gdee, 'group delegate escrow'), + ('dpwe', self.dpwe, 'delegated partial witness escrow'), + ('gpse', self.gpse, 'group partial signature escrow'), + ('epse', self.epse, 'exchange partial signature escrow'), + ('dune', self.dune, 'delegated unanchored escrow'),]: + count = 0 + for (k, _) in escrow.getItemIter(): + count += 1 + escrow.trim() + logger.info(f"KEL: Cleared {count} escrows from ({name.ljust(5)}): {desc}") + @property def current(self): """ Current property determines if we are at the current database migration state. @@ -2139,6 +2214,20 @@ def getUreLast(self, key): """ return self.getIoValLast(self.ures, key) + def getUreItemIter(self, key=b''): + """ + Use sgKey() + Return iterator of partial signed escrowed event triple items at next + key after key. + Items is (key, val) where proem has already been stripped from val + val is triple dig+pre+cig + If key is b'' empty then returns dup items at first key. + If skip is False and key is not b'' empty then returns dup items at key + Raises StopIteration Error when empty + Duplicates are retrieved in insertion order. + """ + return self.getTopIoDupItemIter(self.ures, key) + def getUreItemsNext(self, key=b'', skip=True): """ Use snKey() @@ -2306,6 +2395,20 @@ def getVreLast(self, key): """ return self.getIoValLast(self.vres, key) + def getVreItemIter(self, key=b''): + """ + Use sgKey() + Return iterator of partial signed escrowed event quintuple items at next + key after key. + Items is (key, val) where proem has already been stripped from val + val is Quinlet is edig + spre + ssnu + sdig +sig + If key is b'' empty then returns dup items at first key. + If skip is False and key is not b'' empty then returns dup items at key + Raises StopIteration Error when empty + Duplicates are retrieved in insertion order. + """ + return self.getTopIoDupItemIter(self.vres, key) + def getVreItemsNext(self, key=b'', skip=True): """ Use snKey() @@ -2536,6 +2639,18 @@ def getPseLast(self, key): """ return self.getIoValLast(self.pses, key) + def getPseItemIter(self, key=b''): + """ + Use sgKey() + Return iterator of partial signed escrowed event dig items at next key after key. + Items is (key, val) where proem has already been stripped from val + If key is b'' empty then returns dup items at first key. + If skip is False and key is not b'' empty then returns dup items at key + Raises StopIteration Error when empty + Duplicates are retrieved in insertion order. + """ + return self.getTopIoDupItemIter(self.pses, key) + def getPseItemsNext(self, key=b'', skip=True): """ Use snKey() @@ -2670,6 +2785,18 @@ def getPweLast(self, key): """ return self.getIoValLast(self.pwes, key) + def getPweItemIter(self, key=b''): + """ + Use sgKey() + Return iterator of partial witnessed escrowed event dig items at next key after key. + Items is (key, val) where proem has already been stripped from val + If key is b'' empty then returns dup items at first key. + If skip is False and key is not b'' empty then returns dup items at key + Raises StopIteration Error when empty + Duplicates are retrieved in insertion order. + """ + return self.getTopIoDupItemIter(self.pwes, key) + def getPweItemsNext(self, key=b'', skip=True): """ Use snKey() @@ -2774,6 +2901,20 @@ def getUweLast(self, key): """ return self.getIoValLast(self.uwes, key) + def getUweItemIter(self, key=b''): + """ + Use sgKey() + Return iterator of partial signed escrowed receipt couple items at next + key after key. + Items is (key, val) where proem has already been stripped from val + val is couple edig+wig + If key is b'' empty then returns dup items at first key. + If skip is False and key is not b'' empty then returns dup items at key + Raises StopIteration Error when empty + Duplicates are retrieved in insertion order. + """ + return self.getTopIoDupItemIter(self.uwes, key) + def getUweItemsNext(self, key=b'', skip=True): """ Use snKey() @@ -2868,6 +3009,18 @@ def getOoeLast(self, key): """ return self.getIoValLast(self.ooes, key) + def getOoeItemIter(self, key=b''): + """ + Use sgKey() + Return iterator of out of order escrowed event dig items at next key after key. + Items is (key, val) where proem has already been stripped from val + If key is b'' empty then returns dup items at first key. + If skip is False and key is not b'' empty then returns dup items at key + Raises StopIteration Error when empty + Duplicates are retrieved in insertion order. + """ + return self.getTopIoDupItemIter(self.ooes, key) + def getOoeItemsNext(self, key=b'', skip=True): """ Use snKey() @@ -2983,6 +3136,9 @@ def getQnfItemsNextIter(self, key=b'', skip=True): """ return self.getIoItemsNextIter(self.qnfs, key, skip) + def getPdesItemsNextIter(self, key=b'', skip=True): + return self.getOnIoDupItemIter(self.pdes, key, skip) + def cntQnfs(self, key): """ Use snKey() @@ -3124,6 +3280,18 @@ def getLdeLast(self, key): """ return self.getIoValLast(self.ldes, key) + def getLdeItemIter(self, key=b''): + """ + Use sgKey() + Return iterator of likely duplicitous escrowed event dig items at next key after key. + Items is (key, val) where proem has already been stripped from val + If key is b'' empty then returns dup items at first key. + If skip is False and key is not b'' empty then returns dup items at key + Raises StopIteration Error when empty + Duplicates are retrieved in insertion order. + """ + return self.getTopIoDupItemIter(self.ldes, key) + def getLdeItemsNext(self, key=b'', skip=True): """ Use snKey() diff --git a/src/keri/db/dbing.py b/src/keri/db/dbing.py index 532fe8a7c..f32975943 100644 --- a/src/keri/db/dbing.py +++ b/src/keri/db/dbing.py @@ -82,7 +82,7 @@ def dgKey(pre, dig): return (b'%s.%s' % (pre, dig)) -def onKey(pre, sn): +def onKey(pre, sn, *, sep=b'.'): """ Returns bytes DB key from concatenation with '.' of qualified Base64 prefix bytes pre and int ordinal number of event, such as sequence number or first @@ -90,7 +90,7 @@ def onKey(pre, sn): """ if hasattr(pre, "encode"): pre = pre.encode("utf-8") # convert str to bytes - return (b'%s.%032x' % (pre, sn)) + return (b'%s%s%032x' % (pre, sep, sn)) snKey = onKey # alias so intent is clear, sn vs fn fnKey = onKey # alias so intent is clear, sn vs fn @@ -137,7 +137,7 @@ def splitKey(key, sep=b'.'): return tuple(splits) -def splitKeyON(key): +def splitKeyON(key, *, sep=b'.'): """ Returns list of pre and int on from key Accepts either bytes or str key @@ -145,9 +145,12 @@ def splitKeyON(key): """ if isinstance(key, memoryview): key = bytes(key) - pre, on = splitKey(key) + top, on = splitKey(key, sep=sep) on = int(on, 16) - return (pre, on) + return (top, on) + +splitSnKey = splitKeyON # alias so intent is clear, sn vs fn; backport of 1.2.x alias +splitFnKey = splitKeyON # alias so intent is clear, sn vs fn; backport of 1.2.x alias splitKeySN = splitKeyON # alias so intent is clear, sn vs fn splitKeyFN = splitKeyON # alias so intent is clear, sn vs fn @@ -662,6 +665,71 @@ def delTopVal(self, db, key=b''): ckey, cval = cursor.item() # cursor now at next item after deleted return result + # ported from OnIoDupSuber + def getOnIoDupItemIter(self, db, key=b'', on=0, *, sep=b'.'): + """ + Returns iterator of triples (key, on, val), at each key over all ordinal + numbered keys with same key + sep + on in db. Values are sorted by + onKey(key, on) where on is ordinal number int and key is prefix sans on. + Values duplicates are sorted internally by hidden prefixed insertion order + proem ordinal + Returned items are triples of (key, on, val) + when key is empty then retrieves whole db + + Raises StopIteration Error when empty. + + Returns: + items (Iterator[(key, on, val)]): triples of key, on, val + + Parameters: + db (subdb): named sub db in lmdb + key (bytes): key within sub db's keyspace plus trailing part on + when key is empty then retrieves whole db + on (int): ordinal number at which to initiate retrieval + sep (bytes): separator character for split + """ + for key, on, val in self.getOnItemIter(db=db, key=key, on=on, sep=sep): + val = val[33:] # strip proem + yield (key, on, val) + + # ported from OnSuberBase + def getOnItemIter(self, db, key=b'', on=0, *, sep=b'.'): + """ + Returns iterator of triples (key, on, val), at each key over all ordinal + numbered keys with same key + sep + on in db. Values are sorted by + onKey(key, on) where on is ordinal number int and key is prefix sans on. + Returned items are triples of (key, on, val) + When dupsort==true then duplicates are included in items since .iternext + includes duplicates. + when key is empty then retrieves whole db + + Raises StopIteration Error when empty. + + Returns: + items (Iterator[(key, on, val)]): triples of key, on, val with same + key but increments of on beginning with on + + Parameters: + db (subdb): named sub db in lmdb + key (bytes): key within sub db's keyspace plus trailing part on + when key is empty then retrieves whole db + on (int): ordinal number at which to initiate retrieval + sep (bytes): separator character for split + """ + with self.env.begin(db=db, write=False, buffers=True) as txn: + cursor = txn.cursor() + if key: # not empty + onkey = onKey(key, on, sep=sep) # start replay at this enty 0 is earliest + else: # empty + onkey = key + if not cursor.set_range(onkey): # moves to val at key >= onkey + return # no values end of db raises StopIteration + + for ckey, cval in cursor.iternext(): # get key, val at cursor + ckey, cn = splitKeyON(ckey, sep=sep) + if key and not ckey == key: + break + yield (ckey, cn, cval) # For subdbs with no duplicate values allowed at each key. (dupsort==False) # and use keys with ordinal as monotonically increasing number part @@ -1639,6 +1707,51 @@ def cntIoVals(self, db, key): " or wrong DUPFIXED size. ref) lmdb.BadValsizeError") return count + def getTopIoDupItemIter(self, db, top=b''): + """ + Iterates over top branch of db given by key of IoDup items where each value + has 33 byte insertion ordinal number proem (prefixed) with separator. + Automagically removes (strips) proem before returning items. + + Assumes DB opened with dupsort=True + + Returns: + items (abc.Iterator): iterator of (full key, val) tuples of all + dup items over a branch of the db given by top key where returned + full key is full database key for val not truncated top key. + Item is (key, val) with proem stripped from val stored in db. + If key = b'' then returns list of dup items for all keys in db. + + + Because cursor.iternext() advances cursor after returning item its safe + to delete the item within the iteration loop. curson.iternext() works + for both dupsort==False and dupsort==True + + Raises StopIteration Error when empty. + + Parameters: + db (lmdb._Database): instance of named sub db with dupsort==False + top (bytes): truncated top key, a key space prefix to get all the items + from multiple branches of the key space. If top key is + empty then gets all items in database + + Duplicates at a given key preserve insertion order of duplicate. + Because lmdb is lexocographic an insertion ordering proem is prepended to + all values that makes lexocographic order that same as insertion order. + + Duplicates are ordered as a pair of key plus value so prepending proem + to each value changes duplicate ordering. Proem is 33 characters long. + With 32 character hex string followed by '.' for essentiall unlimited + number of values which will be limited by memory. + + With prepended proem ordinal must explicity check for duplicate values + before insertion. Uses a python set for the duplicate inclusion test. + Set inclusion scales with O(1) whereas list inclusion scales with O(n). + """ + for top, val in self.getTopItemIter(db=db, key=top): + val = val[33:] # strip proem + yield (top, val) + def delIoVals(self, db, key): """ diff --git a/src/keri/vdr/viring.py b/src/keri/vdr/viring.py index 96eec603a..bafc2b749 100644 --- a/src/keri/vdr/viring.py +++ b/src/keri/vdr/viring.py @@ -22,6 +22,10 @@ from ..vc import proving from ..vdr import eventing +from hio import help + +logger = help.ogler.getLogger() + class rbdict(dict): """ Reger backed read through cache for registry state @@ -384,6 +388,42 @@ def reopen(self, **kwa): return self.env + def clearEscrows(self): + """Clear credential event escrows""" + # self.oots, self.twes, self.taes + count = 0 + for (k, _) in self.getOotItemIter(): + count += 1 + self.delOot(k) + logger.info(f"TEL: Cleared {count} out of order escrows.") + + count = 0 + for (k, ) in self.getAllItemIter(self.twes): + count += 1 + self.delTwe(k) + logger.info(f"TEL: Cleared {count} partially witnessed escrows.") + + count = 0 + for (k, _) in self.getAllItemIter(self.taes): + count += 1 + self.delTae(k) + logger.info(f"TEL: Cleared {count} anchorless escrows.") + + for name, sub, desc in [ + ( 'mre', self.mre, 'missing registry escrows'), + ( 'mce', self.mce, 'broken chain escrows'), + ( 'mse', self.mse, 'missing schema escrows'), + ('cmse', self.cmse, 'missing signature escrows'), + ('tpwe', self.tpwe, 'partial witness escrows'), + ('tmse', self.tmse, 'multisig escrows'), + ('tede', self.tede, 'event dissemination escrows'), + ]: + count = 0 + for (k, _) in sub.getItemIter(): + count += 1 + sub.trim() + logger.info(f"TEL: Cleared {count} escrows from ({name.ljust(5)}): {desc}") + def cloneCreds(self, saids, db): """ Returns fully expanded credential with chained credentials attached.