Skip to content

Commit

Permalink
Multisig join for KERIA agents (WebOfTrust#123)
Browse files Browse the repository at this point in the history
* Adding test coverage to patch.

Signed-off-by: pfeairheller <pfeairheller@gmail.com>

* Update to support querying out of Exchange message processing

Signed-off-by: pfeairheller <pfeairheller@gmail.com>

* Addition of a `join` endpoint that can be used by participants being asked to contribute keys to a rotation to join an existing group multisig.

Signed-off-by: pfeairheller <pfeairheller@gmail.com>

* Fixing credential query tests

Signed-off-by: pfeairheller <pfeairheller@gmail.com>

* Multisig join test coverage

Signed-off-by: pfeairheller <pfeairheller@gmail.com>

---------

Signed-off-by: pfeairheller <pfeairheller@gmail.com>
  • Loading branch information
pfeairheller authored Nov 10, 2023
1 parent 839ce4f commit 56c20c7
Show file tree
Hide file tree
Showing 5 changed files with 289 additions and 46 deletions.
46 changes: 33 additions & 13 deletions src/keria/app/agenting.py
Original file line number Diff line number Diff line change
Expand Up @@ -200,8 +200,9 @@ def create(self, caid):
configDir=self.configDir,
configFile=self.configFile)

self.adb.agnt.pin(keys=(caid,),
val=coring.Prefixer(qb64=agent.pre))
res = self.adb.agnt.pin(keys=(caid,),
val=coring.Prefixer(qb64=agent.pre))

self.adb.ctrl.pin(keys=(agent.pre,),
val=coring.Prefixer(qb64=caid))

Expand Down Expand Up @@ -267,8 +268,8 @@ def incept(self, caid, pre):


