async def _create_rev_reg(self, rr_id: str, rr_size: int = None) -> None: """ Create revocation registry and new tails file (and association to corresponding revocation registry definition via symbolic link) for input revocation registry identifier. :param rr_id: revocation registry identifier :param rr_size: revocation registry size (defaults to 256) """ LOGGER.debug('Issuer._create_rev_reg >>> rr_id: %s, rr_size: %s', rr_id, rr_size) if not ok_rev_reg_id(rr_id): LOGGER.debug('Issuer._create_rev_reg <!< Bad rev reg id %s', rr_id) raise BadIdentifier('Bad rev reg id {}'.format(rr_id)) rr_size = rr_size or 256 (cd_id, tag) = rev_reg_id2cred_def_id_tag(rr_id) LOGGER.info( 'Creating revocation registry (capacity %s) for rev reg id %s', rr_size, rr_id) tails_writer_handle = await blob_storage.open_writer( 'default', json.dumps({ 'base_dir': Tails.dir(self._dir_tails, rr_id), 'uri_pattern': '' })) apriori = Tails.unlinked(self._dir_tails) (rr_id, rrd_json, rre_json) = await anoncreds.issuer_create_and_store_revoc_reg( self.wallet.handle, self.did, 'CL_ACCUM', tag, cd_id, json.dumps({ 'max_cred_num': rr_size, 'issuance_type': 'ISSUANCE_ON_DEMAND' }), tails_writer_handle) delta = Tails.unlinked(self._dir_tails) - apriori if len(delta) != 1: LOGGER.debug( 'Issuer._create_rev_reg: <!< Could not create tails file for rev reg id: %s', rr_id) raise CorruptTails( 'Could not create tails file for rev reg id {}'.format(rr_id)) tails_hash = basename(delta.pop()) Tails.associate(self._dir_tails, rr_id, tails_hash) with REVO_CACHE.lock: rrd_req_json = await ledger.build_revoc_reg_def_request( self.did, rrd_json) await self._sign_submit(rrd_req_json) await self._get_rev_reg_def(rr_id) # add to cache en passant rre_req_json = await ledger.build_revoc_reg_entry_request( self.did, rr_id, 'CL_ACCUM', rre_json) await self._sign_submit(rre_req_json) LOGGER.debug('Issuer._create_rev_reg <<<')
async def revoke_cred(self, rr_id: str, cr_id) -> int: """ Revoke credential that input revocation registry identifier and credential revocation identifier specify. Return (epoch seconds) time of revocation. Raise AbsentTails if no tails file is available for input revocation registry identifier. Raise WalletState for closed wallet. Raise BadRevocation if issuer cannot revoke specified credential for any other reason (e.g., did not issue it, already revoked it). :param rr_id: revocation registry identifier :param cr_id: credential revocation identifier :return: time of revocation, in epoch seconds """ LOGGER.debug('Issuer.revoke_cred >>> rr_id: %s, cr_id: %s', rr_id, cr_id) if not self.wallet.handle: LOGGER.debug('Issuer.revoke_cred <!< Wallet %s is closed', self.wallet.name) raise WalletState('Wallet {} is closed'.format(self.wallet.name)) if not ok_rev_reg_id(rr_id): LOGGER.debug('Issuer.revoke_cred <!< Bad rev reg id %s', rr_id) raise BadIdentifier('Bad rev reg id {}'.format(rr_id)) tails_reader_handle = (await Tails( self._dir_tails, *rev_reg_id2cred_def_id_tag(rr_id)).open()).reader_handle try: rrdelta_json = await anoncreds.issuer_revoke_credential( self.wallet.handle, tails_reader_handle, rr_id, cr_id) except IndyError as x_indy: LOGGER.debug( 'Issuer.revoke_cred <!< Could not revoke revoc reg id %s, cred rev id %s: indy error code %s', rr_id, cr_id, x_indy.error_code) raise BadRevocation( 'Could not revoke revoc reg id {}, cred rev id {}: indy error code {}'.format( rr_id, cr_id, x_indy.error_code)) rr_ent_req_json = await ledger.build_revoc_reg_entry_request(self.did, rr_id, 'CL_ACCUM', rrdelta_json) resp_json = await self._sign_submit(rr_ent_req_json) # raises AbsentPool or ClosedPool if applicable resp = json.loads(resp_json) rv = self.pool.protocol.txn2epoch(resp) LOGGER.debug('Issuer.revoke_cred <<< %s', rv) return rv
async def _sync_revoc_for_issue(self, rr_id: str, rr_size: int = None) -> None: """ Create revocation registry if need be for input revocation registry identifier; open and cache tails file reader. :param rr_id: revocation registry identifier :param rr_size: if new revocation registry necessary, its size (default as per RevRegBuilder.create_rev_reg()) """ LOGGER.debug('Issuer._sync_revoc_for_issue >>> rr_id: %s, rr_size: %s', rr_id, rr_size) if not ok_rev_reg_id(rr_id): LOGGER.debug('Issuer._sync_revoc_for_issue <!< Bad rev reg id %s', rr_id) raise BadIdentifier('Bad rev reg id {}'.format(rr_id)) (cd_id, tag) = rev_reg_id2cred_def_id_tag(rr_id) try: await self.get_cred_def(cd_id) except AbsentCredDef: LOGGER.debug( 'Issuer._sync_revoc_for_issue <!< tails tree %s may be for another ledger; no cred def found on %s', self.dir_tails, cd_id) raise AbsentCredDef( 'Tails tree {} may be for another ledger; no cred def found on {}' .format(self.dir_tails, cd_id)) with REVO_CACHE.lock: revo_cache_entry = REVO_CACHE.get(rr_id, None) tails = None if revo_cache_entry is None else revo_cache_entry.tails if tails is None: # it's a new revocation registry, or not yet set in cache try: tails = await Tails(self.dir_tails, cd_id, tag).open() except AbsentTails: # it's a new revocation registry if self.rrbx: await self._set_rev_reg(rr_id, rr_size) else: await self.rrb.create_rev_reg(rr_id, rr_size) await self._send_rev_reg_def(rr_id) tails = await Tails(self.dir_tails, cd_id, tag).open() # symlink should exist now if revo_cache_entry is None: REVO_CACHE[rr_id] = RevoCacheEntry(None, tails) else: REVO_CACHE[rr_id].tails = tails LOGGER.debug('Issuer._sync_revoc_for_issue <<<')
async def create_rev_reg(self, rr_id: str, rr_size: int = None) -> None: """ Create revocation registry artifacts and new tails file (with association to corresponding revocation registry identifier via symbolic link name) for input revocation registry identifier. Symbolic link presence signals completion. If revocation registry builder operates in a process external to its Issuer's, target directory is hopper directory. Raise WalletState for closed wallet. :param rr_id: revocation registry identifier :param rr_size: revocation registry size (defaults to 64) """ LOGGER.debug('RevRegBuilder.create_rev_reg >>> rr_id: %s, rr_size: %s', rr_id, rr_size) if not self.wallet.handle: LOGGER.debug( 'RevRegBuilder.create_rev_reg <!< Wallet %s is closed', self.name) raise WalletState('Wallet {} is closed'.format(self.name)) if not ok_rev_reg_id(rr_id): LOGGER.debug('RevRegBuilder.create_rev_reg <!< Bad rev reg id %s', rr_id) raise BadIdentifier('Bad rev reg id {}'.format(rr_id)) rr_size = rr_size or 64 (cd_id, tag) = rev_reg_id2cred_def_id_tag(rr_id) dir_tails = self.dir_tails_top(rr_id) dir_target = self.dir_tails_target(rr_id) if self.external: try: makedirs(dir_target, exist_ok=False) except FileExistsError: LOGGER.warning( 'RevRegBuilder.create_rev_reg found dir %s, but task not in progress: rebuilding rev reg %s', dir_target, rr_id) rmtree(dir_target) makedirs(dir_target, exist_ok=False) LOGGER.info( 'Creating revocation registry (capacity %s) for rev reg id %s', rr_size, rr_id) tails_writer_handle = await blob_storage.open_writer( 'default', json.dumps({ 'base_dir': dir_target, 'uri_pattern': '' })) (created_rr_id, rr_def_json, rr_ent_json) = await anoncreds.issuer_create_and_store_revoc_reg( self.wallet.handle, self.did, 'CL_ACCUM', tag, cd_id, json.dumps({ 'max_cred_num': rr_size, 'issuance_type': 'ISSUANCE_BY_DEFAULT' }), tails_writer_handle) tails_hash = basename(Tails.unlinked(dir_target).pop()) with open(join(dir_target, 'rr_def.json'), 'w') as rr_def_fh: print(rr_def_json, file=rr_def_fh) with open(join(dir_target, 'rr_ent.json'), 'w') as rr_ent_fh: print(rr_ent_json, file=rr_ent_fh) Tails.associate( dir_tails, created_rr_id, tails_hash) # associate last: symlink signals completion LOGGER.debug('RevRegBuilder.create_rev_reg <<<')