Source code for keri.db.basing

# -*- encoding: utf-8 -*-
"""
keri.db.dbing module


import lmdb
db = lmdb.open("/tmp/keri_db_setup_test")
db.max_key_size()
511

The dupsort, integerkey, integerdup, and dupfixed parameters are ignored
if the database already exists.
The state of those settings are persistent and immutable per database.
See _Database.flags() to view the state of those options for an opened database.
A consequence of the immutability of these flags is that the default non-named
database will never have these flags set.

So only need to set dupsort first time opened each other opening does not
need to call it
"""

import os
import shutil
from collections import namedtuple
from contextlib import contextmanager
from dataclasses import dataclass, asdict, field
import json


import cbor2 as cbor
import msgpack
import lmdb
from ordered_set import OrderedSet as oset

from hio.base import doing

from . import dbing, koming, subing
from .. import kering

from ..core import coring, eventing, parsing, serdering

from .. import help
from ..help import helping


logger = help.ogler.getLogger()


[docs] class dbdict(dict): """ Subclass of dict that has db as attribute and employs read through cache from db Baser.stts of kever states to reload kever from state in database when not found in memory as dict item. """ __slots__ = ('db') # no .__dict__ just for db reference def __init__(self, *pa, **kwa): super(dbdict, self).__init__(*pa, **kwa) self.db = None def __getitem__(self, k): try: return super(dbdict, self).__getitem__(k) except KeyError as ex: if not self.db: raise ex # reraise KeyError if (ksr := self.db.states.get(keys=k)) is None: raise ex # reraise KeyError try: kever = eventing.Kever(state=ksr, db=self.db) except kering.MissingEntryError: # no kel event for keystate raise ex # reraise KeyError self.__setitem__(k, kever) return kever def __contains__(self, k): if not super(dbdict, self).__contains__(k): try: self.__getitem__(k) return True except KeyError: return False else: return True
[docs] def get(self, k, default=None): """Override of dict get method Parameters: k (str): key for dict default: default value to return if not found Returns: kever: converted from underlying dict or database """ if not super(dbdict, self).__contains__(k): return default else: return self.__getitem__(k)
[docs] @dataclass class RawRecord: """RawRecord is base class for dataclasses that provides private utility methods for representing the dataclass as some other format like dict, json bytes, cbor bytes, mgpk bytes as a raw format. Typically uses case is to transform dataclass into dict or serialization of its transformation into dict so that it can be included in messages or stored in a database. """ @classmethod def _fromdict(cls, d: dict): """returns instance of clas initialized from dict d """ return helping.datify(cls, d) def __iter__(self): return iter(asdict(self)) def _asdict(self): """Returns dict version of record""" return helping.dictify(self) def _asjson(self): """Returns json bytes version of record""" return json.dumps(self._asdict(), separators=(",", ":"), ensure_ascii=False).encode("utf-8") def _ascbor(self): """Returns cbor bytes version of record""" return cbor.dumps(self._asdict()) def _asmgpk(self): """Returns mgpk bytes version of record""" return msgpack.dumps(self._asdict())
[docs] @dataclass class StateEERecord(RawRecord): """ Corresponds to StateEstEvent namedtuple used as sub record in KeyStateRecord for latest establishment event associated with current key state Attributes: s (str): sequence number of latest est evt lowercase hex no leading zeros d (str): SAID qb64 of latest est evt br (list[str]): backer aids qb64 remove list (cuts) from latest est event ba (list[str]): backer aids qb64 add list (adds) from latest est event """ s: str ='0' # sequence number of latest event in KEL as hex str d: str ='' # latest event digest qb64 br: list = field(default_factory=list) # backer AID qb64 remove (cut) list ba: list = field(default_factory=list) # backer AID qb64 add list
[docs] @dataclass class KeyStateRecord(RawRecord): # baser.state """ Key State information keyed by Identifier Prefix of associated KEL. For local AIDs that correspond to Habs this is the Hab AID. (see baser.state at 'stts') Attributes: vn (list[int]): version number [major, minor] i (str): identifier prefix qb64 s (str): sequence number of latest event in KEL as hex str p (str): prior event digest qb64 d (str): latest event digest qb64 f (str): first seen ordinal number of latest event in KEL as hex str dt (str): datetime iso-8601 of key state record update, usually now et (str): latest event packet type kt (str): signing threshold sith k (list[str]): signing keys qb64 nt (str): next prerotated threshold sith n (list[str]): pre-rotation keys qb64 bt (str): backer threshold hex num b (list[str]): backer aids qb64 c (list[str]): config traits ee (StateEERecord): instance corresponds to StateEstEvent namedtuple s = sn of latest est event as lowercase hex string no leading zeros, d = SAID digest qb64 of latest establishment event br = backer (witness) remove list (cuts) from latest est event ba = backer (witness) add list (adds) from latest est event di (str): delegator aid qb64 or empty str if not delegated Note: the seal anchor dict 'a' field is not included in the state notice because it may be verbose and would impede the main purpose of a notic which is to trigger the download of the latest events, which would include the anchored seals. """ vn: list[int] = field(default_factory=list) # version number [major, minor] round trip serializable i: str ='' # identifier prefix qb64 s: str ='0' # sequence number of latest event in KEL as hex str p: str ='' # prior event digest qb64 d: str ='' # latest event digest qb64 f: str ='0' # first seen ordinal number of latest event in KEL as hex str dt: str = '' # datetime of creation of state et: str = '' # latest evt packet type (ilk) kt: str = '0' # signing threshold sith k: list[str] = field(default_factory=list) # signing key list qb64 nt: str = '0' # next rotation threshold nsith n: list[str] = field(default_factory=list) # next rotation key digest list qb64 bt: str = '0' # backer threshold hex num str b: list = field(default_factory=list) # backer AID list qb64 c: list[str] = field(default_factory=list) # config trait list ee: StateEERecord = field(default_factory=StateEERecord) di: str = '' # delegator aid qb64 if any otherwise empty '' str
[docs] @dataclass class HabitatRecord: # baser.habs """ Habitat application state information keyed by habitat name (baser.habs) Attributes: hid (str): identifier prefix of hab qb64 mid (str | None): group member identifier qb64 when hid is group smids (list | None): group signing member identifiers qb64 when hid is group rmids (list | None): group signing member identifiers qb64 when hid is group watchers: (list[str]) = list of id prefixes qb64 of watchers """ hid: str # hab own identifier prefix qb64 mid: str | None = None # group member identifier qb64 when hid is group smids: list | None = None # group signing member ids when hid is group rmids: list | None = None # group rotating member ids when hid is group sid: str | None = None # Signify identifier qb64 when hid is Signify watchers: list[str] = field(default_factory=list) # id prefixes qb64 of watchers
[docs] @dataclass class TopicsRecord: # baser.tops """ Tracks the last message topic index retrieved from the witness mailbox Database Key is the identifier prefix of the witness that is storing events in a mailbox. (baser.tops) """ topics: dict
[docs] @dataclass class OobiQueryRecord: # information for responding to OOBI query """ Keyed by cid in oobis field of HabitatRecord (oobiq). Determines which endpoints are allowed as responses to oobi query for cid cid is aid of controller with endpoint. role is functional role of endpoint provider eids are aids of endpoint providers for a role. schemes are url schemes of endpoint url This record acts as a constraint tree with path cid.role.eid.scheme. Partial path specification permits the resultant subtree. Full path specification permits only the leaf. No record could be either all allowed or none allowed depending on the habitat type or function. Defaults rules for each pairing of querier and replier. This functionality is aspirational for now. It is likely that we need an endpoint identity constraint graph to properly model the endpoint relationship permissing constraint structure. For now we just operate with a promiscuous constraint policy for endpoint discovery . Usage: oobiqs: dict[str, OobiQueryRecord] = field(default_factory=dict) """ cid: str = None # qb64 role: str = None # one of kering.Roles None is any or all eids: list[str] = field(default_factory=list) # of qb64 empty is any scheme: str = None # one of kering.Schemes None is any or all def __iter__(self): return iter(asdict(self))
[docs] @dataclass class OobiRecord: """ Keyed by CID (AID) and role, the minimum information needed for any OOBI """ oobialias: str = None said: str = None cid: str = None eid: str = None role: str = None date: str = None state: str = None urls: list = None
[docs] @dataclass class EndpointRecord: # baser.ends """ Service Endpoint ID (SEID) Record with fields and keys to manage endpoints by cid,role, and eid. Serves as aggregation mechanism for authorization and other functions such as UX naming with regards the endpoint. The namespace is a tree of branches with each leaf at a specific (cid, role, eid). Retrieval by branch returns groups of leaves as appropriate for a cid braanch or cid.role branch. Database Keys are (cid, role, eid) where cid is attributable controller identifier (qb64 prefix) that has role(s) such as watcher, witness etc and eid is the identifier of the controller acting in a role i.e. watcher identifier. Attributes: allowed (bool): AuthZ via reply message True means eid is allowed as controller of endpoint in role False means eid is disallowed as conroller of endpint in role None means eid is neither allowed or disallowed (no reply msg) enabled (bool): AuthZ via expose message True means eid is enabled as controller of endpoint in role False means eid is disenabled as conroller of endpint in role None means eid is neither enabled or disenabled (no expose msg) name (str): user fieldly name for eid in role An end authorization reply message is required from which the field values for this record are extracted. A routes of /end/role/eid/add /end/role/eid/cut Uses add-cut model with allowed field allowed==True eid is allowed (add) as endpoint provider for cid at role and name allowed==False eid is disallowed (cut) as endpoint provider for cid at role and name { "v" : "KERI10JSON00011c_", "t" : "rep", "d": "EZ-i0d8JZAoTNZH3ULaU6JR2nmwyvYAfSVPzhzS6b5CM", "dt": "2020-08-22T17:50:12.988921+00:00", "r" : "/end/role/add", "a" : { "cid": "EaU6JR2nmwyZ-i0d8JZAoTNZH3ULvYAfSVPzhzS6b5CM", "role": "watcher", # one of kering.Roles "eid": "BrHLayDN-mXKv62DAjFLX1_Y5yEUe0vA9YPe_ihiKYHE", } } { "v" : "KERI10JSON00011c_", "t" : "rep", "d": "EZ-i0d8JZAoTNZH3ULaU6JR2nmwyvYAfSVPzhzS6b5CM", "dt": "2020-08-22T17:50:12.988921+00:00", "r" : "/end/role/cut", "a" : { "cid": "EaU6JR2nmwyZ-i0d8JZAoTNZH3ULvYAfSVPzhzS6b5CM", "role": "watcher", # one of kering.Roles "eid": "BrHLayDN-mXKv62DAjFLX1_Y5yEUe0vA9YPe_ihiKYHE", } } An end authorization expose message provides enablement via an exposure of and anchored seal to the expose message on the authorizing KEL. """ allowed: bool = None # True eid allowed (add), False eid disallowed (cut), None neither enabled: bool = None # True eid enabled (add), False eid disenabled (cut), None neither name: str = "" # optional user friendly name of endpoint def __iter__(self): return iter(asdict(self))
[docs] @dataclass class EndAuthRecord: # nested as field value in baser.locs """ Service Endpoint Authorization Record provides cross reference field for search purposes to find authorization for endpoint provider eid. The default is controller id, cid, and a role. used to lookup authorization in end authN database with keyspace given by (cid.role.eid) where cid is the authorizing controller for the eid (endpoint id) at the given role. The cid is usually a transferable identifier with a KEL but may be non-trans. The eid is usually a nontransferable identifier when its used for roles witness or watcher but may be transferable for other roles such as controller, judge, juror, public watcher, or registrar. This is an embedded record type in a LocationRecord in the cids field """ cid: str = "" # identifier prefix of controller that authorizes endpoint roles: list[str] = field(default_factory=list) # str endpoint roles such as watcher, witness etc def __iter__(self): return iter(asdict(self))
[docs] @dataclass class LocationRecord: # baser.locs """ Service Endpoint Record with url for endpoint of a given scheme The eid is usually a nontransferable identifier when its used for roles witness or watcher but may be transferable for other roles such as controller, judge, juror, public watcher, or registrar. Database Keys are (eid, scheme) where eid is service endpoint identifier (qb64 prefix) and scheme is the url protocol scheme (tcp, https). A loc reply message is required from which the values of this database record are extracted. route is /loc/scheme Uses enact-anul model To nullify endpoint set url field to empty. An end authorization reply message is also required to authorize the eid as endpoint provider for cid at role. See EndpointRecord { "v" : "KERI10JSON00011c_", "t" : "rep", "d": "EZ-i0d8JZAoTNZH3ULaU6JR2nmwyvYAfSVPzhzS6b5CM", "dt": "2020-08-22T17:50:12.988921+00:00", "r" : "/loc/scheme", "a" : { "eid": "BrHLayDN-mXKv62DAjFLX1_Y5yEUe0vA9YPe_ihiKYHE", "scheme": "http", # one of kering.Schemes "url": "http://localhost:8080/watcher/wilma", } } { "v" : "KERI10JSON00011c_", "t" : "rep", "d": "EZ-i0d8JZAoTNZH3ULaU6JR2nmwyvYAfSVPzhzS6b5CM", "dt": "2020-08-22T17:50:12.988921+00:00", "r" : "/loc/scheme", "a" : { "eid": "BrHLayDN-mXKv62DAjFLX1_Y5yEUe0vA9YPe_ihiKYHE", "scheme": "http", # one of kering.Schemes "url": "", } } """ url: str # full url including host:port/path?query scheme is optional # cids: list[EndAuthRecord] = field(default_factory=list) # optional authorization record references def __iter__(self): return iter(asdict(self))
[docs] @dataclass class WellKnownAuthN: """ Each WellKnownAuthN represents a successfully resolved .well-known OOBI URL keyed by the AID of the OOBI tuple embedded in the URL """ url: str # full .well-known OOBI URL resolved dt: str # iso8601 date/time of success resolution
[docs] def openDB(*, cls=None, name="test", **kwa): """ Returns contextmanager generated by openLMDB but with Baser instance as default """ if cls == None: # can't reference class before its defined below cls = Baser return dbing.openLMDB(cls=cls, name=name, **kwa)
[docs] @contextmanager def reopenDB(db, clear=False, **kwa): """ Context manager wrapper LMDB DB instances. Repens and closes db.path and db.env LMDB Parameters: db (LMDBer): instance with LMDB environment at .env clear (bool): True means clear directory after close Usage: with reopenDB(baser) as env: env. .... """ try: db.reopen(clear=clear, **kwa) yield db.env finally: db.close(clear=clear)
[docs] class Baser(dbing.LMDBer): """ Baser sets up named sub databases with Keri Event Logs within main database Attributes: see superclass LMDBer for inherited attributes kevers (dict): Kever instances indexed by identifier prefix qb64 prefixes (OrderedSet): local prefixes corresponding to habitats for this db .evts is named sub DB whose values are serialized events dgKey DB is keyed by identifier prefix plus digest of serialized event Only one value per DB key is allowed .fels is named sub DB of first seen event log table (FEL) of digests that indexes events in first 'seen' accepted order for replay and cloning of event log. Only one value per DB key is allowed. Provides append only ordering of accepted first seen events. Uses first seen order number or fn. fnKey DB is keyed by identifier prefix plus monotonically increasing first seen order number fn. Value is digest of serialized event used to lookup event in .evts sub DB .dtss is named sub DB of datetime stamp strings in ISO 8601 format of the datetime when the event was first escrosed and then later first seen by log. Used for escrows timeouts and extended validation. dgKey DB is keyed by identifier prefix plus digest of serialized event Value is ISO 8601 datetime stamp bytes .aess is named sub DB of authorizing event source seal couples that map digest to seal source couple of authorizer's (delegator or issuer) event. Each couple is a concatenation of full qualified items, snu+dig of the authorizing (delegating or issuing) source event. dgKey Values are couples used to lookup authorizer's source event in .kels sub DB DB is keyed by identifier prefix plus digest of key event Only one value per DB key is allowed .sigs is named sub DB of fully qualified indexed event signatures dgKey DB is keyed by identifier prefix plus digest of serialized event More than one value per DB key is allowed .wigs is named sub DB of indexed witness signatures of event Witnesses always have nontransferable indetifier prefixes. The index is the offset of the witness into the witness list of the most recent establishment event wrt the receipted event. dgKey DB is keyed by identifier prefix plus digest of serialized event More than one value per DB key is allowed .rcts is named sub DB of event receipt couplets from nontransferable signers. Each couple is concatenation of fully qualified items. These are: non-transferale prefix plus non-indexed event signature by that prefix. dgKey DB is keyed by identifier prefix plus digest of serialized event More than one value per DB key is allowed .ures is named sub DB of unverified event receipt escrowed triples from non-transferable signers. Each triple is concatenation of fully qualified items. These are: receipted event digest, non-transferable receiptor identifier prefix, plus nonindexed receipt event signature by that prefix. snKey DB is keyed by receipted event controller prefix plus sn of serialized event More than one value per DB key is allowed .vrcs is named sub DB of event validator receipt quadruples from transferable signers. Each quadruple is concatenation of four fully qualified items of validator. These are: transferable prefix, plus latest establishment event sequence number plus latest establishment event digest, plus indexed event signature. When latest establishment event is multisig then there will be multiple quadruples one per signing key, each a dup at same db key. dgKey DB is keyed by identifier prefix plus digest of serialized event More than one value per DB key is allowed .vres is named sub DB of unverified event validator receipt escrowed quadruples from transferable signers. Each quadruple is concatenation of four fully qualified items of validator. These are: transferable prefix, plus latest establishment event sequence number plus latest establishment event digest, plus indexed event signature. When latest establishment event is multisig then there will be multiple quadruples one per signing key, each a dup at same db key. dgKey DB is keyed by identifier prefix plus digest of serialized event More than one value per DB key is allowed .kels is named sub DB of key event log tables that map sequence numbers to serialized event digests. snKey Values are digests used to lookup event in .evts sub DB DB is keyed by identifier prefix plus sequence number of key event More than one value per DB key is allowed .pses is named sub DB of partially signed escrowed event tables that map sequence numbers to serialized event digests. snKey Values are digests used to lookup event in .evts sub DB DB is keyed by identifier prefix plus sequence number of key event More than one value per DB key is allowed .pdes is named sub DB of partially delegated escrowed couples that map digest to seal source couple that provides source (delegator or issuer) event seal. Each couples is concatenations of full qualified items, snu+dig of authorizing (delegating or issuing) source event. dgKey Values are couples used to lookup source event in .kels sub DB DB is keyed by identifier prefix plus digest of key event Only one value per DB key is allowed .pwes is named sub DB of partially witnessed escrowed event tables that map sequence numbers to serialized event digests. snKey Values are digests used to lookup event in .evts sub DB DB is keyed by identifier prefix plus sequence number of key event More than one value per DB key is allowed .uwes is named sub DB of unverified event indexed escrowed couples from witness signers. Witnesses are from witness list of latest establishment event for the receipted event. Each couple is concatenation of fully qualified items, edig+sig where: edig is receipted event digest wig is indexed signature of that event with keypair derived from witness nontrans identifier prefix from witness list and index is offset into witness list of latest establishment event for receipted event snKey DB is keyed by receipted event controller prefix plus sn of serialized event More than one value per DB key is allowed .ooes is named sub DB of out of order escrowed event tables that map a prefix and sequence number to a set of serialized event digests. Values are digests used to lookup event in .evts, .sigs and .dtss sub DBs. snKey DB is keyed by identifier prefix plus sequence number of key event More than one value per DB key is allowed .dels is named sub DB of duplicitous event log tables that map sequence numbers to serialized event digests. snKey Values are digests used to lookup event in .evts sub DB DB is keyed by identifier prefix plus sequence number of key event More than one value per DB key is allowed .ldes is named sub DB of likely duplicitous escrowed event tables that map sequence numbers to serialized event digests. snKey Values are digests used to lookup event in .evts sub DB DB is keyed by identifier prefix plus sequence number of key event More than one value per DB key is allowed .fons is named subDB instance of MatterSuber that maps (prefix, digest) e.g. dgKey to fn value (first seen ordinal number) of the associated event. So one can lookup event digest, get its fn here and then use fn to fetch event by fn from .fels. .states (stts) is named subDB instance of SerderSuber that maps a prefix to the latest keystate for that prefix. Used by ._kevers.db for read through cache of key state to reload kevers in memory .habs is named subDB instance of Komer that maps habitat names to habitat application state. Includes habitat identifier prefix key is habitat name str value is serialized HabitatRecord dataclass .nmsp is named subDB instance of Komer that maps habitat namespaces and names to habitat application state. Includes habitat identifier prefix key is habitat namespace + b'\x00' + name str value is serialized HabitatRecord dataclass .sdts (sad date-time-stamp) named subDB instance of CesrSuber that that maps SAD SAID to Dater instance's CESR serialization of ISO-8601 datetime key = said (bytes) of sad, val = dater.qb64b .ssgs (sad trans indexed sigs) named subDB instance of CesrIoSetSuber that maps keys quadruple (saider.qb64, prefixer.qb64, seqner.q64, diger.qb64) to val Siger of trans id siganture. Where: saider is said of SAD and prefixer, seqner, and diger indicate the key state est event for signer or reply SAD. Each key may have a set of vals in insertion order one for each signer of the sad. key = join (saider.qb64b, prefixer.qb64b, seqner.qb64b, diger.qb64b) (bytes) val = siger.qb64b .scgs (sad nontrans cigs) named subDB instance of CatCesrIoSetSuber that maps said of SAD to couple (Verfer, Cigar) for nontrans signer. For nontrans qb64 of Verfer is same as Prefixer. Each key may have a set of vals in insertion order one for each nontrans signer of the sad. key = said (bytes of SAD, val = cat of (verfer.qb64, cigar.qb64b) .rpys (replys) named subDB instance of SerderSuber that maps said of reply message (versioned SAD) to serialization of that reply message. key is said bytes, val is Serder.raw bytes of reply 'rpy' message .rpes (reply escrows) named subDB instance of CesrIoSetSuber that maps routes of reply (versioned SAD) to single Saider of that reply msg. Routes such as '/end/role/' and '/loc/scheme' key is route bytes, vals = saider.qb64b of reply 'rpy' msg .eans is named subDB instance of CesrSuber with klas=Saider that maps cid.role.eid to said of reply SAD as auth: authN by controller cid of authZ that designates endpoint provider eid in role routes /end/role/add and /end/role/cut to nullify key is cid.role.eid, val = saider.qb64b of reply 'rpy' msg SAD .lans is named subDB instance of CesrSuber with klas=Saider that maps eid.scheme to said of reply SAD as auth: authN by endpoint provider eid that designates scheme for url route /loc/scheme use null url to nullify key is eid.scheme, val = saider.qb64b of reply 'rpy' msg SAD .ends is named subDB instance of Komer that maps (cid, role, eid) to attributes about endpoint authorization where: cid is controller prefix, role is endpoint role, watcher etc, and eid is controller prefix of endpoint controller watcher etc. key is cid.role.eid, value is serialized EndpointRecord dataclass .locs is named subDB instance of Komer that maps endpoint prefix eid and endpoint network location scheme to endpoint location details key is eid.scheme, val is serialized LocationRecord dataclass .tops is named subDB instance of Komer that maps Witness identifier prefix to topic index of last received mailbox message. key is witness prefix identifier value is serialized TopicsRecord dataclass .gids is named subDB instance of Komer that maps group identifier prefix to the local identifier prefix and list of remote identifier prefixes that participate in the group identifier. key is group identifier prefix value is serialized GroupIdentifier dataclass .mpids is named subDB instance of CesrIoSetSuber mapping payload SAID (of 'e' block) to the SAID of the `exn` messages is was contained in. This aggregates identical message bodies across participants in group multisig body trying to reach concensus on events or credentials. Properties: kevers (dbdict): read through cache of kevers of states for KELs in db """
[docs] def __init__(self, headDirPath=None, reopen=False, **kwa): """ Setup named sub databases. Parameters: name is str directory path name differentiator for main database When system employs more than one keri database, name allows differentiating each instance by name temp is boolean, assign to .temp True then open in temporary directory, clear on close Othewise then open persistent directory, do not clear on close headDirPath is optional str head directory pathname for main database If not provided use default .HeadDirpath mode is int numeric os dir permissions for database directory reopen (bool): True means database will be reopened by this init """ self.prefixes = oset() self._kevers = dbdict() self._kevers.db = self # assign db for read through cache of kevers super(Baser, self).__init__(headDirPath=headDirPath, reopen=reopen, **kwa)
@property def kevers(self): """ Returns .db.kevers """ return self._kevers
[docs] def reopen(self, **kwa): """ Open sub databases Notes: dupsort=True for sub DB means allow unique (key,pair) duplicates at a key. Duplicate means that is more than one value at a key but not a redundant copies a (key,value) pair per key. In other words the pair (key,value) must be unique both key and value in combination. Attempting to put the same (key,value) pair a second time does not add another copy. Duplicates are inserted in lexocographic order by value, insertion order. """ super(Baser, self).reopen(**kwa) # Create by opening first time named sub DBs within main DB instance # Names end with "." as sub DB name must include a non Base64 character # to avoid namespace collisions with Base64 identifier prefixes. self.evts = self.env.open_db(key=b'evts.') self.fels = self.env.open_db(key=b'fels.') self.dtss = self.env.open_db(key=b'dtss.') self.aess = self.env.open_db(key=b'aess.') self.sigs = self.env.open_db(key=b'sigs.', dupsort=True) self.wigs = self.env.open_db(key=b'wigs.', dupsort=True) self.rcts = self.env.open_db(key=b'rcts.', dupsort=True) self.ures = self.env.open_db(key=b'ures.', dupsort=True) self.vrcs = self.env.open_db(key=b'vrcs.', dupsort=True) self.vres = self.env.open_db(key=b'vres.', dupsort=True) self.kels = self.env.open_db(key=b'kels.', dupsort=True) self.pses = self.env.open_db(key=b'pses.', dupsort=True) self.pdes = self.env.open_db(key=b'pdes.') self.pwes = self.env.open_db(key=b'pwes.', dupsort=True) self.uwes = self.env.open_db(key=b'uwes.', dupsort=True) self.ooes = self.env.open_db(key=b'ooes.', dupsort=True) self.dels = self.env.open_db(key=b'dels.', dupsort=True) self.ldes = self.env.open_db(key=b'ldes.', dupsort=True) self.qnfs = self.env.open_db(key=b'qnfs.', dupsort=True) # events as ordered by first seen ordinals self.fons = subing.CesrSuber(db=self, subkey='fons.', klas=coring.Seqner) # Kever state made of KeyStateRecord key states self.states = koming.Komer(db=self, schema=KeyStateRecord, subkey='stts.') self.wits = subing.CesrIoSetSuber(db=self, subkey="wits.", klas=coring.Prefixer) # habitat application state keyed by habitat name, includes prefix self.habs = koming.Komer(db=self, subkey='habs.', schema=HabitatRecord, ) # habitat application state keyed by habitat namespace + b'\x00' + name, includes prefix self.nmsp = koming.Komer(db=self, subkey='nmsp.', schema=HabitatRecord, ) # SAD support datetime stamps and signatures indexed and not-indexed # all sad sdts (sad datetime serializations) maps said to date-time self.sdts = subing.CesrSuber(db=self, subkey='sdts.', klas=coring.Dater) # all sad ssgs (sad indexed signature serializations) maps SAD quadkeys # given by quadruple (saider.qb64, prefixer.qb64, seqner.q64, diger.qb64) # of reply and trans signer's key state est evt to val Siger for each # signature. self.ssgs = subing.CesrIoSetSuber(db=self, subkey='ssgs.', klas=coring.Siger) # all sad scgs (sad non-indexed signature serializations) maps SAD SAID # to couple (Verfer, Cigar) of nontrans signer of signature in Cigar # nontrans qb64 of Prefixer is same as Verfer self.scgs = subing.CatCesrIoSetSuber(db=self, subkey='scgs.', klas=(coring.Verfer, coring.Cigar)) # all reply messages. Maps reply said to serialization. Replys are # versioned sads ( with version string) so use Serder to deserialize and # use .sdts, .ssgs, and .scgs for datetimes and signatures self.rpys = subing.SerderSuber(db=self, subkey='rpys.') # all reply escrows indices of partially signed reply messages. Maps # route in reply to single (Saider,) of escrowed reply. # Routes such as /end/role /loc/schema self.rpes = subing.CesrIoSetSuber(db=self, subkey='rpes.', klas=coring.Saider) # auth AuthN/AuthZ by controller at cid of endpoint provider at eid # maps key=cid.role.eid to val=said of end reply self.eans = subing.CesrSuber(db=self, subkey='eans.', klas=coring.Saider) # auth AuthN/AuthZ by endpoint provider at eid of location at scheme url # maps key=cid.role.eid to val=said of end reply self.lans = subing.CesrSuber(db=self, subkey='lans.', klas=coring.Saider) # service endpoint identifier (eid) auths keyed by controller cid.role.eid # data extracted from reply /end/role/add or /end/role/cut self.ends = koming.Komer(db=self, subkey='ends.', schema=EndpointRecord, ) # service endpoint locations keyed by eid.scheme (endpoint identifier) # data extracted from reply loc self.locs = koming.Komer(db=self, subkey='locs.', schema=LocationRecord, ) # index of last retrieved message from witness mailbox self.tops = koming.Komer(db=self, subkey='witm.', schema=TopicsRecord, ) # group partial signature escrow self.gpse = subing.CatCesrIoSetSuber(db=self, subkey='gpse.', klas=(coring.Seqner, coring.Saider)) # group delegate escrow self.gdee = subing.CatCesrIoSetSuber(db=self, subkey='gdee.', klas=(coring.Seqner, coring.Saider)) # group partial witness escrow self.gpwe = subing.CatCesrIoSetSuber(db=self, subkey='gdwe.', klas=(coring.Seqner, coring.Saider)) # completed group multisig self.cgms = subing.CesrSuber(db=self, subkey='cgms.', klas=coring.Saider) # exchange message partial signature escrow self.epse = subing.SerderSuber(db=self, subkey="epse.") # exchange messages self.exns = subing.SerderSuber(db=self, subkey="exns.") # Forward pointer to a provided reply message self.erpy = subing.CesrSuber(db=self, subkey="erpy.", klas=coring.Saider) # exchange messages self.sxns = subing.SerderSuber(db=self, subkey="sxns.") # exchange message signatures self.esigs = subing.CesrIoSetSuber(db=self, subkey='esigs.', klas=coring.Siger) # exchange message signatures self.ecigs = subing.CatCesrIoSetSuber(db=self, subkey='ecigs.', klas=(coring.Verfer, coring.Cigar)) # exchange pathed attachments self.epath = subing.IoSetSuber(db=self, subkey=".epath") # accepted signed 12-word challenge response exn messages keys by prefix of signer self.chas = subing.CesrIoSetSuber(db=self, subkey='chas.', klas=coring.Saider) # successfull signed 12-word challenge response exn messages keys by prefix of signer self.reps = subing.CesrIoSetSuber(db=self, subkey='reps.', klas=coring.Saider) # authorzied well known OOBIs self.wkas = koming.IoSetKomer(db=self, subkey='wkas.', schema=WellKnownAuthN) # KSN support datetime stamps and signatures indexed and not-indexed # all ksn kdts (key state datetime serializations) maps said to date-time self.kdts = subing.CesrSuber(db=self, subkey='kdts.', klas=coring.Dater) # all key state messages. Maps key state said to serialization. ksns are # KeyStateRecords so use ._asdict or ._asjson as appropriate # use .kdts, .ksgs, and .kcgs for datetimes and signatures self.ksns = koming.Komer(db=self, schema=KeyStateRecord, subkey='ksns.') #self.ksns = subing.SerderSuber(db=self, subkey='ksns.') # key state SAID database for successfully saved key state notices # maps key=(prefix, aid) to val=said of key state self.knas = subing.CesrSuber(db=self, subkey='knas.', klas=coring.Saider) # config loaded oobis to be processed asynchronously, keyed by oobi URL self.oobis = koming.Komer(db=self, subkey='oobis.', schema=OobiRecord, sep=">") # Use seperator not a allowed in URLs so no splitting occurs. # escrow OOBIs that failed to load, retriable, keyed by oobi URL self.eoobi = koming.Komer(db=self, subkey='eoobi.', schema=OobiRecord, sep=">") # Use seperator not a allowed in URLs so no splitting occurs. # OOBIs with outstand client requests. self.coobi = koming.Komer(db=self, subkey='coobi.', schema=OobiRecord, sep=">") # Use seperator not a allowed in URLs so no splitting occurs. # Resolved OOBIs (those that have been processed successfully for this database. self.roobi = koming.Komer(db=self, subkey='roobi.', schema=OobiRecord, sep=">") # Use seperator not a allowed in URLs so no splitting occurs. # Well known OOBIs that are to be used for mfa against a resolved OOBI. self.woobi = koming.Komer(db=self, subkey='woobi.', schema=OobiRecord, sep=">") # Use seperator not a allowed in URLs so no splitting occurs. # Well known OOBIs that are to be used for mfa against a resolved OOBI. self.moobi = koming.Komer(db=self, subkey='moobi.', schema=OobiRecord, sep=">") # Use seperator not a allowed in URLs so no splitting occurs. # Multifactor well known OOBI auth records to process. Keys by controller URL self.mfa = koming.Komer(db=self, subkey='mfa.', schema=OobiRecord, sep=">") # Use seperator not a allowed in URLs so no splitting occurs. # Resolved multifactor well known OOBI auth records. Keys by controller URL self.rmfa = koming.Komer(db=self, subkey='mfa.', schema=OobiRecord, sep=">") # Use seperator not a allowed in URLs so no splitting occurs. # JSON schema SADs keys by the SAID self.schema = subing.SchemerSuber(db=self, subkey='schema.') # Field values for contact information for remote identifiers. Keyed by prefix/field self.cfld = subing.Suber(db=self, subkey="cfld.") # Global settings for the Habery environment self.hbys = subing.Suber(db=self, subkey='hbys.') # Signed contact data, keys by prefix self.cons = subing.Suber(db=self, subkey="cons.") # Transferable signatures on contact data self.ccigs = subing.CesrSuber(db=self, subkey='ccigs.', klas=coring.Cigar) # Chunked image data for contact information for remote identifiers self.imgs = self.env.open_db(key=b'imgs.') # Delegation escrow dbs # # delegated partial witness escrow self.dpwe = subing.SerderSuber(db=self, subkey='dpwe.') # delegated unanchored escrow self.dune = subing.SerderSuber(db=self, subkey='dune.') # completed group multisig self.cdel = subing.CesrSuber(db=self, subkey='cdel.', klas=coring.Saider) # public keys mapped to the AID and event seq no they appeared in self.pubs = subing.CatCesrIoSetSuber(db=self, subkey="pubs.", klas=(coring.Prefixer, coring.Seqner)) # next key digests mapped to the AID and event seq no they appeared in self.digs = subing.CatCesrIoSetSuber(db=self, subkey="digs.", klas=(coring.Prefixer, coring.Seqner)) # multisig sig embed payload SAID mapped to containing exn messages across group multisig participants self.meids = subing.CesrIoSetSuber(db=self, subkey="meids.", klas=coring.Saider) # multisig sig embed payload SAID mapped to group multisig participants AIDs self.maids = subing.CesrIoSetSuber(db=self, subkey="maids.", klas=coring.Prefixer) self.reload() return self.env
[docs] def reload(self): """ Reload stored prefixes and Kevers from .habs """ removes = [] for keys, data in self.habs.getItemIter(): if (ksr := self.states.get(keys=data.hid)) is not None: try: kever = eventing.Kever(state=ksr, db=self, prefixes=self.prefixes, local=True) except kering.MissingEntryError as ex: # no kel event for keystate removes.append(keys) # remove from .habs continue self.kevers[kever.prefixer.qb64] = kever self.prefixes.add(kever.prefixer.qb64) elif data.mid is None: # in .habs but no corresponding key state and not a group so remove removes.append(keys) # no key state or KEL event for .hab record for keys in removes: # remove bare .habs records self.habs.rem(keys=keys) # Load namespaced Habs removes = [] for keys, data in self.nmsp.getItemIter(): if (ksr := self.states.get(keys=data.hid)) is not None: try: kever = eventing.Kever(state=ksr, db=self, prefixes=self.prefixes, local=True) except kering.MissingEntryError as ex: # no kel event for keystate removes.append(keys) # remove from .habs continue self.kevers[kever.prefixer.qb64] = kever self.prefixes.add(kever.prefixer.qb64) elif data.mid is None: # in .habs but no corresponding key state and not a group so remove removes.append(keys) # no key state or KEL event for .hab record for keys in removes: # remove bare .habs records self.nmsp.rem(keys=keys)
[docs] def clean(self): """ Clean database by creating re-verified cleaned cloned copy and then replacing original with cleaned cloned copy Database usage should be offline during cleaning as it will be cloned in readonly mode """ # create copy to clone into with openDB(name=self.name, temp=self.temp, headDirPath=self.headDirPath, perm=self.perm, clean=True) as copy: with reopenDB(db=self, reuse=True, readonly=True): # reopen as readonly if not os.path.exists(self.path): raise ValueError("Error while cleaning, no orig at {}." "".format(self.path)) kvy = eventing.Kevery(db=copy) # promiscuous mode # Revise in future to NOT parse msgs but to extract the processed # objects so can pass directly to kvy.processEvent() # need new method cloneObjAllPreIter() # process event doesn't capture exceptions so we can more easily # detect in the cloning that some events did not make it through psr = parsing.Parser(kvy=kvy) for msg in self.cloneAllPreIter(): # clone into copy psr.parseOne(ims=msg) # clone .habs habitat name prefix Komer subdb # copy.habs = koming.Komer(db=copy, schema=HabitatRecord, subkey='habs.') # copy for keys, val in self.habs.getItemIter(): if val.hid in copy.kevers: # only copy habs that verified copy.habs.put(keys=keys, val=val) copy.prefixes.add(val.hid) if not copy.habs.get(keys=(self.name,)): raise ValueError("Error cloning habs, missing orig name={}." "".format(self.name)) # clone .ends and .locs databases for keys, val in self.ends.getItemIter(): exists = False # only copy if entries in both .ends and .locs for scheme in ("https", "http", "tcp"): # all supported schemes lval = self.locs.get(keys=(val.eid, scheme)) if lval and lval.cid == keys[0] and lval.role == keys[1]: exists = True # loc with matching cid and rol copy.locs.put(keys=(val.eid, scheme), val=lval) if exists: # only copy end if has at least one matching loc copy.ends.put(keys=keys, vals=[val]) # remove own db directory replace with clean clone copy if os.path.exists(self.path): shutil.rmtree(self.path) dst = shutil.move(copy.path, self.path) # move copy back to orig if not dst: # move failed leave new in place so can manually fix raise ValueError("Error cloning, unable to move {} to {}." "".format(copy.path, self.path)) # replace own kevers with copy kevers by clear and copy # future do this by loading kever from .stts key state subdb self.kevers.clear() for pre, kever in copy.kevers.items(): self.kevers[pre] = kever # replace prefixes with cloned copy prefixes # clear and clone .prefixes self.prefixes.clear() self.prefixes.update(copy.prefixes) with reopenDB(db=self, reuse=True): # make sure can reopen if not isinstance(self.env, lmdb.Environment): raise ValueError("Error cloning, unable to reopen." "".format(self.path)) # clone success so remove if still there if os.path.exists(copy.path): shutil.rmtree(copy.path)
[docs] def clonePreIter(self, pre, fn=0): """ Returns iterator of first seen event messages with attachments for the identifier prefix pre starting at first seen order number, fn. Essentially a replay in first seen order with attachments """ if hasattr(pre, 'encode'): pre = pre.encode("utf-8") for fn, dig in self.getFelItemPreIter(pre, fn=fn): try: msg = self.cloneEvtMsg(pre=pre, fn=fn, dig=dig) except Exception: continue # skip this event yield msg
[docs] def cloneAllPreIter(self, key=b''): """ Returns iterator of first seen event messages with attachments for all identifier prefixes starting at key. If key == b'' then rstart at first key in databse. Use key to resume replay. Essentially a replay in first seen order with attachments of entire set of FELs. Parameters: key (bytes): fnKey(pre, fn) """ for pre, fn, dig in self.getFelItemAllPreIter(key=key): try: msg = self.cloneEvtMsg(pre=pre, fn=fn, dig=dig) except Exception: continue # skip this event yield msg
[docs] def cloneEvtMsg(self, pre, fn, dig): """ Clones Event as Serialized CESR Message with Body and attached Foot Parameters: pre (bytes): identifier prefix of event fn (int): first seen number (ordinal) of event dig (bytes): digest of event Returns: bytearray: message body with attachments """ msg = bytearray() # message atc = bytearray() # attachments dgkey = dbing.dgKey(pre, dig) # get message if not (raw := self.getEvt(key=dgkey)): raise kering.MissingEntryError("Missing event for dig={}.".format(dig)) msg.extend(raw) # add indexed signatures to attachments if not (sigs := self.getSigs(key=dgkey)): raise kering.MissingEntryError("Missing sigs for dig={}.".format(dig)) atc.extend(coring.Counter(code=coring.CtrDex.ControllerIdxSigs, count=len(sigs)).qb64b) for sig in sigs: atc.extend(sig) # add indexed witness signatures to attachments if wigs := self.getWigs(key=dgkey): atc.extend(coring.Counter(code=coring.CtrDex.WitnessIdxSigs, count=len(wigs)).qb64b) for wig in wigs: atc.extend(wig) # add authorizer (delegator/issuer) source seal event couple to attachments couple = self.getAes(dgkey) if couple is not None: atc.extend(coring.Counter(code=coring.CtrDex.SealSourceCouples, count=1).qb64b) atc.extend(couple) elif self.kevers[pre].delegated: if serdering.SerderKERI(raw=bytes(raw)).estive: raise kering.MissingEntryError("Missing delegator anchor seal for dig={}.".format(dig)) # add trans receipts quadruples to attachments if quads := self.getVrcs(key=dgkey): atc.extend(coring.Counter(code=coring.CtrDex.TransReceiptQuadruples, count=len(quads)).qb64b) for quad in quads: atc.extend(quad) # add nontrans receipts couples to attachments if coups := self.getRcts(key=dgkey): atc.extend(coring.Counter(code=coring.CtrDex.NonTransReceiptCouples, count=len(coups)).qb64b) for coup in coups: atc.extend(coup) # add first seen replay couple to attachments if not (dts := self.getDts(key=dgkey)): raise kering.MissingEntryError("Missing datetime for dig={}.".format(dig)) atc.extend(coring.Counter(code=coring.CtrDex.FirstSeenReplayCouples, count=1).qb64b) atc.extend(coring.Seqner(sn=fn).qb64b) atc.extend(coring.Dater(dts=bytes(dts)).qb64b) # prepend pipelining counter to attachments if len(atc) % 4: raise ValueError("Invalid attachments size={}, nonintegral" " quadlets.".format(len(atc))) pcnt = coring.Counter(code=coring.CtrDex.AttachedMaterialQuadlets, count=(len(atc) // 4)).qb64b msg.extend(pcnt) msg.extend(atc) return msg
[docs] def cloneDelegation(self, kever): """ Recursively clone delegation chain from AID of Kever if one exits. Parameters: kever (Kever): Kever from which to clone the delegator's AID. """ if kever.delegated: dkever = self.kevers[kever.delegator] yield from self.cloneDelegation(dkever) for dmsg in self.clonePreIter(pre=kever.delegator, fn=0): yield dmsg
[docs] def findAnchoringSealEvent(self, pre, seal, sn=0): """ Search through a KEL for the event that contains a specific anchored SealEvent type of provided seal but in dict form and is also fully witnessed. Searchs from sn forward (default = 0).Searches all events in KEL of pre including disputed and/or superseded events. Returns the Serder of the first event with the anchored SealEvent seal, None if not found Parameters: pre (bytes|str): identifier of the KEL to search seal (dict): dict form of Seal of any type SealEvent to find in anchored seals list of each event sn (int): beginning sn to search """ if tuple(seal.keys()) != eventing.SealEvent._fields: # wrong type of seal return None seal = eventing.SealEvent(**seal) #convert to namedtuple for evt in self.getEvtPreIter(pre=pre, sn=sn): # includes disputed & superseded srdr = serdering.SerderKERI(raw=evt.tobytes()) for eseal in srdr.seals or []: if tuple(eseal.keys()) == eventing.SealEvent._fields: eseal = eventing.SealEvent(**eseal) # convert to namedtuple if seal == eseal and self.fullyWitnessed(srdr): return srdr return None
[docs] def findAnchoringSeal(self, pre, seal, sn=0): """ Search through a KEL for the event that contains an anchored Seal with same Seal type as provided seal but in dict form. Searchs from sn forward (default = 0). Only searches last event at any sn therefore does not search any disputed or superseded events. Returns the Serder of the first event with the anchored Seal seal, None if not found Parameters: pre (bytes|str): identifier of the KEL to search seal (dict): dict form of Seal of any type to find in anchored seals list of each event sn (int): beginning sn to search """ # create generic Seal namedtuple class using keys from provided seal dict Seal = namedtuple('Seal', seal.keys()) # matching type for evt in self.getEvtLastPreIter(pre=pre, sn=sn): # only last evt at sn srdr = serdering.SerderKERI(raw=evt.tobytes()) for eseal in srdr.seals or []: if tuple(eseal.keys()) == Seal._fields: # same type of seal eseal = Seal(**eseal) #convert to namedtuple if seal == eseal and self.fullyWitnessed(srdr): return srdr return None
[docs] def findAnchoringSealEventClone(self, pre, seal): """ Search through a KEL for the event that contains a specific anchored SealEvent type of provided seal but in dict form. Returns the Serder of the first event with the anchored SealEvent seal, None if not found Searchs from inception forward Parameters: pre is qb64 identifier of the KEL to search seal is dict form of SealEvent to find in anchored seals list of each event """ if tuple(seal.keys()) != eventing.SealEvent._fields: # wrong type of seal return None #raise ValueError(f"Expected SealEvent got {seal}.") seal = eventing.SealEvent(**seal) #convert to namedtuple # getEvtPreIter getEvtLastPreIter for evt in self.clonePreIter(pre=pre): # all events including superseded srdr = serdering.SerderKERI(raw=evt) for eseal in srdr.seals or []: if tuple(eseal.keys()) == eventing.SealEvent._fields: eseal = eventing.SealEvent(**eseal) #convert to namedtuple if seal == eseal and self.fullyWitnessed(srdr): return srdr #spre = anc["i"] #ssn = int(anc["s"], 16) #sdig = anc["d"] #if spre == seal["i"] and ssn == int(seal["s"], 16) \ #and seal["d"] == sdig and self.fullyWitnessed(srdr): #return srdr return None
[docs] def findAnchoringSealClone(self, pre, seal): """ Search through a KEL for the event that contains an anchored Seal with same Seal type as provided seal but in dict form. Returns the Serder of the first event with the anchored Seal seal, None if not found Searchs from inception forward Parameters: pre is qb64 identifier of the KEL to search seal is dict form of Seal of any type to find in anchored seals list of each event """ # create generic Seal namedtuple class using keys from provided seal dict Seal = namedtuple('Seal', seal.keys()) # matching type # getEvtPreIter getEvtLastPreIter for evt in self.clonePreIter(pre=pre): # all events including superseded srdr = serdering.SerderKERI(raw=evt) for eseal in srdr.seals or []: if tuple(eseal.keys()) == Seal._fields: # same type of seal eseal = Seal(**eseal) #convert to namedtuple if seal == eseal and self.fullyWitnessed(srdr): return srdr return None
[docs] def signingMembers(self, pre: str): """ Find signing members of a multisig group aid. Using the pubs index to find members of a signing group Parameters: pre (str): qb64 identifier prefix to find members Returns: list: qb64 identifier prefixes of signing members for provided aid """ members = [] if pre not in self.kevers: return members kever = self.kevers[pre] for verfer in kever.verfers: if (couples := self.pubs.get(keys=(verfer.qb64,))) is None: continue for couple in couples: prefixer, seqner = couple if prefixer.qb64 != pre: # Rule out aid being queried members.append(prefixer.qb64) return members
[docs] def rotationMembers(self, pre: str): """ Find rotation members of a multisig group aid. Using the digs index to lookup member pres of a group aid Parameters: pre (str): qb64 identifier prefix to find members Returns: list: qb64 identifier prefixes of rotation members for provided aid """ members = [] if pre not in self.kevers: return members kever = self.kevers[pre] for diger in kever.ndigers: if (couples := self.digs.get(keys=(diger.qb64,))) is None: continue for couple in couples: prefixer, seqner = couple if prefixer.qb64 != pre: # Rule out aid being queried members.append(prefixer.qb64) return members
[docs] def fullyWitnessed(self, serder): """ Verify the witness threshold on the event Parameters: serder (Serder): event serder to validate witness threshold Returns: """ # Verify fully receipted, because this witness may have persisted before all receipts # have been gathered if this ius a witness for serder.pre dgkey = dbing.dgKey(serder.preb, serder.saidb) # get unique verified wigers and windices lists from wigers list wigs = self.getWigs(key=dgkey) kever = self.kevers[serder.pre] toad = kever.toader.num return not len(wigs) < toad
[docs] def resolveVerifiers(self, pre=None, sn=0, dig=None): """ Returns the Tholder and Verfers for the provided identifier prefix. Default pre is own .pre Parameters: pre(str) is qb64 str of bytes of identifier prefix. sn(int) is the sequence number of the est event dig(str) is qb64 str of digest of est event """ prefixer = coring.Prefixer(qb64=pre) if prefixer.transferable: # receipted event and receipter in database so get receipter est evt # retrieve dig of last event at sn of est evt of receipter. sdig = self.getKeLast(key=dbing.snKey(pre=prefixer.qb64b, sn=sn)) if sdig is None: # receipter's est event not yet in receipters's KEL raise kering.ValidationError("key event sn {} for pre {} is not yet in KEL" "".format(sn, pre)) # retrieve last event itself of receipter est evt from sdig sraw = self.getEvt(key=dbing.dgKey(pre=prefixer.qb64b, dig=bytes(sdig))) # assumes db ensures that sraw must not be none because sdig was in KE sserder = serdering.SerderKERI(raw=bytes(sraw)) if dig is not None and not sserder.compare(said=dig): # endorser's dig not match event raise kering.ValidationError("Bad proof sig group at sn = {}" " for ksn = {}." "".format(sn, sserder.sad)) verfers = sserder.verfers tholder = sserder.tholder else: verfers = [coring.Verfer(qb64=pre)] tholder = coring.Tholder(sith="1") return tholder, verfers
[docs] def putEvt(self, key, val): """ Use dgKey() Write serialized event bytes val to key Does not overwrite existing val if any Returns True If val successfully written Else False Return False if key already exists """ return self.putVal(self.evts, key, val)
[docs] def setEvt(self, key, val): """ Use dgKey() Write serialized event bytes val to key Overwrites existing val if any Returns True If val successfully written Else False """ return self.setVal(self.evts, key, val)
[docs] def getEvt(self, key): """ Use dgKey() Return event at key Returns None if no entry at key """ return self.getVal(self.evts, key)
[docs] def delEvt(self, key): """ Use dgKey() Deletes value at key. Returns True If key exists in database Else False """ return self.delVal(self.evts, key)
[docs] def getEvtPreIter(self, pre, sn=0): """ Returns iterator of event messages without attachments in sn order from the KEL of identifier prefix pre. Essentially a replay of all event messages without attachments for each sn from the KEL of pre including superseded duplicates Parameters: pre (bytes|str): identifier prefix sn (int): sequence number (default 0) to begin interation """ if hasattr(pre, 'encode'): pre = pre.encode("utf-8") for dig in self.getKelIter(pre, sn=sn): try: dgkey = dbing.dgKey(pre, dig) # get message if not (raw := self.getEvt(key=dgkey)): raise kering.MissingEntryError("Missing event for dig={}.".format(dig)) except Exception: continue # skip this event yield raw # event message
[docs] def getEvtLastPreIter(self, pre, sn=0): """ Returns iterator of event messages without attachments in sn order from the KEL of identifier prefix pre. Essentially a replay of all event messages without attachments for each sn from the KEL of pre including superseded duplicates Parameters: pre (bytes|str): identifier prefix sn (int): sequence number (default 0) to begin interation """ if hasattr(pre, 'encode'): pre = pre.encode("utf-8") for dig in self.getKelLastIter(pre, sn=sn): try: dgkey = dbing.dgKey(pre, dig) # get message if not (raw := self.getEvt(key=dgkey)): raise kering.MissingEntryError("Missing event for dig={}.".format(dig)) except Exception: continue # skip this event yield raw # event message
[docs] def putFe(self, key, val): """ Use fnKey() Write event digest bytes val to key Does not overwrite existing val if any Returns True If val successfully written Else False Return False if key already exists """ return self.putVal(self.fels, key, val)
[docs] def setFe(self, key, val): """ Use fnKey() Write event digest bytes val to key Overwrites existing val if any Returns True If val successfully written Else False """ return self.setVal(self.fels, key, val)
[docs] def getFe(self, key): """ Use fnKey() Return event digest at key Returns None if no entry at key """ return self.getVal(self.fels, key)
[docs] def delFe(self, key): """ Use snKey() Deletes value at key. Returns True If key exists in database Else False """ return self.delVal(self.fels, key)
[docs] def appendFe(self, pre, val): """ Return first seen order number int, fn, of appended entry. Computes fn as next fn after last entry. Uses fnKey(pre, fn) for entries. Append val to end of db entries with same pre but with fn incremented by 1 relative to last preexisting entry at pre. Parameters: pre is bytes identifier prefix for event val is event digest """ return self.appendOrdValPre(db=self.fels, pre=pre, val=val)
[docs] def getFelItemPreIter(self, pre, fn=0): """ Returns iterator of all (fn, dig) duples in first seen order for all events with same prefix, pre, in database. Items are sorted by fnKey(pre, fn) where fn is first seen order number int. Returns a First Seen Event Log FEL. Returned items are duples of (fn, dig): Where fn is first seen order number int and dig is event digest for lookup in .evts sub db. Raises StopIteration Error when empty. Parameters: pre is bytes of itdentifier prefix fn is int fn to resume replay. Earliset is fn=0 """ return self.getAllOrdItemPreIter(db=self.fels, pre=pre, on=fn)
[docs] def getFelItemAllPreIter(self, key=b''): """ Returns iterator of all (pre, fn, dig) triples in first seen order for all events for all prefixes in database. Items are sorted by fnKey(pre, fn) where fn is first seen order number int. Returns all First Seen Event Logs FELs. Returned items are tripes of (pre, fn, dig): Where pre is identifier prefix, fn is first seen order number int and dig is event digest for lookup in .evts sub db. Raises StopIteration Error when empty. Parameters: key is key location in db to resume replay, If empty then start at first key in database """ return self.getAllOrdItemAllPreIter(db=self.fels, key=key)
[docs] def putDts(self, key, val): """ Use dgKey() Write serialized event datetime stamp val to key Does not overwrite existing val if any Returns True If val successfully written Else False Returns False if key already exists """ return self.putVal(self.dtss, key, val)
[docs] def setDts(self, key, val): """ Use dgKey() Write serialized event datetime stamp val to key Overwrites existing val if any Returns True If val successfully written Else False """ return self.setVal(self.dtss, key, val)
[docs] def getDts(self, key): """ Use dgKey() Return datetime stamp at key Returns None if no entry at key """ return self.getVal(self.dtss, key)
[docs] def delDts(self, key): """ Use dgKey() Deletes value at key. Returns True If key exists in database Else False """ return self.delVal(self.dtss, key)
[docs] def putAes(self, key, val): """ Use dgKey() Write serialized source seal event couple val to key Does not overwrite existing val if any Returns True If val successfully written Else False Returns False if key already exists """ return self.putVal(self.aess, key, val)
[docs] def setAes(self, key, val): """ Use dgKey() Write serialized source seal event couple val to key Overwrites existing val if any Returns True If val successfully written Else False """ return self.setVal(self.aess, key, val)
[docs] def getAes(self, key): """ Use dgKey() Return source seal event couple at key Returns None if no entry at key """ return self.getVal(self.aess, key)
[docs] def delAes(self, key): """ Use dgKey() Deletes value at key. Returns True If key exists in database Else False """ return self.delVal(self.aess, key)
[docs] def getSigs(self, key): """ Use dgKey() Return list of signatures at key Returns empty list if no entry at key Duplicates are retrieved in lexocographic order not insertion order. """ return self.getVals(self.sigs, key)
[docs] def getSigsIter(self, key): """ Use dgKey() Return iterator of signatures at key Raises StopIteration Error when empty Duplicates are retrieved in lexocographic order not insertion order. """ return self.getValsIter(self.sigs, key)
[docs] def putSigs(self, key, vals): """ Use dgKey() Write each entry from list of bytes signatures vals to key Adds to existing signatures at key if any Returns True If no error Apparently always returns True (is this how .put works with dupsort=True) Duplicates are inserted in lexocographic order not insertion order. """ return self.putVals(self.sigs, key, vals)
[docs] def addSig(self, key, val): """ Use dgKey() Add signature val bytes as dup to key in db Adds to existing values at key if any Returns True if written else False if dup val already exists Duplicates are inserted in lexocographic order not insertion order. """ return self.addVal(self.sigs, key, val)
[docs] def cntSigs(self, key): """ Use dgKey() Return count of signatures at key Returns zero if no entry at key """ return self.cntVals(self.sigs, key)
[docs] def delSigs(self, key, val=b''): """ Use dgKey() Deletes all values at key if val = b'' else deletes dup val = val. Returns True If key exists in database (or key, val if val not b'') Else False """ return self.delVals(self.sigs, key, val)
[docs] def getWigs(self, key): """ Use dgKey() Return list of indexed witness signatures at key Returns empty list if no entry at key Duplicates are retrieved in lexocographic order not insertion order. """ return self.getVals(self.wigs, key)
[docs] def getWigsIter(self, key): """ Use dgKey() Return iterator of indexed witness signatures at key Raises StopIteration Error when empty Duplicates are retrieved in lexocographic order not insertion order. """ return self.getValsIter(self.wigs, key)
[docs] def putWigs(self, key, vals): """ Use dgKey() Write each entry from list of bytes indexed witness signatures vals to key Adds to existing signatures at key if any Returns True If no error Apparently always returns True (is this how .put works with dupsort=True) Duplicates are inserted in lexocographic order not insertion order. """ return self.putVals(self.wigs, key, vals)
[docs] def addWig(self, key, val): """ Use dgKey() Add indexed witness signature val bytes as dup to key in db Adds to existing values at key if any Returns True if written else False if dup val already exists Duplicates are inserted in lexocographic order not insertion order. """ return self.addVal(self.wigs, key, val)
[docs] def cntWigs(self, key): """ Use dgKey() Return count of indexed witness signatures at key Returns zero if no entry at key """ return self.cntVals(self.wigs, key)
[docs] def delWigs(self, key, val=b''): """ Use dgKey() Deletes all values at key if val = b'' else deletes dup val = val. Returns True If key exists in database (or key, val if val not b'') Else False """ return self.delVals(self.wigs, key, val)
[docs] def putRcts(self, key, vals): """ Use dgKey() Write each entry from list of bytes receipt couplets vals to key Couple is pre+cig (non indexed signature) Adds to existing receipts at key if any Returns True If no error Apparently always returns True (is this how .put works with dupsort=True) Duplicates are inserted in lexocographic order not insertion order. """ return self.putVals(self.rcts, key, vals)
[docs] def addRct(self, key, val): """ Use dgKey() Add receipt couple val bytes as dup to key in db Couple is pre+cig (non indexed signature) Adds to existing values at key if any Returns True if written else False if dup val already exists Duplicates are inserted in lexocographic order not insertion order. """ return self.addVal(self.rcts, key, val)
[docs] def getRcts(self, key): """ Use dgKey() Return list of receipt couplets at key Couple is pre+cig (non indexed signature) Returns empty list if no entry at key Duplicates are retrieved in lexocographic order not insertion order. """ return self.getVals(self.rcts, key)
[docs] def getRctsIter(self, key): """ Use dgKey() Return iterator of receipt couplets at key Couple is pre+cig (non indexed signature) Raises StopIteration Error when empty Duplicates are retrieved in lexocographic order not insertion order. """ return self.getValsIter(self.rcts, key)
[docs] def cntRcts(self, key): """ Use dgKey() Return count of receipt couplets at key Couple is pre+cig (non indexed signature) Returns zero if no entry at key """ return self.cntVals(self.rcts, key)
[docs] def delRcts(self, key, val=b''): """ Use dgKey() Deletes all values at key if val = b'' else deletes dup val = val. Returns True If key exists in database (or key, val if val not b'') Else False """ return self.delVals(self.rcts, key, val)
[docs] def putUres(self, key, vals): """ Use snKey() Write each entry from list of bytes receipt triples vals to key Triple is dig+pre+cig Adds to existing receipts at key if any Returns True If at least one of vals is added as dup, False otherwise Duplicates are inserted in insertion order. """ return self.putIoVals(self.ures, key, vals)
[docs] def addUre(self, key, val): """ Use snKey() Add receipt triple val bytes as dup to key in db Triple is dig+pre+cig Adds to existing values at key if any Returns True If at least one of vals is added as dup, False otherwise Duplicates are inserted in insertion order. """ return self.addIoVal(self.ures, key, val)
[docs] def getUres(self, key): """ Use snKey() Return list of receipt triplets at key Triple is dig+pre+cig Returns empty list if no entry at key Duplicates are retrieved in insertion order. """ return self.getIoVals(self.ures, key)
[docs] def getUresIter(self, key): """ Use snKey() Return iterator of receipt triplets at key Triple is dig+pre+cig Raises StopIteration Error when empty Duplicates are retrieved in insertion order. """ return self.getIoValsIter(self.ures, key)
[docs] def getUreLast(self, key): """ Use snKey() Return last inserted dup partial signed escrowed event triple val at key Triple is dig+pre+cig Returns None if no entry at key Duplicates are retrieved in insertion order. """ return self.getIoValLast(self.ures, key)
[docs] def getUreItemsNext(self, key=b'', skip=True): """ Use snKey() Return all dups of partial signed escrowed event triple items at next key after key. Item 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 Returns empty list if no entry at key Duplicates are retrieved in insertion order. """ return self.getIoItemsNext(self.ures, key, skip)
[docs] def getUreItemsNextIter(self, key=b'', skip=True): """ 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.getIoItemsNextIter(self.ures, key, skip)
[docs] def cntUres(self, key): """ Use snKey() Return count of receipt triplets at key Returns zero if no entry at key """ return self.cntIoVals(self.ures, key)
[docs] def delUres(self, key): """ Use snKey() Deletes all values at key in db. Returns True If key exists in database Else False """ return self.delIoVals(self.ures, key)
[docs] def delUre(self, key, val): """ Use snKey() Deletes dup val at key in db. Returns True If dup at exists in db Else False Parameters: key is bytes of key within sub db's keyspace val is dup val (does not include insertion ordering proem) """ return self.delIoVal(self.ures, key, val)
[docs] def putVrcs(self, key, vals): """ Use dgKey() Write each entry from list of bytes receipt quadruples vals to key quadruple is spre+ssnu+sdig+sig Adds to existing receipts at key if any Returns True If no error Apparently always returns True (is this how .put works with dupsort=True) Duplicates are inserted in lexocographic order not insertion order. """ return self.putVals(self.vrcs, key, vals)
[docs] def addVrc(self, key, val): """ Use dgKey() Add receipt quadruple val bytes as dup to key in db quadruple is spre+ssnu+sdig+sig Adds to existing values at key if any Returns True if written else False if dup val already exists Duplicates are inserted in lexocographic order not insertion order. """ return self.addVal(self.vrcs, key, val)
[docs] def getVrcs(self, key): """ Use dgKey() Return list of receipt quadruples at key quadruple is spre+ssnu+sdig+sig Returns empty list if no entry at key Duplicates are retrieved in lexocographic order not insertion order. """ return self.getVals(self.vrcs, key)
[docs] def getVrcsIter(self, key): """ Use dgKey() Return iterator of receipt quadruples at key quadruple is spre+ssnu+sdig+sig Raises StopIteration Error when empty Duplicates are retrieved in lexocographic order not insertion order. """ return self.getValsIter(self.vrcs, key)
[docs] def cntVrcs(self, key): """ Use dgKey() Return count of receipt quadruples at key Returns zero if no entry at key """ return self.cntVals(self.vrcs, key)
[docs] def delVrcs(self, key, val=b''): """ Use dgKey() Deletes all values at key if val = b'' else deletes dup val = val. Returns True If key exists in database (or key, val if val not b'') Else False """ return self.delVals(self.vrcs, key, val)
[docs] def putVres(self, key, vals): """ Use snKey() Write each entry from list of bytes receipt quinlets vals to key Quinlet is edig + spre + ssnu + sdig +sig Adds to existing receipts at key if any Returns True If at least one of vals is added as dup, False otherwise Duplicates are inserted in insertion order. """ return self.putIoVals(self.vres, key, vals)
[docs] def addVre(self, key, val): """ Use snKey() Add receipt quintuple val bytes as dup to key in db Quinlet is edig + spre + ssnu + sdig +sig Adds to existing values at key if any Returns True If at least one of vals is added as dup, False otherwise Duplicates are inserted in insertion order. """ return self.addIoVal(self.vres, key, val)
[docs] def getVres(self, key): """ Use snKey() Return list of receipt quinlets at key Quinlet is edig + spre + ssnu + sdig +sig Returns empty list if no entry at key Duplicates are retrieved in insertion order. """ return self.getIoVals(self.vres, key)
[docs] def getVresIter(self, key): """ Use snKey() Return iterator of receipt quinlets at key Quinlet is edig + spre + ssnu + sdig +sig Raises StopIteration Error when empty Duplicates are retrieved in insertion order. """ return self.getIoValsIter(self.vres, key)
[docs] def getVreLast(self, key): """ Use snKey() Return last inserted dup partial signed escrowed event quintuple val at key Quinlet is edig + spre + ssnu + sdig +sig Returns None if no entry at key Duplicates are retrieved in insertion order. """ return self.getIoValLast(self.vres, key)
[docs] def getVreItemsNext(self, key=b'', skip=True): """ Use snKey() Return all dups of partial signed escrowed event quintuple items at next key after key. Item 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 Returns empty list if no entry at key Duplicates are retrieved in insertion order. """ return self.getIoItemsNext(self.vres, key, skip)
[docs] def getVreItemsNextIter(self, key=b'', skip=True): """ 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.getIoItemsNextIter(self.vres, key, skip)
[docs] def cntVres(self, key): """ Use snKey() Return count of receipt quinlets at key Returns zero if no entry at key """ return self.cntIoVals(self.vres, key)
[docs] def delVres(self, key): """ Use snKey() Deletes all values at key in db. Returns True If key exists in database Else False """ return self.delIoVals(self.vres, key)
[docs] def delVre(self, key, val): """ Use snKey() Deletes dup val at key in db. Returns True If dup at exists in db Else False Parameters: key is bytes of key within sub db's keyspace val is dup val (does not include insertion ordering proem) """ return self.delIoVal(self.vres, key, val)
[docs] def putKes(self, key, vals): """ Use snKey() Write each key event dig entry from list of bytes vals to key Adds to existing event indexes at key if any Returns True If at least one of vals is added as dup, False otherwise Duplicates are inserted in insertion order. """ return self.putIoVals(self.kels, key, vals)
[docs] def addKe(self, key, val): """ Use snKey() Add key event val bytes as dup to key in db Adds to existing event indexes at key if any Returns True if written else False if dup val already exists Duplicates are inserted in insertion order. """ return self.addIoVal(self.kels, key, val)
[docs] def getKes(self, key): """ Use snKey() Return list of key event dig vals at key Returns empty list if no entry at key Duplicates are retrieved in insertion order. """ return self.getIoVals(self.kels, key)
[docs] def getKeLast(self, key): """ Use snKey() Return last inserted dup key event dig vals at key Returns None if no entry at key Duplicates are retrieved in insertion order. """ return self.getIoValLast(self.kels, key)
[docs] def cntKes(self, key): """ Use snKey() Return count of dup key event dig val at key Returns zero if no entry at key """ return self.cntIoVals(self.kels, key)
[docs] def delKes(self, key): """ Use snKey() Deletes all values at key. Returns True If key exists in database Else False """ return self.delIoVals(self.kels, key)
[docs] def getKelIter(self, pre, sn=0): """ Returns iterator of all dup vals in insertion order for all entries with same prefix across all sequence numbers without gaps. Stops if encounters gap. Assumes that key is combination of prefix and sequence number given by .snKey(). db .kels values are digests used to lookup event in .evts sub DB Raises StopIteration Error when empty. Duplicates are retrieved in insertion order. db is opened as named sub db with dupsort=True Parameters: pre (bytes | str): of itdentifier prefix prepended to sn in key within sub db's keyspace sn (int): initial sequence number to begin at """ if hasattr(pre, "encode"): pre = pre.encode("utf-8") # convert str to bytes return self.getIoValsAllPreIter(self.kels, pre, on=sn)
[docs] def getKelBackIter(self, pre, sn=0): """ Returns iterator of all dup vals in insertion order for all entries with same prefix across all sequence numbers without gaps in decreasing order starting with first sequence number sn. Stops if encounters gap. Assumes that key is combination of prefix and sequence number given by .snKey(). db .kels values are digests used to lookup event in .evts sub DB Raises StopIteration Error when empty. Duplicates are retrieved in insertion order. db is opened as named sub db with dupsort=True Parameters: pre (bytes | str): of itdentifier prefix prepended to sn in key within sub db's keyspace sn (int): """ if hasattr(pre, "encode"): pre = pre.encode("utf-8") # convert str to bytes return self.getIoValsAllPreBackIter(self.kels, pre, sn)
[docs] def getKelLastIter(self, pre, sn=0): """ Returns iterator of last one of dup vals at each key in insertion order for all entries with same prefix across all sequence numbers without gaps. Stops if encounters gap. Assumes that key is combination of prefix and sequence number given by .snKey(). db .kels values are digests used to lookup event in .evts sub DB Raises StopIteration Error when empty. Duplicates are retrieved in insertion order. db is opened as named sub db with dupsort=True Parameters: pre (bytes | str): of itdentifier prefix prepended to sn in key within sub db's keyspace sn (int); sequence number to being iteration """ if hasattr(pre, "encode"): pre = pre.encode("utf-8") # convert str to bytes return self.getIoValLastAllPreIter(self.kels, pre, on=sn)
[docs] def putPses(self, key, vals): """ Use snKey() Write each partial signed escrow event entry from list of bytes dig vals to key Adds to existing event indexes at key if any Returns True If at least one of vals is added as dup, False otherwise Duplicates are inserted in insertion order. """ return self.putIoVals(self.pses, key, vals)
[docs] def addPse(self, key, val): """ Use snKey() Add Partial signed escrow val bytes as dup to key in db Adds to existing event indexes at key if any Returns True if written else False if dup val already exists Duplicates are inserted in insertion order. """ return self.addIoVal(self.pses, key, val)
[docs] def getPses(self, key): """ Use snKey() Return list of partial signed escrowed event dig vals at key Returns empty list if no entry at key Duplicates are retrieved in insertion order. """ return self.getIoVals(self.pses, key)
[docs] def getPsesIter(self, key): """ Use sgKey() Return iterator of partial signed escrowed event dig vals at key Raises StopIteration Error when empty Duplicates are retrieved in insertion order. """ return self.getIoValsIter(self.pses, key)
[docs] def getPseLast(self, key): """ Use snKey() Return last inserted dup partial signed escrowed event dig val at key Returns None if no entry at key Duplicates are retrieved in insertion order. """ return self.getIoValLast(self.pses, key)
[docs] def getPseItemsNext(self, key=b'', skip=True): """ Use snKey() Return all dups of partial signed escrowed event dig items at next key after key. Item 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 Returns empty list if no entry at key Duplicates are retrieved in insertion order. """ return self.getIoItemsNext(self.pses, key, skip)
[docs] def getPseItemsNextIter(self, key=b'', skip=True): """ 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.getIoItemsNextIter(self.pses, key, skip)
[docs] def cntPses(self, key): """ Use snKey() Return count of dup event dig vals at key Returns zero if no entry at key """ return self.cntIoVals(self.pses, key)
[docs] def delPses(self, key): """ Use snKey() Deletes all values at key in db. Returns True If key exists in db Else False """ return self.delIoVals(self.pses, key)
[docs] def delPse(self, key, val): """ Use snKey() Deletes dup val at key in db. Returns True If dup at exists in db Else False Parameters: key is bytes of key within sub db's keyspace val is dup val (does not include insertion ordering proem) """ return self.delIoVal(self.pses, key, val)
[docs] def putPde(self, key, val): """ Use dgKey() Write serialized event source couple to key (snu+dig) Does not overwrite existing val if any Returns True If val successfully written Else False Returns False if key already exists """ return self.putVal(self.pdes, key, val)
[docs] def setPde(self, key, val): """ Use dgKey() Write serialized seal source couple to key (snu+dig) Overwrites existing val if any Returns True If val successfully written Else False """ return self.setVal(self.pdes, key, val)
[docs] def getPde(self, key): """ Use dgKey() Return seal source couple at key Returns None if no entry at key """ return self.getVal(self.pdes, key)
[docs] def delPde(self, key): """ Use dgKey() Deletes value at key. Returns True If key exists in database Else False """ return self.delVal(self.pdes, key)
[docs] def putPwes(self, key, vals): """ Use snKey() Write each partial witnessed escrow event entry from list of bytes dig vals to key Adds to existing event indexes at key if any Returns True If at least one of vals is added as dup, False otherwise Duplicates are inserted in insertion order. """ return self.putIoVals(self.pwes, key, vals)
[docs] def addPwe(self, key, val): """ Use snKey() Add Partial witnessed escrow dig val bytes as dup to key in db Adds to existing event indexes at key if any Returns True if written else False if dup val already exists Duplicates are inserted in insertion order. """ return self.addIoVal(self.pwes, key, val)
[docs] def getPwes(self, key): """ Use snKey() Return list of witnessed signed escrowed event dig vals at key Returns empty list if no entry at key Duplicates are retrieved in insertion order. """ return self.getIoVals(self.pwes, key)
[docs] def getPwesIter(self, key): """ Use sgKey() Return iterator of partial witnessed escrowed event dig vals at key Raises StopIteration Error when empty Duplicates are retrieved in insertion order. """ return self.getIoValsIter(self.pwes, key)
[docs] def getPweLast(self, key): """ Use snKey() Return last inserted dup partial witnessed escrowed event dig val at key Returns None if no entry at key Duplicates are retrieved in insertion order. """ return self.getIoValLast(self.pwes, key)
[docs] def getPweItemsNext(self, key=b'', skip=True): """ Use snKey() Return all dups of partial witnessed escrowed event dig items at next key after key. Item 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 Returns empty list if no entry at key Duplicates are retrieved in insertion order. """ return self.getIoItemsNext(self.pwes, key, skip)
[docs] def getPweItemsNextIter(self, key=b'', skip=True): """ 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.getIoItemsNextIter(self.pwes, key, skip)
[docs] def cntPwes(self, key): """ Use snKey() Return count of dup event dig vals at key Returns zero if no entry at key """ return self.cntIoVals(self.pwes, key)
[docs] def delPwes(self, key): """ Use snKey() Deletes all values at key in db. Returns True If key exists in db Else False """ return self.delIoVals(self.pwes, key)
[docs] def delPwe(self, key, val): """ Use snKey() Deletes dup val at key in db. Returns True If dup at exists in db Else False Parameters: key is bytes of key within sub db's keyspace val is dup val (does not include insertion ordering proem) """ return self.delIoVal(self.pwes, key, val)
[docs] def putUwes(self, key, vals): """ Use snKey() Write each entry from list of bytes witness receipt couples vals to key Witness couple is edig+wig Adds to existing receipts at key if any Returns True If at least one of vals is added as dup, False otherwise Duplicates are inserted in insertion order. """ return self.putIoVals(self.uwes, key, vals)
[docs] def addUwe(self, key, val): """ Use snKey() Add receipt couple val bytes as dup to key in db Witness couple is edig+wig Adds to existing values at key if any Returns True If at least one of vals is added as dup, False otherwise Duplicates are inserted in insertion order. """ return self.addIoVal(self.uwes, key, val)
[docs] def getUwes(self, key): """ Use snKey() Return list of receipt couples at key Witness couple is edig+wig Returns empty list if no entry at key Duplicates are retrieved in insertion order. """ return self.getIoVals(self.uwes, key)
[docs] def getUwesIter(self, key): """ Use snKey() Return iterator of receipt couples at key Witness couple is edig+wig Raises StopIteration Error when empty Duplicates are retrieved in insertion order. """ return self.getIoValsIter(self.uwes, key)
[docs] def getUweLast(self, key): """ Use snKey() Return last inserted dup partial signed escrowed receipt couple val at key Witness couple is edig+wig Returns None if no entry at key Duplicates are retrieved in insertion order. """ return self.getIoValLast(self.uwes, key)
[docs] def getUweItemsNext(self, key=b'', skip=True): """ Use snKey() Return all dups of partial signed escrowed receipt couple items at next key after key. Item 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 Returns empty list if no entry at key Duplicates are retrieved in insertion order. """ return self.getIoItemsNext(self.uwes, key, skip)
[docs] def getUweItemsNextIter(self, key=b'', skip=True): """ 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.getIoItemsNextIter(self.uwes, key, skip)
[docs] def cntUwes(self, key): """ Use snKey() Return count of receipt couples at key Returns zero if no entry at key """ return self.cntIoVals(self.uwes, key)
[docs] def delUwes(self, key): """ Use snKey() Deletes all values at key in db. Returns True If key exists in database Else False """ return self.delIoVals(self.uwes, key)
[docs] def delUwe(self, key, val): """ Use snKey() Deletes dup val at key in db. Returns True If dup at exists in db Else False Parameters: key is bytes of key within sub db's keyspace val is dup val (does not include insertion ordering proem) """ return self.delIoVal(self.uwes, key, val)
[docs] def putOoes(self, key, vals): """ Use snKey() Write each out of order escrow event dig entry from list of bytes vals to key Adds to existing event indexes at key if any Returns True If at least one of vals is added as dup, False otherwise Duplicates are inserted in insertion order. """ return self.putIoVals(self.ooes, key, vals)
[docs] def addOoe(self, key, val): """ Use snKey() Add out of order escrow val bytes as dup to key in db Adds to existing event indexes at key if any Returns True if written else False if dup val already exists Duplicates are inserted in insertion order. """ return self.addIoVal(self.ooes, key, val)
[docs] def getOoes(self, key): """ Use snKey() Return list of out of order escrow event dig vals at key Returns empty list if no entry at key Duplicates are retrieved in insertion order. """ return self.getIoVals(self.ooes, key)
[docs] def getOoeLast(self, key): """ Use snKey() Return last inserted dup val of out of order escrow event dig vals at key Returns None if no entry at key Duplicates are retrieved in insertion order. """ return self.getIoValLast(self.ooes, key)
[docs] def getOoeItemsNext(self, key=b'', skip=True): """ Use snKey() Return all dups of out of order escrowed event dig items at next key after key. Item 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 Returns empty list if no entry at key Duplicates are retrieved in insertion order. """ return self.getIoItemsNext(self.ooes, key, skip)
[docs] def getOoeItemsNextIter(self, key=b'', skip=True): """ 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.getIoItemsNextIter(self.ooes, key, skip)
[docs] def cntOoes(self, key): """ Use snKey() Return count of dup event dig at key Returns zero if no entry at key """ return self.cntIoVals(self.ooes, key)
[docs] def delOoes(self, key): """ Use snKey() Deletes all values at key. Returns True If key exists in database Else False """ return self.delIoVals(self.ooes, key)
[docs] def delOoe(self, key, val): """ Use snKey() Deletes dup val at key in db. Returns True If dup at exists in db Else False Parameters: db is opened named sub db with dupsort=True key is bytes of key within sub db's keyspace val is dup val (does not include insertion ordering proem) """ return self.delIoVal(self.ooes, key, val)
[docs] def putQnfs(self, key, vals): """ Use snKey() Write each out of order escrow event dig entry from list of bytes vals to key Adds to existing event indexes at key if any Returns True If at least one of vals is added as dup, False otherwise Duplicates are inserted in insertion order. """ return self.putIoVals(self.qnfs, key, vals)
[docs] def addQnf(self, key, val): """ Use snKey() Add out of order escrow val bytes as dup to key in db Adds to existing event indexes at key if any Returns True if written else False if dup val already exists Duplicates are inserted in insertion order. """ return self.addIoVal(self.qnfs, key, val)
[docs] def getQnfs(self, key): """ Use snKey() Return list of out of order escrow event dig vals at key Returns empty list if no entry at key Duplicates are retrieved in insertion order. """ return self.getIoVals(self.qnfs, key)
[docs] def getQnfLast(self, key): """ Use snKey() Return last inserted dup val of out of order escrow event dig vals at key Returns None if no entry at key Duplicates are retrieved in insertion order. """ return self.getIoValLast(self.qnfs, key)
[docs] def getQnfItemsNext(self, key=b'', skip=True): """ Use snKey() Return all dups of out of order escrowed event dig items at next key after key. Item 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 Returns empty list if no entry at key Duplicates are retrieved in insertion order. """ return self.getIoItemsNext(self.qnfs, key, skip)
[docs] def getQnfItemsNextIter(self, key=b'', skip=True): """ 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.getIoItemsNextIter(self.qnfs, key, skip)
[docs] def cntQnfs(self, key): """ Use snKey() Return count of dup event dig at key Returns zero if no entry at key """ return self.cntIoVals(self.qnfs, key)
[docs] def delQnfs(self, key): """ Use snKey() Deletes all values at key. Returns True If key exists in database Else False """ return self.delIoVals(self.qnfs, key)
[docs] def delQnf(self, key, val): """ Use snKey() Deletes dup val at key in db. Returns True If dup at exists in db Else False Parameters: db is opened named sub db with dupsort=True key is bytes of key within sub db's keyspace val is dup val (does not include insertion ordering proem) """ return self.delIoVal(self.qnfs, key, val)
[docs] def putDes(self, key, vals): """ Use snKey() Write each duplicitous event entry dig from list of bytes vals to key Adds to existing event indexes at key if any Returns True If at least one of vals is added as dup, False otherwise Duplicates are inserted in insertion order. """ return self.putIoVals(self.dels, key, vals)
[docs] def addDe(self, key, val): """ Use snKey() Add duplicate event index val bytes as dup to key in db Adds to existing event indexes at key if any Returns True if written else False if dup val already exists Duplicates are inserted in insertion order. """ return self.addIoVal(self.dels, key, val)
[docs] def getDes(self, key): """ Use snKey() Return list of duplicitous event dig vals at key Returns empty list if no entry at key Duplicates are retrieved in insertion order. """ return self.getIoVals(self.dels, key)
[docs] def getDeLast(self, key): """ Use snKey() Return last inserted dup value of duplicitous event dig vals at key Returns None if no entry at key Duplicates are retrieved in insertion order. """ return self.getIoValLast(self.dels, key)
[docs] def cntDes(self, key): """ Use snKey() Return count of dup event dig vals at key Returns zero if no entry at key """ return self.cntIoVals(self.dels, key)
[docs] def delDes(self, key): """ Use snKey() Deletes all values at key. Returns True If key exists in database Else False """ return self.delIoVals(self.dels, key)
[docs] def getDelIter(self, pre): """ Returns iterator of all dup vals in insertion order for any entries with same prefix across all sequence numbers including gaps. Assumes that key is combination of prefix and sequence number given by .snKey(). Raises StopIteration Error when empty. Duplicates are retrieved in insertion order. Parameters: db is opened named sub db with dupsort=True pre is bytes of itdentifier prefix prepended to sn in key within sub db's keyspace """ if hasattr(pre, "encode"): pre = pre.encode("utf-8") # convert str to bytes return self.getIoValsAnyPreIter(self.dels, pre)
[docs] def putLdes(self, key, vals): """ Use snKey() Write each likely duplicitous event entry dig from list of bytes vals to key Adds to existing event indexes at key if any Returns True If at least one of vals is added as dup, False otherwise Duplicates are inserted in insertion order. """ return self.putIoVals(self.ldes, key, vals)
[docs] def addLde(self, key, val): """ Use snKey() Add likely duplicitous escrow val bytes as dup to key in db Adds to existing event indexes at key if any Returns True if written else False if dup val already exists Duplicates are inserted in insertion order. """ return self.addIoVal(self.ldes, key, val)
[docs] def getLdes(self, key): """ Use snKey() Return list of likely duplicitous event dig vals at key Returns empty list if no entry at key Duplicates are retrieved in insertion order. """ return self.getIoVals(self.ldes, key)
[docs] def getLdeLast(self, key): """ Use snKey() Return last inserted dup val of likely duplicitous event dig vals at key Returns None if no entry at key Duplicates are retrieved in insertion order. """ return self.getIoValLast(self.ldes, key)
[docs] def getLdeItemsNext(self, key=b'', skip=True): """ Use snKey() Return all dups of likely duplicitous escrowed event dig items at next key after key. Item 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 Returns empty list if no entry at key Duplicates are retrieved in insertion order. """ return self.getIoItemsNext(self.ldes, key, skip)
[docs] def getLdeItemsNextIter(self, key=b'', skip=True): """ 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.getIoItemsNextIter(self.ldes, key, skip)
[docs] def cntLdes(self, key): """ Use snKey() Return count of dup event dig at key Returns zero if no entry at key """ return self.cntIoVals(self.ldes, key)
[docs] def delLdes(self, key): """ Use snKey() Deletes all values at key. Returns True If key exists in database Else False """ return self.delIoVals(self.ldes, key)
[docs] def delLde(self, key, val): """ Use snKey() Deletes dup val at key in db. Returns True If dup at exists in db Else False Parameters: db is opened named sub db with dupsort=True key is bytes of key within sub db's keyspace val is dup val (does not include insertion ordering proem) """ return self.delIoVal(self.ldes, key, val)
[docs] class BaserDoer(doing.Doer): """ Basic Baser Doer ( LMDB Database ) Attributes: (inherited) done (bool): completion state: True means completed Otherwise incomplete. Incompletion maybe due to close or abort. Attributes: .baser is Baser or LMDBer subclass Properties: (inherited) .tyme is float relative cycle time of associated Tymist .tyme obtained via injected .tymth function wrapper closure. .tymth is function wrapper closure returned by Tymist .tymeth() method. When .tymth is called it returns associated Tymist .tyme. .tymth provides injected dependency on Tymist tyme base. .tock is float, desired time in seconds between runs or until next run, non negative, zero means run asap Properties: Methods: .wind injects ._tymth dependency from associated Tymist to get its .tyme .__call__ makes instance callable Appears as generator function that returns generator .do is generator method that returns generator .enter is enter context action method .recur is recur context action method or generator method .exit is exit context method .close is close context method .abort is abort context method Hidden: ._tymth is injected function wrapper closure returned by .tymen() of associated Tymist instance that returns Tymist .tyme. when called. ._tock is hidden attribute for .tock property """
[docs] def __init__(self, baser, **kwa): """ Inherited Parameters: tymist is Tymist instance tock is float seconds initial value of .tock Parameters: baser is Baser instance """ super(BaserDoer, self).__init__(**kwa) self.baser = baser
def enter(self): """""" if not self.baser.opened: self.baser.reopen() def exit(self): """""" self.baser.close(clear=self.baser.temp)