Skip to content

Commit a156cc1

Browse files
authored
Merge branch 'main' into upgrade-check-clustered-env
2 parents fa5edc7 + 704eed5 commit a156cc1

2 files changed

Lines changed: 66 additions & 13 deletions

File tree

acapy_agent/revocation/indy.py

Lines changed: 22 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77

88
from ..core.profile import Profile
99
from ..ledger.base import BaseLedger
10+
from ..ledger.error import LedgerError
1011
from ..ledger.multiple_ledger.ledger_requests_executor import (
1112
GET_CRED_DEF,
1213
GET_REVOC_REG_DEF,
@@ -260,6 +261,7 @@ async def get_or_create_active_registry(
260261
except StorageNotFoundError:
261262
pass
262263

264+
posted_registries = []
263265
async with self._profile.session() as session:
264266
full_registries = await IssuerRevRegRecord.query_by_cred_def_id(
265267
session, cred_def_id, None, IssuerRevRegRecord.STATE_FULL, 1
@@ -280,19 +282,30 @@ async def get_or_create_active_registry(
280282
cred_def_id,
281283
max_cred_num=any_registry.max_cred_num,
282284
)
283-
# if there is a posted registry, activate oldest
285+
# if there is a posted registry, complete its setup and activate it
284286
else:
285287
posted_registries = await IssuerRevRegRecord.query_by_cred_def_id(
286288
session, cred_def_id, IssuerRevRegRecord.STATE_POSTED, None, None
287289
)
288-
if posted_registries:
289-
posted_registries = sorted(
290-
posted_registries, key=lambda r: r.created_at
291-
)
292-
await self._set_registry_status(
293-
revoc_reg_id=posted_registries[0].revoc_reg_id,
294-
state=IssuerRevRegRecord.STATE_ACTIVE,
295-
)
290+
291+
if posted_registries:
292+
# A registry is only left posted when its setup stalled after the
293+
# definition reached the ledger: the tails file upload and/or the
294+
# initial accumulator entry are still outstanding. Complete those
295+
# steps instead of activating with a bare state change, which would
296+
# break every credential later issued against the registry
297+
# (unresolvable tailsLocation, missing initial entry).
298+
rec = min(posted_registries, key=lambda r: r.created_at)
299+
try:
300+
await rec.upload_tails_file(self._profile)
301+
# publishing the initial entry transitions posted -> active
302+
await rec.send_entry(self._profile)
303+
except (RevocationError, LedgerError):
304+
LOGGER.exception(
305+
"Setup of posted revocation registry %s could not be completed; "
306+
"not activating (will retry on next issuance attempt)",
307+
rec.revoc_reg_id,
308+
)
296309
return None
297310

298311
async def get_ledger_registry(self, revoc_reg_id: str) -> RevocationRegistry:

acapy_agent/revocation/tests/test_indy.py

Lines changed: 44 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -318,11 +318,51 @@ async def test_get_or_create_active_registry_has_no_active_or_any_registry(self,
318318
],
319319
],
320320
)
321+
@mock.patch.object(IssuerRevRegRecord, "upload_tails_file", mock.CoroutineMock())
322+
@mock.patch.object(IssuerRevRegRecord, "send_entry", mock.CoroutineMock())
321323
async def test_get_or_create_active_registry_has_no_active_with_posted(self, *_):
322324
result = await self.revoc.get_or_create_active_registry("cred_def_id")
323325

324326
assert not result
325-
assert (
326-
self.revoc._set_registry_status.call_args.kwargs["state"]
327-
== IssuerRevRegRecord.STATE_ACTIVE
328-
)
327+
# the stalled setup pipeline is completed rather than bare-activated:
328+
# the tails file is uploaded and the initial entry published (which
329+
# transitions the record posted -> active)
330+
IssuerRevRegRecord.upload_tails_file.assert_awaited_once()
331+
IssuerRevRegRecord.send_entry.assert_awaited_once()
332+
self.revoc._set_registry_status.assert_not_called()
333+
334+
@mock.patch(
335+
"acapy_agent.revocation.indy.IndyRevocation.get_active_issuer_rev_reg_record",
336+
mock.CoroutineMock(side_effect=StorageNotFoundError("No such record")),
337+
)
338+
@mock.patch(
339+
"acapy_agent.revocation.indy.IndyRevocation._set_registry_status",
340+
mock.CoroutineMock(return_value=None),
341+
)
342+
@mock.patch.object(
343+
IssuerRevRegRecord,
344+
"query_by_cred_def_id",
345+
side_effect=[
346+
[IssuerRevRegRecord(max_cred_num=3)],
347+
[
348+
IssuerRevRegRecord(
349+
revoc_reg_id="test-rev-reg-id",
350+
state=IssuerRevRegRecord.STATE_POSTED,
351+
)
352+
],
353+
],
354+
)
355+
@mock.patch.object(
356+
IssuerRevRegRecord,
357+
"upload_tails_file",
358+
mock.CoroutineMock(side_effect=RevocationError("tails server unavailable")),
359+
)
360+
@mock.patch.object(IssuerRevRegRecord, "send_entry", mock.CoroutineMock())
361+
async def test_get_or_create_active_registry_posted_setup_incomplete(self, *_):
362+
result = await self.revoc.get_or_create_active_registry("cred_def_id")
363+
364+
assert not result
365+
# the registry must stay posted: activating it without a tails file
366+
# on the server would break credentials issued against it
367+
IssuerRevRegRecord.send_entry.assert_not_called()
368+
self.revoc._set_registry_status.assert_not_called()

0 commit comments

Comments
 (0)