class Agent(doing.DoDoer):
"""
"""
The top level object and DoDoer representing a Habery for a remote controller and all associated processing
"""
Expand Down Expand Up @@ -365,7 +366,7 @@ def __init__(self, hby, rgy, agentHab, agency, caid, **opts):
Admitter(hby=hby, witq=self.witq, psr=self.parser, agentHab=agentHab, exc=self.exc, admits=self.admits),
GroupRequester(hby=hby, agentHab=agentHab, counselor=self.counselor, groups=self.groups),
SeekerDoer(seeker=self.seeker, cues=self.verifier.cues),
ExnSeekerDoer(seeker=self.exnseeker, cues=self.exc.cues)
ExchangeCueDoer(seeker=self.exnseeker, cues=self.exc.cues, queries=self.queries)
])

super(Agent, self).__init__(doers=doers, always=True, **opts)
Expand Down Expand Up @@ -554,21 +555,32 @@ def recur(self, tyme=None):
return False


class ExnSeekerDoer(doing.Doer):
class ExchangeCueDoer(doing.Doer):

def __init__(self, seeker, cues):
def __init__(self, seeker, cues, queries):
self.seeker = seeker
self.cues = cues
self.queries = queries

super(ExnSeekerDoer, self).__init__()
super(ExchangeCueDoer, self).__init__()

def recur(self, tyme=None):
if self.cues:
cue = self.cues.popleft()
if cue["kin"] == "saved":
said = cue["said"]
print(f"indexing exn said={said}")
self.seeker.index(said=said)
try:
self.seeker.index(said=said)
except Exception:
self.cues.append(cue)
return False
elif cue["kin"] == "query":
print("passing it along to the querier!")
self.queries.append(cue['q'])
return False
else:
self.cues.append(cue)
return False


class Initer(doing.Doer):
Expand Down Expand Up @@ -626,6 +638,9 @@ def recur(self, tyme, deeds=None):
""" Processes query reqests submitting any on the cue"""
if self.queries:
msg = self.queries.popleft()
if "pre" not in msg:
return False

pre = msg["pre"]

if "sn" in msg:
Expand Down Expand Up @@ -1012,7 +1027,8 @@ def on_get(req, rep, alias):
if role in (kering.Roles.witness,): # Fetch URL OOBIs for all witnesses
oobis = []
for wit in hab.kever.wits:
urls = hab.fetchUrls(eid=wit, scheme=kering.Schemes.http) or hab.fetchUrls(eid=wit, scheme=kering.Schemes.https)
urls = hab.fetchUrls(eid=wit, scheme=kering.Schemes.http) or hab.fetchUrls(eid=wit,
scheme=kering.Schemes.https)
if not urls:
raise falcon.HTTPNotFound(description=f"unable to query witness {wit}, no http endpoint")

Expand All @@ -1022,7 +1038,8 @@ def on_get(req, rep, alias):
res["oobis"] = oobis
elif role in (kering.Roles.controller,): # Fetch any controller URL OOBIs
oobis = []
urls = hab.fetchUrls(eid=hab.pre, scheme=kering.Schemes.http) or hab.fetchUrls(eid=hab.pre, scheme=kering.Schemes.https)
urls = hab.fetchUrls(eid=hab.pre, scheme=kering.Schemes.http) or hab.fetchUrls(eid=hab.pre,
scheme=kering.Schemes.https)
if not urls:
raise falcon.HTTPNotFound(description=f"unable to query controller {hab.pre}, no http endpoint")

Expand All @@ -1032,7 +1049,10 @@ def on_get(req, rep, alias):
res["oobis"] = oobis
elif role in (kering.Roles.agent,):
oobis = []
roleUrls = hab.fetchRoleUrls(hab.pre, scheme=kering.Schemes.http, role=kering.Roles.agent) or hab.fetchRoleurls(hab.pre, scheme=kering.Schemes.https, role=kering.Roles.agent)
roleUrls = hab.fetchRoleUrls(hab.pre, scheme=kering.Schemes.http,
role=kering.Roles.agent) or hab.fetchRoleurls(hab.pre,
scheme=kering.Schemes.https,
role=kering.Roles.agent)
if not roleUrls:
raise falcon.HTTPNotFound(description=f"unable to query controller {hab.pre}, no http endpoint")

Expand Down
13 changes: 6 additions & 7 deletions src/keria/app/credentialing.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ def loadEnds(app, identifierResource):
app.add_route("/identifiers/{name}/credentials/{said}", credentialResourceEnd)

queryCollectionEnd = CredentialQueryCollectionEnd()
app.add_route("/identifiers/{name}/credentials/query", queryCollectionEnd)
app.add_route("/credentials/query", queryCollectionEnd)


class RegistryCollectionEnd:
Expand Down Expand Up @@ -307,13 +307,12 @@ class CredentialQueryCollectionEnd:
"""

@staticmethod
def on_post(req, rep, name):
def on_post(req, rep):
""" Credentials GET endpoint
Parameters:
req: falcon.Request HTTP request
rep: falcon.Response HTTP response
name (str): human readable alias for AID to use as issuer
---
summary: List credentials in credential store (wallet)
Expand Down Expand Up @@ -352,10 +351,6 @@ def on_post(req, rep, name):
"""
agent = req.context.agent
hab = agent.hby.habByName(name)
if hab is None:
raise falcon.HTTPNotFound(description="name is not a valid reference to an identifier")

try:
body = req.get_media()
if "filter" in body:
Expand Down Expand Up @@ -387,6 +382,10 @@ def on_post(req, rep, name):
saids = [coring.Saider(qb64=said) for said in cur]
creds = agent.rgy.reger.cloneCreds(saids=saids, db=agent.hby.db)

end = skip + (len(creds) - 1) if len(creds) > 0 else 0
rep.set_header("Accept-Ranges", "credentials")
rep.set_header("Content-Range", f"credentials {skip}-{end}/{limit}")

rep.status = falcon.HTTP_200
rep.content_type = "application/json"
rep.data = json.dumps(creds).encode("utf-8")
Expand Down
82 changes: 81 additions & 1 deletion src/keria/app/grouping.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,14 @@
from keri.app import habbing
from keri.core import coring, eventing

from keria.core import httping
from keria.core import httping, longrunning


