2727from uuid_utils import uuid4
2828
2929from ..askar .profile_anon import AskarAnonCredsProfile , AskarAnonCredsProfileSession
30+ from ..core .async_lock .async_lock import AsyncLock
3031from ..core .error import BaseError
3132from ..core .event_bus import Event , EventBus
3233from ..core .profile import Profile , ProfileSession
@@ -975,7 +976,9 @@ async def _create_credential(
975976
976977 """
977978
978- def _handle_missing_entries (rev_list : Entry , rev_reg_def : Entry , rev_key : Entry ):
979+ def _raise_error_when_missing_entries (
980+ rev_list : Entry , rev_reg_def : Entry , rev_key : Entry
981+ ):
979982 if not rev_list :
980983 raise AnonCredsRevocationError ("Revocation registry list not found" )
981984 if not rev_reg_def :
@@ -988,66 +991,79 @@ def _handle_missing_entries(rev_list: Entry, rev_reg_def: Entry, rev_key: Entry)
988991 def _has_required_id_and_tails_path ():
989992 return rev_reg_def_id and tails_file_path
990993
994+ lock = self .profile .inject (AsyncLock )
991995 revoc = None
992996 credential_revocation_id = None
993997 rev_list = None
994998
995- if _has_required_id_and_tails_path ():
996- async with self .profile .session () as session :
997- rev_reg_def = await session .handle .fetch (
998- CATEGORY_REV_REG_DEF , rev_reg_def_id
999- )
1000- rev_list = await session .handle .fetch (CATEGORY_REV_LIST , rev_reg_def_id )
1001- rev_key = await session .handle .fetch (
1002- CATEGORY_REV_REG_DEF_PRIVATE , rev_reg_def_id
1003- )
1004-
1005- _handle_missing_entries (rev_list , rev_reg_def , rev_key )
1006-
1007- rev_list_value_json = rev_list .value_json
1008- rev_list_tags = rev_list .tags
999+ # This lock is used to ensure that only one process on multiple instances
1000+ # can acquire the credential index for the same revocation registry.
1001+ # This is important to avoid because the same index could be used by multiple
1002+ # processes and cause issues with the revocation list.
1003+ # The lock is released when the taken index is updated on the revocation registry.
1004+ async with lock .lock (rev_reg_def_id , timeout = 60 ):
1005+ if _has_required_id_and_tails_path ():
1006+ async with self .profile .session () as session :
1007+ rev_reg_def = await session .handle .fetch (
1008+ CATEGORY_REV_REG_DEF , rev_reg_def_id
1009+ )
1010+ rev_list = await session .handle .fetch (
1011+ CATEGORY_REV_LIST , rev_reg_def_id
1012+ )
1013+ rev_key = await session .handle .fetch (
1014+ CATEGORY_REV_REG_DEF_PRIVATE , rev_reg_def_id
1015+ )
10091016
1010- # If the rev_list state is failed then the tails file was never uploaded,
1011- # try to upload it now and finish the revocation list
1012- if rev_list_tags .get ("state" ) == RevListState .STATE_FAILED :
1013- await self .upload_tails_file (
1014- RevRegDef .deserialize (rev_reg_def .value_json )
1015- )
1016- rev_list_tags ["state" ] = RevListState .STATE_FINISHED
1017+ _raise_error_when_missing_entries (rev_list , rev_reg_def , rev_key )
10171018
1018- rev_reg_index = rev_list_value_json ["next_index" ]
1019- try :
1020- rev_reg_def = RevocationRegistryDefinition .load (rev_reg_def .raw_value )
1021- rev_list = RevocationStatusList .load (rev_list_value_json ["rev_list" ])
1022- except AnoncredsError as err :
1023- raise AnonCredsRevocationError (
1024- "Error loading revocation registry"
1025- ) from err
1019+ rev_list_value_json = rev_list .value_json
1020+ rev_list_tags = rev_list .tags
10261021
1027- # NOTE: we increment the index ahead of time to keep the
1028- # transaction short. The revocation registry itself will NOT
1029- # be updated because we always use ISSUANCE_BY_DEFAULT.
1030- # If something goes wrong later, the index will be skipped.
1031- # FIXME - double check issuance type in case of upgraded wallet?
1032- if rev_reg_index > rev_reg_def .max_cred_num :
1033- raise AnonCredsRevocationRegistryFullError ("Revocation registry is full" )
1034- rev_list_value_json ["next_index" ] = rev_reg_index + 1
1035- async with self .profile .transaction () as txn :
1036- await txn .handle .replace (
1037- CATEGORY_REV_LIST ,
1038- rev_reg_def_id ,
1039- value_json = rev_list_value_json ,
1040- tags = rev_list_tags ,
1041- )
1042- await txn .commit ()
1022+ # If the rev_list state is failed then the tails file was never uploaded,
1023+ # try to upload it now and finish the revocation list
1024+ if rev_list_tags .get ("state" ) == RevListState .STATE_FAILED :
1025+ await self .upload_tails_file (
1026+ RevRegDef .deserialize (rev_reg_def .value_json )
1027+ )
1028+ rev_list_tags ["state" ] = RevListState .STATE_FINISHED
10431029
1044- revoc = CredentialRevocationConfig (
1045- rev_reg_def ,
1046- rev_key .raw_value ,
1047- rev_list ,
1048- rev_reg_index ,
1049- )
1050- credential_revocation_id = str (rev_reg_index )
1030+ rev_reg_index = rev_list_value_json ["next_index" ]
1031+ try :
1032+ rev_reg_def = RevocationRegistryDefinition .load (rev_reg_def .raw_value )
1033+ rev_list = RevocationStatusList .load (rev_list_value_json ["rev_list" ])
1034+ except AnoncredsError as err :
1035+ raise AnonCredsRevocationError (
1036+ "Error loading revocation registry"
1037+ ) from err
1038+
1039+ # NOTE: we increment the index ahead of time to keep the
1040+ # transaction short. The revocation registry itself will NOT
1041+ # be updated because we always use ISSUANCE_BY_DEFAULT.
1042+ # If something goes wrong later, the index will be skipped.
1043+ # FIXME - double check issuance type in case of upgraded wallet?
1044+ if rev_reg_index > rev_reg_def .max_cred_num :
1045+ raise AnonCredsRevocationRegistryFullError (
1046+ "Revocation registry is full"
1047+ )
1048+ rev_list_value_json ["next_index" ] = rev_reg_index + 1
1049+ async with self .profile .transaction () as txn :
1050+ await txn .handle .replace (
1051+ CATEGORY_REV_LIST ,
1052+ rev_reg_def_id ,
1053+ value_json = rev_list_value_json ,
1054+ tags = rev_list_tags ,
1055+ )
1056+ await txn .commit ()
1057+ # Free the distributed lock after commit the revocation list update
1058+ # by exiting with the async with block.
1059+
1060+ revoc = CredentialRevocationConfig (
1061+ rev_reg_def ,
1062+ rev_key .raw_value ,
1063+ rev_list ,
1064+ rev_reg_index ,
1065+ )
1066+ credential_revocation_id = str (rev_reg_index )
10511067
10521068 cred_def , cred_def_private = await self ._get_cred_def_objects (
10531069 credential_definition_id
0 commit comments