From 152d576f1ca92ba58b518f67ea5771e0b62b871e Mon Sep 17 00:00:00 2001 From: pfeairheller Date: Fri, 23 Feb 2024 06:56:31 -0800 Subject: [PATCH 1/8] Fix credential registry list endpoint to account for a not yet fully committed registry. Closes #147 Signed-off-by: pfeairheller --- src/keria/app/credentialing.py | 3 +++ tests/app/test_credentialing.py | 16 ++++++++++++++-- 2 files changed, 17 insertions(+), 2 deletions(-) diff --git a/src/keria/app/credentialing.py b/src/keria/app/credentialing.py index 3b655f27..69e13efa 100644 --- a/src/keria/app/credentialing.py +++ b/src/keria/app/credentialing.py @@ -84,6 +84,9 @@ def on_get(req, rep, name): res = [] for name, registry in agent.rgy.regs.items(): + if registry.regk not in registry.tevers: # defensive programming for a registry not being fully committed + continue + if registry.hab.pre == hab.pre: rd = dict( name=registry.name, diff --git a/tests/app/test_credentialing.py b/tests/app/test_credentialing.py index 3039856c..890ce292 100644 --- a/tests/app/test_credentialing.py +++ b/tests/app/test_credentialing.py @@ -118,7 +118,7 @@ def test_registry_end(helpers, seeder): baks=[], toad="0", nonce=nonce, - cnfg=[TraitCodex.NoBackers], + cnfg=[TraitCodex.NoRegistrarBackers], code=coring.MtrDex.Blake3_256) anchor = dict(i=regser.ked['i'], s=regser.ked["s"], d=regser.said) serder, sigers = helpers.interact(pre=pre, bran=salt, pidx=0, ridx=0, dig=aid['d'], sn='1', data=[anchor]) @@ -131,6 +131,10 @@ def test_registry_end(helpers, seeder): assert metadata["anchor"] == anchor assert result.status == falcon.HTTP_202 + result = client.simulate_get(path="/identifiers/test/registries") + assert result.status == falcon.HTTP_200 + assert result.json == [] + tock = 0.03125 limit = 1.0 doist = doing.Doist(limit=limit, tock=tock, real=True) @@ -143,11 +147,19 @@ def test_registry_end(helpers, seeder): assert regser.pre in agent.tvy.tevers + result = client.simulate_get(path="/identifiers/test/registries") + assert result.status == falcon.HTTP_200 + assert len(result.json) == 1 + + result = client.simulate_post(path="/identifiers/test/registries", body=json.dumps(body).encode("utf-8")) + assert result.status == falcon.HTTP_400 + assert result.json == {'description': 'registry name test already in use', 'title': '400 Bad Request'} body = dict(name="test", alias="test", vcp=regser.ked, ixn=serder.ked, sigs=sigers) result = client.simulate_post(path="/identifiers/bad_test/registries", body=json.dumps(body).encode("utf-8")) assert result.status == falcon.HTTP_404 - assert result.json == {'description': 'alias is not a valid reference to an identifier', 'title': '404 Not Found'} + assert result.json == {'description': 'alias is not a valid reference to an identifier', + 'title': '404 Not Found'} result = client.simulate_get(path="/identifiers/not_test/registries") From 37fc03ee3c1802c0e9db462ed526d5e571fee999 Mon Sep 17 00:00:00 2001 From: pfeairheller Date: Fri, 23 Feb 2024 16:47:45 -0800 Subject: [PATCH 2/8] Fix long running endpoint to allow for an operaiton for a KEL event not yet processed. Signed-off-by: pfeairheller --- src/keria/core/longrunning.py | 36 +++++++++++++++++++---------------- tests/app/test_aiding.py | 12 ++++++++++++ 2 files changed, 32 insertions(+), 16 deletions(-) diff --git a/src/keria/core/longrunning.py b/src/keria/core/longrunning.py index fa6aad5f..c0d41122 100644 --- a/src/keria/core/longrunning.py +++ b/src/keria/core/longrunning.py @@ -197,26 +197,30 @@ def status(self, op): sn = op.metadata["sn"] kever = self.hby.kevers[op.oid] sdig = self.hby.db.getKeLast(key=dbing.snKey(pre=kever.prefixer.qb64b, sn=sn)) + if sdig is not None: - dgkey = dbing.dgKey(kever.prefixer.qb64b, bytes(sdig)) - wigs = self.hby.db.getWigs(dgkey) + dgkey = dbing.dgKey(kever.prefixer.qb64b, bytes(sdig)) + wigs = self.hby.db.getWigs(dgkey) - if len(wigs) >= kever.toader.num: - evt = self.hby.db.getEvt(dbing.dgKey(pre=kever.prefixer.qb64, dig=bytes(sdig))) - serder = serdering.SerderKERI(raw=bytes(evt)) - operation.done = True - operation.response = serder.ked - - else: - start = helping.fromIso8601(op.start) - dtnow = helping.nowUTC() - if (dtnow - start) > datetime.timedelta(seconds=eventing.Kevery.TimeoutPWE): + if len(wigs) >= kever.toader.num: + evt = self.hby.db.getEvt(dbing.dgKey(pre=kever.prefixer.qb64, dig=bytes(sdig))) + serder = serdering.SerderKERI(raw=bytes(evt)) operation.done = True - operation.error = Status(code=408, # Using HTTP error codes here for lack of a better alternative - message=f"long running {op.type} for {op.oid} operation timed out before " - f"receiving sufficient witness receipts") + operation.response = serder.ked + else: - operation.done = False + start = helping.fromIso8601(op.start) + dtnow = helping.nowUTC() + if (dtnow - start) > datetime.timedelta(seconds=eventing.Kevery.TimeoutPWE): + operation.done = True + operation.error = Status(code=408, # Using HTTP error codes here for lack of a better alternative + message=f"long running {op.type} for {op.oid} operation timed out before " + f"receiving sufficient witness receipts") + else: + operation.done = False + + else: + operation.done = False elif op.type in (OpTypes.oobi,): if "oobi" not in op.metadata: diff --git a/tests/app/test_aiding.py b/tests/app/test_aiding.py index dc9caa17..23bc5d81 100644 --- a/tests/app/test_aiding.py +++ b/tests/app/test_aiding.py @@ -25,6 +25,7 @@ from keria.app import aiding, agenting from keria.app.aiding import IdentifierOOBICollectionEnd, RpyEscrowCollectionEnd +from keria.core import longrunning def test_load_ends(helpers): @@ -252,6 +253,11 @@ def test_identifier_collection_end(helpers): groupEnd = aiding.GroupMemberCollectionEnd() app.add_route("/identifiers/{name}/members", groupEnd) + opColEnd = longrunning.OperationCollectionEnd() + app.add_route("/operations", opColEnd) + opResEnd = longrunning.OperationResourceEnd() + app.add_route("/operations/{name}", opResEnd) + client = testing.TestClient(app) res = client.simulate_post(path="/identifiers", body=b'{}') @@ -393,6 +399,12 @@ def test_identifier_collection_end(helpers): res = client.simulate_post(path="/identifiers", body=json.dumps(body)) assert res.status_code == 202 + op = res.json + name = op['name'] + + res = client.simulate_get(path=f"/operations/{name}") + assert res.status_code == 200 + assert len(agent.witners) == 1 res = client.simulate_get(path="/identifiers") assert res.status_code == 200 From 87300ea2e0fa36ff90def1e6ae24ceb1b37bc3c7 Mon Sep 17 00:00:00 2001 From: pfeairheller Date: Sat, 24 Feb 2024 07:49:13 -0800 Subject: [PATCH 3/8] Remove extra test condition pulled in from development Signed-off-by: pfeairheller --- tests/app/test_credentialing.py | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/tests/app/test_credentialing.py b/tests/app/test_credentialing.py index 890ce292..f650fec3 100644 --- a/tests/app/test_credentialing.py +++ b/tests/app/test_credentialing.py @@ -118,7 +118,7 @@ def test_registry_end(helpers, seeder): baks=[], toad="0", nonce=nonce, - cnfg=[TraitCodex.NoRegistrarBackers], + cnfg=[TraitCodex.NoBackers], code=coring.MtrDex.Blake3_256) anchor = dict(i=regser.ked['i'], s=regser.ked["s"], d=regser.said) serder, sigers = helpers.interact(pre=pre, bran=salt, pidx=0, ridx=0, dig=aid['d'], sn='1', data=[anchor]) @@ -151,10 +151,6 @@ def test_registry_end(helpers, seeder): assert result.status == falcon.HTTP_200 assert len(result.json) == 1 - result = client.simulate_post(path="/identifiers/test/registries", body=json.dumps(body).encode("utf-8")) - assert result.status == falcon.HTTP_400 - assert result.json == {'description': 'registry name test already in use', 'title': '400 Bad Request'} - body = dict(name="test", alias="test", vcp=regser.ked, ixn=serder.ked, sigs=sigers) result = client.simulate_post(path="/identifiers/bad_test/registries", body=json.dumps(body).encode("utf-8")) assert result.status == falcon.HTTP_404 From fa9eed35844c6d218b795ab50d6d844aeef40b59 Mon Sep 17 00:00:00 2001 From: pfeairheller Date: Sat, 24 Feb 2024 08:26:29 -0800 Subject: [PATCH 4/8] Add test for invalid sequence number in long running witness operation. Signed-off-by: pfeairheller --- tests/app/test_aiding.py | 11 +++++++++++ tests/core/test_longrunning.py | 2 ++ 2 files changed, 13 insertions(+) diff --git a/tests/app/test_aiding.py b/tests/app/test_aiding.py index 23bc5d81..ec34e63b 100644 --- a/tests/app/test_aiding.py +++ b/tests/app/test_aiding.py @@ -404,6 +404,17 @@ def test_identifier_collection_end(helpers): res = client.simulate_get(path=f"/operations/{name}") assert res.status_code == 200 + assert res.json['done'] is False + + # Modify sequence number to test invalid sn + op = agent.monitor.opr.ops.get(keys=(name,)) + op.metadata['sn'] = 4 + agent.monitor.opr.ops.pin(keys=(name,), val=op) + + res = client.simulate_get(path=f"/operations/{name}") + assert res.status_code == 200 + assert res.json['done'] is False + assert len(agent.witners) == 1 res = client.simulate_get(path="/identifiers") diff --git a/tests/core/test_longrunning.py b/tests/core/test_longrunning.py index 8cc26e9a..267a6ed5 100644 --- a/tests/core/test_longrunning.py +++ b/tests/core/test_longrunning.py @@ -185,6 +185,8 @@ def test_error(helpers): except ValidationError as e: err = e + assert err is not None + res = client.simulate_get(path="/operations") assert isinstance(res.json, list) assert len(res.json) == 1 From ad4a67c37117e9348442d03e432ba41e260fdafd Mon Sep 17 00:00:00 2001 From: pfeairheller Date: Sat, 24 Feb 2024 08:45:10 -0800 Subject: [PATCH 5/8] Add test for satisfied witness receipts for long running operation Signed-off-by: pfeairheller --- tests/app/test_aiding.py | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/tests/app/test_aiding.py b/tests/app/test_aiding.py index ec34e63b..7b6efe29 100644 --- a/tests/app/test_aiding.py +++ b/tests/app/test_aiding.py @@ -18,7 +18,7 @@ from keri.core.coring import MtrDex from keri.db.basing import LocationRecord from keri.peer import exchanging -from keri.db import basing +from keri.db import basing, dbing from keri import kering from keri.vdr import credentialing from hio.base import doing @@ -426,6 +426,17 @@ def test_identifier_collection_end(helpers): ss = aid[Algos.salty] assert ss["pidx"] == 3 + # Reset sn + op.metadata['sn'] = 0 + agent.monitor.opr.ops.pin(keys=(name,), val=op) + + # Add fake witness receipts to test satified witnessing + dgkey = dbing.dgKey(serder.preb, serder.preb) + agent.hby.db.putWigs(dgkey, vals=[b'A', b'B', b'C']) + res = client.simulate_get(path=f"/operations/{name}") + assert res.status_code == 200 + assert res.json['done'] is True + res = client.simulate_get(path=f"/identifiers/aid1") mhab = res.json agent0 = mhab["state"] From 6d336fc1493f0c22bbba42af3eb58c43b6d5b5b0 Mon Sep 17 00:00:00 2001 From: pfeairheller Date: Sun, 25 Feb 2024 14:06:45 -0800 Subject: [PATCH 6/8] Fix reference to revocation anchor event. Signed-off-by: pfeairheller --- src/keria/app/credentialing.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/keria/app/credentialing.py b/src/keria/app/credentialing.py index 69e13efa..8e75f295 100644 --- a/src/keria/app/credentialing.py +++ b/src/keria/app/credentialing.py @@ -811,8 +811,9 @@ def revoke(self, regk, rserder, anc): self.rgy.reger.tpwe.add(keys=(vcid, rseq.qb64), val=(hab.kever.prefixer, seqner, saider)) return vcid, rseq.sn else: - sn = anc.sn - said = anc.said + serder = serdering.SerderKERI(sad=anc) + sn = serder.sn + said = serder.said prefixer = coring.Prefixer(qb64=hab.pre) seqner = coring.Seqner(sn=sn) From 5ab6768e916ea1addcb3ffcb35069b672b428b0c Mon Sep 17 00:00:00 2001 From: pfeairheller Date: Sun, 25 Feb 2024 15:07:32 -0800 Subject: [PATCH 7/8] Add test coverage for multisig revocation. Signed-off-by: pfeairheller --- tests/app/test_credentialing.py | 9 ++++--- tests/app/test_ipexing.py | 47 +++++++++++++++++++++++++++++++++ 2 files changed, 53 insertions(+), 3 deletions(-) diff --git a/tests/app/test_credentialing.py b/tests/app/test_credentialing.py index f650fec3..69c91ee1 100644 --- a/tests/app/test_credentialing.py +++ b/tests/app/test_credentialing.py @@ -521,7 +521,8 @@ def test_revoke_credential(helpers, seeder): rev=sad, ixn=serder.ked, sigs=sigers) - res = client.simulate_delete(path=f"/identifiers/issuer/credentials/{creder.said}", body=json.dumps(badbody).encode("utf-8")) + res = client.simulate_delete(path=f"/identifiers/issuer/credentials/{creder.said}", + body=json.dumps(badbody).encode("utf-8")) assert res.status_code == 404 assert res.json == {'description': 'revocation against invalid registry SAID ' 'EIVtei3pGKGUw8H2Ri0h1uOevtSA6QGAq5wifbtHIaNI', @@ -535,12 +536,14 @@ def test_revoke_credential(helpers, seeder): rev=sad, ixn=serder.ked, sigs=sigers) - res = client.simulate_delete(path=f"/identifiers/issuer/credentials/{creder.said}", body=json.dumps(badbody).encode("utf-8")) + res = client.simulate_delete(path=f"/identifiers/issuer/credentials/{creder.said}", + body=json.dumps(badbody).encode("utf-8")) assert res.status_code == 400 assert res.json == {'description': "invalid revocation event.", 'title': '400 Bad Request'} - res = client.simulate_delete(path=f"/identifiers/issuer/credentials/{creder.said}", body=json.dumps(body).encode("utf-8")) + res = client.simulate_delete(path=f"/identifiers/issuer/credentials/{creder.said}", + body=json.dumps(body).encode("utf-8")) assert res.status_code == 200 while not agent.registrar.complete(creder.said, sn=1): diff --git a/tests/app/test_ipexing.py b/tests/app/test_ipexing.py index 63dc8771..2c363248 100644 --- a/tests/app/test_ipexing.py +++ b/tests/app/test_ipexing.py @@ -368,6 +368,9 @@ def test_multisig_grant_admit(seeder, helpers): endRolesEnd = aiding.EndRoleCollectionEnd() app.add_route("/identifiers/{name}/endroles", endRolesEnd) + credentialResourceDelEnd = credentialing.CredentialResourceDeleteEnd(aidEnd) + app.add_route("/identifiers/{name}/credentials/{said}", credentialResourceDelEnd) + # Create Issuer Participant 0 ipsalt0 = b'0123456789abcM00' op = helpers.createAid(client0, "issuerParticipant0", ipsalt0) @@ -852,6 +855,50 @@ def test_multisig_grant_admit(seeder, helpers): # Ensure that the credential has been persisted by both agents assert hagent1.rgy.reger.saved.get(keys=(creder.said,)) is not None + # Get latest state + ip0 = client0.simulate_get("/identifiers/issuerParticipant0").json + ip1 = client1.simulate_get("/identifiers/issuerParticipant1").json + + # Create the revocation event from the credential SAID and the registry SAID + issueSaid = regser.said + regser = veventing.revoke(vcdig=creder.said, regk=regk, dt=dt, dig=issueSaid) + + anchor = dict(i=regser.ked['i'], s=regser.ked["s"], d=regser.said) + interact = eventing.interact(pre=issuerPre, dig=interact.said, sn=3, data=[anchor]) + sigs = [issuerSigner0.sign(ser=interact.raw, index=0).qb64, issuerSigner1.sign(ser=interact.raw, index=1).qb64] + + # Submit the Revocation to Agent 0 + body = dict( + rev=regser.ked, + ixn=interact.ked, + sigs=sigs, + group={ + "mhab": ip0, + "keys": ikeys + } + ) + + result = client0.simulate_delete(path=f"/identifiers/issuer/credentials/{creder.said}", + body=json.dumps(body).encode("utf-8")) + + assert result.status == falcon.HTTP_200 + + # Submit the Revocation to Agent 1 + body = dict( + rev=regser.ked, + ixn=interact.ked, + sigs=sigs, + group={ + "mhab": ip1, + "keys": ikeys + } + ) + + result = client1.simulate_delete(path=f"/identifiers/issuer/credentials/{creder.said}", + body=json.dumps(body).encode("utf-8")) + + assert result.status == falcon.HTTP_200 + def test_granter(helpers): with helpers.openKeria() as (agency, agent, app, client): From 90f940d213d7b14e5dcd80a0cfec95fd2ab08cef Mon Sep 17 00:00:00 2001 From: pfeairheller Date: Sun, 25 Feb 2024 18:21:13 -0800 Subject: [PATCH 8/8] Rev version number Signed-off-by: pfeairheller --- Makefile | 2 +- setup.py | 2 +- src/keria/__init__.py | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Makefile b/Makefile index cfc239c9..19dfb9aa 100644 --- a/Makefile +++ b/Makefile @@ -1,6 +1,6 @@ .PHONY: build-keria build-keria: - @docker buildx build --platform=linux/amd64 --no-cache -f images/keria.dockerfile --tag weboftrust/keria:0.1.0 --tag weboftrust/keria:latest . + @docker buildx build --platform=linux/amd64 --no-cache -f images/keria.dockerfile --tag weboftrust/keria:0.1.1 --tag weboftrust/keria:latest . publish-keria: @docker push weboftrust/keria --all-tags \ No newline at end of file diff --git a/setup.py b/setup.py index ca624705..5539d42f 100644 --- a/setup.py +++ b/setup.py @@ -34,7 +34,7 @@ setup( name='keria', - version='0.1.0', # also change in src/keria/__init__.py + version='0.1.1', # also change in src/keria/__init__.py license='Apache Software License 2.0', description='KERIA: KERI Agent in the cloud', long_description="KERIA: KERI Agent in the cloud.", diff --git a/src/keria/__init__.py b/src/keria/__init__.py index e39210af..f609cc86 100644 --- a/src/keria/__init__.py +++ b/src/keria/__init__.py @@ -3,5 +3,5 @@ main package """ -__version__ = '0.1.0' # also change in setup.py +__version__ = '0.1.1' # also change in setup.py