def loadEnds(app):
msrCol = MultisigRequestCollectionEnd()
app.add_route("/identifiers/{name}/multisig/request", msrCol)
joinCol = MultisigJoinCollectionEnd()
app.add_route("/identifiers/{name}/multisig/join", joinCol)
msrRes = MultisigRequestResourceEnd()
app.add_route("/multisig/request/{said}", msrRes)

Expand Down Expand Up @@ -80,6 +82,75 @@ def on_post(req, rep, name):
rep.data = json.dumps(serder.ked).encode("utf-8")


class MultisigJoinCollectionEnd:
""" Collection endpoint class for creating mulisig exn requests from """

@staticmethod
def on_post(req, rep, name):
""" POST method for multisig request collection
Parameters:
req (falcon.Request): HTTP request object
rep (falcon.Response): HTTP response object
name (str): AID of Hab to load credentials for
"""
agent = req.context.agent

# Get the hab
hab = agent.hby.habByName(name)
if hab is not None:
raise falcon.HTTPBadRequest(description=f"attempt to create identifier with an already used alias={name}")

agent = req.context.agent
body = req.get_media()

# Get the rot, sigs and recipients from the request
rot = httping.getRequiredParam(body, "rot")
sigs = httping.getRequiredParam(body, "sigs")

# Get group specific values
gid = httping.getRequiredParam(body, "gid")
smids = httping.getRequiredParam(body, "smids")
rmids = httping.getRequiredParam(body, "rmids")

both = list(set(smids + (rmids or [])))
for recp in both: # Have to verify we already know all the recipients.
if recp not in agent.hby.kevers:
agent.hby.deleteHab(name=name)
raise falcon.HTTPBadRequest(description=f"attempt to merge with unknown AID={recp}")

sigers = [coring.Siger(qb64=sig) for sig in sigs]
verfers = [coring.Verfer(qb64=k) for k in rot['k']]
digers = [coring.Diger(qb64=n) for n in rot['n']]

mhab = None
for mid in both:
if mid in agent.hby.habs:
mhab = agent.hby.habs[mid]
break

if mhab is None:
raise falcon.HTTPBadRequest(description="Invalid multisig group rotation request,"
" signing member list must contain a local identifier'")

hab = agent.hby.joinSignifyGroupHab(gid, name=name, mhab=mhab, smids=smids, rmids=rmids)
try:
hab.make(serder=coring.Serder(ked=rot), sigers=sigers)
agent.inceptGroup(pre=gid, mpre=mhab.pre, verfers=verfers, digers=digers)
except ValueError as e:
agent.hby.deleteHab(name=name)
raise falcon.HTTPBadRequest(description=f"{e.args[0]}")

serder = coring.Serder(ked=rot)
agent.groups.append(dict(pre=hab.pre, serder=serder, sigers=sigers, smids=smids, rmids=rmids))
op = agent.monitor.submit(serder.pre, longrunning.OpTypes.group, metadata=dict(sn=0))

rep.content_type = "application/json"
rep.status = falcon.HTTP_202
rep.data = op.to_json().encode("utf-8")


class MultisigRequestResourceEnd:
""" Resource endpoint class for getting full data for a mulisig exn request from a notification """

Expand All @@ -106,6 +177,8 @@ def on_get(req, rep, said):
match route.split("/"):
case ["", "multisig", "icp"]:
pass
case ["", "multisig", "rot"]:
pass
case ["", "multisig", *_]:
gid = payload["gid"]
if gid not in agent.hby.habs:
Expand All @@ -123,6 +196,13 @@ def on_get(req, rep, said):
match route.split("/"):
case ["", "multisig", "icp"]:
pass
case ["", "multisig", "rot"]:
gid = payload["gid"]
if gid in agent.hby.habs:
ghab = agent.hby.habs[gid]
d['groupName'] = ghab.name
d['memberName'] = ghab.mhab.name

case ["", "multisig", "vcp"]:
gid = payload["gid"]
ghab = agent.hby.habs[gid]
Expand Down
Loading

0 comments on commit 56c20c7

Please sign in to comment.