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 c52dfc85..5f5e2041 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 diff --git a/src/keria/app/credentialing.py b/src/keria/app/credentialing.py index 8436061d..f70d0e0c 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, @@ -811,8 +814,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) 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..7b6efe29 100644 --- a/tests/app/test_aiding.py +++ b/tests/app/test_aiding.py @@ -18,13 +18,14 @@ 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 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,23 @@ 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 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") assert res.status_code == 200 @@ -403,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"] diff --git a/tests/app/test_credentialing.py b/tests/app/test_credentialing.py index e8201305..b015f308 100644 --- a/tests/app/test_credentialing.py +++ b/tests/app/test_credentialing.py @@ -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,6 +147,9 @@ 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'} @@ -517,7 +524,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', @@ -531,12 +539,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 76ca941f..c2c42a65 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): 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