Skip to content

Commit 46b9338

Browse files
committed
🐛 Fix recovery of private rev reg def
1 parent 606e5ee commit 46b9338

1 file changed

Lines changed: 36 additions & 31 deletions

File tree

acapy_agent/anoncreds/revocation.py

Lines changed: 36 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -269,29 +269,37 @@ def create_rev_reg_def() -> Tuple[
269269

270270
rev_reg_def = RevRegDef.from_native(rev_reg_def)
271271

272-
# Generate a temporary identifier for storing the private key
273-
# We don't know the final rev_reg_def_id until after registry creation
274-
temp_private_key_id = f"temp_private_{uuid4()}"
272+
# Generate and set the public tails URI
273+
public_tails_uri = self.generate_public_tails_uri(rev_reg_def)
274+
rev_reg_def.value.tails_location = public_tails_uri
275+
276+
# Upload tails file
277+
await self.upload_tails_file(rev_reg_def)
278+
279+
# We want to store the private key now so we can recover it in case of failure
280+
# Example of failure and recovery scenario:
281+
# - Rev reg creation is requested, but agent terminates before it completes.
282+
# - Rev reg creation actually succeeded on the ledger, but the agent never
283+
# got the response to emit next event, so on recovery, tries to recreate it.
284+
# - The ledger should return the existing rev reg, instead of recreating it.
285+
# We don't know the rev_reg_def_id until after registry creation, so to link
286+
# the rev reg with the private key, we can instead use the tails hash, which
287+
# is known, and then we recover the private key from storage using the tails
288+
# file hash of the returned RevRegDefResult
275289

276290
# Store the private definition with temporary identifier to avoid losing it
291+
private_key_storage_id = self._get_private_key_storage_id(rev_reg_def)
277292
LOGGER.debug(
278-
"Storing private revocation registry definition with temp ID: %s",
279-
temp_private_key_id,
293+
"Storing private revocation registry definition with storage ID: %s",
294+
private_key_storage_id,
280295
)
281296
async with self.profile.session() as session:
282297
await session.handle.insert(
283298
CATEGORY_REV_REG_DEF_PRIVATE,
284-
temp_private_key_id,
299+
private_key_storage_id,
285300
rev_reg_def_private.to_json_buffer(),
286301
)
287302

288-
# Generate and set the public tails URI
289-
public_tails_uri = self.generate_public_tails_uri(rev_reg_def)
290-
rev_reg_def.value.tails_location = public_tails_uri
291-
292-
# Upload tails file
293-
await self.upload_tails_file(rev_reg_def)
294-
295303
# Register on network
296304
anoncreds_registry = self.profile.inject(AnonCredsRegistry)
297305
result = await anoncreds_registry.register_revocation_registry_definition(
@@ -308,14 +316,11 @@ def create_rev_reg_def() -> Tuple[
308316
tag,
309317
max_cred_num,
310318
)
311-
# Include temp_private_key_id in options for potential recovery
312-
options_with_temp_id = options.copy()
313-
options_with_temp_id["temp_private_key_id"] = temp_private_key_id
314319

315320
event = RevRegDefCreateResponseEvent.with_payload(
316321
rev_reg_def_result=result,
317322
rev_reg_def=rev_reg_def,
318-
options=options_with_temp_id,
323+
options=options,
319324
)
320325
await self.notify(event)
321326

@@ -337,11 +342,6 @@ def create_rev_reg_def() -> Tuple[
337342

338343
LOGGER.warning(f"{error_msg}. Emitting failure event.")
339344

340-
# Include temp_private_key_id in options for potential recovery
341-
failure_options = options.copy()
342-
if "temp_private_key_id" in locals():
343-
failure_options["temp_private_key_id"] = temp_private_key_id
344-
345345
event = RevRegDefCreateResponseEvent.with_failure(
346346
error_msg=error_msg,
347347
should_retry=should_retry,
@@ -351,7 +351,7 @@ def create_rev_reg_def() -> Tuple[
351351
registry_type=registry_type,
352352
tag=tag,
353353
max_cred_num=max_cred_num,
354-
options=failure_options,
354+
options=options,
355355
)
356356
await self.notify(event)
357357

@@ -481,20 +481,18 @@ async def store_revocation_registry_definition( # ✅
481481
rev_reg_def_state = result.revocation_registry_definition_state.state
482482

483483
try:
484-
temp_private_key_id = options.pop("temp_private_key_id", None)
485-
if not temp_private_key_id:
486-
LOGGER.error("No temp private key id found in options")
487-
raise AnonCredsRevocationError("No temp private key id found in options")
484+
private_key_storage_id = self._get_private_key_storage_id(rev_reg_def)
488485

489486
# Read the private definition from storage (stored immediately after creation)
490487
async with self.profile.session() as session:
491488
rev_reg_def_private_entry = await session.handle.fetch(
492-
CATEGORY_REV_REG_DEF_PRIVATE, temp_private_key_id
489+
CATEGORY_REV_REG_DEF_PRIVATE, private_key_storage_id
493490
)
494491

495492
if not rev_reg_def_private_entry:
496493
raise AnonCredsRevocationError(
497-
f"Private revocation registry definition not found for {temp_private_key_id}" # noqa: E501
494+
"Private revocation registry definition "
495+
f"not found for {private_key_storage_id}"
498496
)
499497

500498
async with self.profile.transaction() as txn:
@@ -513,9 +511,12 @@ async def store_revocation_registry_definition( # ✅
513511
identifier,
514512
rev_reg_def_private_entry.value,
515513
)
516-
await txn.handle.remove(CATEGORY_REV_REG_DEF_PRIVATE, temp_private_key_id)
514+
await txn.handle.remove(
515+
CATEGORY_REV_REG_DEF_PRIVATE, private_key_storage_id
516+
)
517517
LOGGER.debug(
518-
"Removed temp private key id %s from storage", temp_private_key_id
518+
"Removed rev reg def private %s from storage",
519+
private_key_storage_id,
519520
)
520521
await txn.commit()
521522
LOGGER.debug("Revocation registry definition storage transaction committed")
@@ -2209,3 +2210,7 @@ async def set_rev_reg_state(self, rev_reg_id: str, state: str) -> RevRegDef:
22092210
def _generate_backup_registry_tag(self) -> str:
22102211
"""Generate a unique tag for a backup registry."""
22112212
return str(uuid4())
2213+
2214+
def _get_private_key_storage_id(self, rev_reg_def: RevRegDef) -> str:
2215+
"""Get the private key storage identifier for a revocation registry definition."""
2216+
return f"rev_reg_def_private_{rev_reg_def.value.tails_hash}"

0 commit comments

Comments
 (0)