2020)
2121
2222from ...askar .profile import AskarProfile
23- from ...core .profile import ProfileSession
2423
2524from ..issuer import (
2625 IndyIssuer ,
@@ -384,7 +383,6 @@ async def revoke_credentials(
384383 revoc_reg_id : str ,
385384 tails_file_path : str ,
386385 cred_revoc_ids : Sequence [str ],
387- transaction : ProfileSession = None ,
388386 ) -> Tuple [str , Sequence [str ]]:
389387 """
390388 Revoke a set of credentials in a revocation registry.
@@ -399,66 +397,84 @@ async def revoke_credentials(
399397
400398 """
401399
402- txn = transaction if transaction else await self ._profile .transaction ()
403- try :
404- rev_reg_def = await txn .handle .fetch (CATEGORY_REV_REG_DEF , revoc_reg_id )
405- rev_reg = await txn .handle .fetch (
406- CATEGORY_REV_REG , revoc_reg_id , for_update = True
407- )
408- rev_reg_info = await txn .handle .fetch (
409- CATEGORY_REV_REG_INFO , revoc_reg_id , for_update = True
410- )
411- if not rev_reg_def :
412- raise IndyIssuerError ("Revocation registry definition not found" )
413- if not rev_reg :
414- raise IndyIssuerError ("Revocation registry not found" )
415- if not rev_reg_info :
416- raise IndyIssuerError ("Revocation registry metadata not found" )
417- except AskarError as err :
418- raise IndyIssuerError ("Error retrieving revocation registry" ) from err
400+ delta = None
401+ failed_crids = set ()
402+ max_attempt = 5
403+ attempt = 0
419404
420- try :
421- rev_reg_def = RevocationRegistryDefinition .load (rev_reg_def .raw_value )
422- except CredxError as err :
423- raise IndyIssuerError (
424- "Error loading revocation registry definition"
425- ) from err
426-
427- rev_crids = []
428- failed_crids = []
429- max_cred_num = rev_reg_def .max_cred_num
430- rev_info = rev_reg_info .value_json
431- used_ids = set (rev_info .get ("used_ids" ) or [])
432-
433- for rev_id in cred_revoc_ids :
434- rev_id = int (rev_id )
435- if rev_id < 1 or rev_id > max_cred_num :
436- LOGGER .error (
437- "Skipping requested credential revocation"
438- "on rev reg id %s, cred rev id=%s not in range" ,
439- revoc_reg_id ,
440- rev_id ,
441- )
442- elif rev_id > rev_info ["curr_id" ]:
443- LOGGER .warn (
444- "Skipping requested credential revocation"
445- "on rev reg id %s, cred rev id=%s not yet issued" ,
446- revoc_reg_id ,
447- rev_id ,
448- )
449- elif rev_id in used_ids :
450- LOGGER .warn (
451- "Skipping requested credential revocation"
452- "on rev reg id %s, cred rev id=%s already revoked" ,
453- revoc_reg_id ,
454- rev_id ,
455- )
456- else :
457- rev_crids .append (rev_id )
405+ while True :
406+ attempt += 1
407+ if attempt >= max_attempt :
408+ raise IndyIssuerError ("Repeated conflict attempting to update registry" )
409+ try :
410+ async with self ._profile .session () as session :
411+ rev_reg_def = await session .handle .fetch (
412+ CATEGORY_REV_REG_DEF , revoc_reg_id
413+ )
414+ rev_reg = await session .handle .fetch (CATEGORY_REV_REG , revoc_reg_id )
415+ rev_reg_info = await session .handle .fetch (
416+ CATEGORY_REV_REG_INFO , revoc_reg_id
417+ )
418+ if not rev_reg_def :
419+ raise IndyIssuerError ("Revocation registry definition not found" )
420+ if not rev_reg :
421+ raise IndyIssuerError ("Revocation registry not found" )
422+ if not rev_reg_info :
423+ raise IndyIssuerError ("Revocation registry metadata not found" )
424+ except AskarError as err :
425+ raise IndyIssuerError ("Error retrieving revocation registry" ) from err
426+
427+ try :
428+ rev_reg_def = RevocationRegistryDefinition .load (rev_reg_def .raw_value )
429+ except CredxError as err :
430+ raise IndyIssuerError (
431+ "Error loading revocation registry definition"
432+ ) from err
433+
434+ rev_crids = set ()
435+ failed_crids = set ()
436+ max_cred_num = rev_reg_def .max_cred_num
437+ rev_info = rev_reg_info .value_json
438+ used_ids = set (rev_info .get ("used_ids" ) or [])
439+
440+ for rev_id in cred_revoc_ids :
441+ rev_id = int (rev_id )
442+ if rev_id < 1 or rev_id > max_cred_num :
443+ LOGGER .error (
444+ "Skipping requested credential revocation"
445+ "on rev reg id %s, cred rev id=%s not in range" ,
446+ revoc_reg_id ,
447+ rev_id ,
448+ )
449+ failed_crids .add (rev_id )
450+ elif rev_id > rev_info ["curr_id" ]:
451+ LOGGER .warn (
452+ "Skipping requested credential revocation"
453+ "on rev reg id %s, cred rev id=%s not yet issued" ,
454+ revoc_reg_id ,
455+ rev_id ,
456+ )
457+ failed_crids .add (rev_id )
458+ elif rev_id in used_ids :
459+ LOGGER .warn (
460+ "Skipping requested credential revocation"
461+ "on rev reg id %s, cred rev id=%s already revoked" ,
462+ revoc_reg_id ,
463+ rev_id ,
464+ )
465+ failed_crids .add (rev_id )
466+ else :
467+ rev_crids .add (rev_id )
468+
469+ if not rev_crids :
470+ break
458471
459- if rev_crids :
460472 try :
461473 rev_reg = RevocationRegistry .load (rev_reg .raw_value )
474+ except CredxError as err :
475+ raise IndyIssuerError ("Error loading revocation registry" ) from err
476+
477+ try :
462478 delta = await asyncio .get_event_loop ().run_in_executor (
463479 None ,
464480 lambda : rev_reg .update (
@@ -472,31 +488,41 @@ async def revoke_credentials(
472488 raise IndyIssuerError ("Error updating revocation registry" ) from err
473489
474490 try :
475- await txn .handle .replace (
476- CATEGORY_REV_REG , revoc_reg_id , rev_reg .to_json_buffer ()
477- )
478- used_ids .update (rev_crids )
479- rev_info ["used_ids" ] = list (used_ids )
480- await txn .handle .replace (
481- CATEGORY_REV_REG_INFO , revoc_reg_id , value_json = rev_info
482- )
483- for cred_rev_id in rev_crids :
484- issuer_cr_rec = await IssuerCredRevRecord .retrieve_by_ids (
485- txn ,
486- revoc_reg_id ,
487- str (cred_rev_id ),
491+ async with self ._profile .transaction () as txn :
492+ rev_reg_upd = await txn .handle .fetch (
493+ CATEGORY_REV_REG , revoc_reg_id , for_update = True
494+ )
495+ rev_info_upd = await txn .handle .fetch (
496+ CATEGORY_REV_REG_INFO , revoc_reg_id , for_update = True
488497 )
489- await issuer_cr_rec .set_state (
490- txn , IssuerCredRevRecord .STATE_REVOKED
498+ if not rev_reg_upd or not rev_reg_info :
499+ LOGGER .warn (
500+ "Revocation registry missing, skipping update: {}" ,
501+ revoc_reg_id ,
502+ )
503+ delta = None
504+ break
505+ rev_info_upd = rev_info_upd .value_json
506+ if rev_info_upd != rev_info :
507+ # handle concurrent update to the registry by retrying
508+ continue
509+ await txn .handle .replace (
510+ CATEGORY_REV_REG , revoc_reg_id , rev_reg .to_json_buffer ()
511+ )
512+ used_ids .update (rev_crids )
513+ rev_info_upd ["used_ids" ] = sorted (used_ids )
514+ await txn .handle .replace (
515+ CATEGORY_REV_REG_INFO , revoc_reg_id , value_json = rev_info_upd
491516 )
492- if not transaction :
493517 await txn .commit ()
494518 except AskarError as err :
495519 raise IndyIssuerError ("Error saving revocation registry" ) from err
496- else :
497- delta = None
520+ break
498521
499- return (delta and delta .to_json (), failed_crids )
522+ return (
523+ delta and delta .to_json (),
524+ [str (rev_id ) for rev_id in sorted (failed_crids )],
525+ )
500526
501527 async def merge_revocation_registry_deltas (
502528 self , fro_delta : str , to_delta : str
@@ -567,7 +593,7 @@ async def create_and_store_revocation_registry(
567593 rev_reg_def ,
568594 rev_reg_def_private ,
569595 rev_reg ,
570- rev_reg_delta ,
596+ _rev_reg_delta ,
571597 ) = await asyncio .get_event_loop ().run_in_executor (
572598 None ,
573599 lambda : RevocationRegistryDefinition .create (
0 commit comments