Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

New endpoints and updates to support exn messaging between group participants #98

Merged
merged 1 commit into from
Sep 4, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
34 changes: 11 additions & 23 deletions src/keria/app/agenting.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,15 +12,14 @@
from keri import kering
from keri.app.notifying import Notifier
from keri.app.storing import Mailboxer
from ordered_set import OrderedSet as oset

import falcon
from falcon import media
from hio.base import doing
from hio.core import http
from hio.help import decking
from keri.app import configing, keeping, habbing, storing, signaling, oobiing, agenting, delegating, \
forwarding, querying, connecting
forwarding, querying, connecting, grouping
from keri.app.grouping import Counselor
from keri.app.keeping import Algos
from keri.core import coring, parsing, eventing, routing
Expand All @@ -37,6 +36,8 @@
from keri.app import challenging

from . import aiding, notifying, indirecting, credentialing, presenting
from . import grouping as keriagrouping
from ..peer import exchanging as keriaexchanging
from .specing import AgentSpecResource
from ..core import authing, longrunning, httping
from ..core.authing import Authenticater
Expand Down Expand Up @@ -81,6 +82,8 @@ def setup(name, bran, adminPort, bootPort, base='', httpPort=None, configFile=No
credentialing.loadEnds(app=app, identifierResource=aidEnd)
presenting.loadEnds(app=app)
notifying.loadEnds(app=app)
keriagrouping.loadEnds(app=app)
keriaexchanging.loadEnds(app=app)

if httpPort:
happ = falcon.App(middleware=falcon.CORSMiddleware(
Expand Down Expand Up @@ -274,11 +277,13 @@ def __init__(self, hby, rgy, agentHab, agency, caid, **opts):

signaler = signaling.Signaler()
self.notifier = Notifier(hby=hby, signaler=signaler)
self.mux = grouping.Multiplexor(hby=hby, notifier=self.notifier)

# Initialize all the credential processors
self.verifier = verifying.Verifier(hby=hby, reger=rgy.reger)
self.registrar = credentialing.Registrar(agentHab=agentHab, hby=hby, rgy=rgy, counselor=self.counselor, witPub=self.witPub,
witDoer=self.witDoer, postman=self.postman, verifier=self.verifier)
self.registrar = credentialing.Registrar(agentHab=agentHab, hby=hby, rgy=rgy, counselor=self.counselor,
witPub=self.witPub, witDoer=self.witDoer, postman=self.postman,
verifier=self.verifier)
self.credentialer = credentialing.Credentialer(agentHab=agentHab, hby=self.hby, rgy=self.rgy,
postman=self.postman, registrar=self.registrar,
verifier=self.verifier, notifier=self.notifier)
Expand All @@ -296,7 +301,8 @@ def __init__(self, hby, rgy, agentHab, agency, caid, **opts):
challengeHandler = challenging.ChallengeHandler(db=hby.db, signaler=signaler)

handlers = [issueHandler, requestHandler, proofHandler, applyHandler, challengeHandler]
self.exc = exchanging.Exchanger(db=hby.db, handlers=handlers)
self.exc = exchanging.Exchanger(hby=hby, handlers=handlers)
grouping.loadHandlers(hby=hby, exc=self.exc, mux=self.mux)

self.rvy = routing.Revery(db=hby.db, cues=self.cues)
self.kvy = eventing.Kevery(db=hby.db,
Expand Down Expand Up @@ -470,28 +476,11 @@ def recur(self, tyme):
sigers = msg["sigers"]

ghab = self.hby.habs[serder.pre]
if "smids" in msg:
smids = msg['smids']
else:
smids = ghab.db.signingMembers(pre=ghab.pre)

if "rmids" in msg:
rmids = msg['rmids']
else:
rmids = ghab.db.rotationMembers(pre=ghab.pre)

atc = bytearray() # attachment
atc.extend(coring.Counter(code=coring.CtrDex.ControllerIdxSigs, count=len(sigers)).qb64b)
for siger in sigers:
atc.extend(siger.qb64b)

others = list(oset(smids + (rmids or [])))
others.remove(ghab.mhab.pre) # don't send to self
print(f"Sending multisig event to {len(others)} other participants")
for recpt in others:
self.postman.send(hab=self.agentHab, dest=recpt, topic="multisig", serder=serder,
attachment=atc)

prefixer = coring.Prefixer(qb64=serder.pre)
seqner = coring.Seqner(sn=serder.sn)
saider = coring.Saider(qb64=serder.said)
Expand Down Expand Up @@ -924,7 +913,6 @@ def on_get(req, rep, alias):
rep.status = falcon.HTTP_404
return


rep.status = falcon.HTTP_200
rep.content_type = "application/json"
rep.data = json.dumps(res).encode("utf-8")
Expand Down
11 changes: 8 additions & 3 deletions src/keria/app/credentialing.py
Original file line number Diff line number Diff line change
Expand Up @@ -151,6 +151,9 @@ def on_post(self, req, rep, name):
ked = httping.getRequiredParam(body, "vcp")
vcp = coring.Serder(ked=ked)

ked = httping.getRequiredParam(body, "ixn")
ixn = coring.Serder(ked=ked)

hab = agent.hby.habByName(name)
if hab is None:
raise falcon.HTTPNotFound(description="alias is not a valid reference to an identfier")
Expand All @@ -164,7 +167,9 @@ def on_post(self, req, rep, name):
anchor = dict(i=registry.regk, s="0", d=registry.regk)
# Create registry long running OP that embeds the above received OP or Serder.

agent.registrar.incept(hab, registry)
seqner = coring.Seqner(sn=ixn.sn)
prefixer = coring.Prefixer(qb64=ixn.pre)
agent.registrar.incept(hab, registry, prefixer=prefixer, seqner=seqner, saider=ixn.saider)
op = agent.monitor.submit(hab.kever.prefixer.qb64, longrunning.OpTypes.registry,
metadata=dict(anchor=anchor, depends=op))

Expand Down Expand Up @@ -664,8 +669,8 @@ def incept(self, hab, registry, prefixer=None, seqner=None, saider=None):
hab (Hab): human readable name for the registry
registry (SignifyRegistry): qb64 identifier prefix of issuing identifier in control of this registry
prefixer (Prefixer):
seqner (Seqner):
saider (Saider):
seqner (Seqner): sequence number class of anchoring event
saider (Saider): SAID class of anchoring event

Returns:
Registry: created registry
Expand Down
137 changes: 137 additions & 0 deletions src/keria/app/grouping.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,137 @@
# -*- encoding: utf-8 -*-
"""
KERIA
keria.app.grouping module

"""
import json

import falcon
from keri.app import habbing
from keri.core import coring, eventing

from keria.core import httping


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


class MultisigRequestCollectionEnd:
""" 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

body = req.get_media()

# Get the hab
hab = agent.hby.habByName(name)
if hab is None:
raise falcon.HTTPNotFound(description=f"alias={name} is not a valid reference to an identfier")

# ...and make sure we're a Group
if not isinstance(hab, habbing.SignifyGroupHab):
raise falcon.HTTPBadRequest(description=f"hab for alias {name} is not a multisig")

# grab all of the required parameters
ked = httping.getRequiredParam(body, "exn")
serder = coring.Serder(ked=ked)
sigs = httping.getRequiredParam(body, "sigs")
atc = httping.getRequiredParam(body, "atc")

# create sigers from the edge signatures so we can messagize the whole thing
sigers = [coring.Siger(qb64=sig) for sig in sigs]

# create seal for the proper location to find the signatures
kever = hab.mhab.kever
seal = eventing.SealEvent(i=hab.mhab.pre, s=hex(kever.lastEst.s), d=kever.lastEst.d)

ims = eventing.messagize(serder=serder, sigers=sigers, seal=seal)
ims.extend(atc.encode("utf-8")) # add the pathed attachments
# make a copy and parse
agent.hby.psr.parseOne(ims=bytearray(ims))
# now get rid of the event so we can pass it as atc to send
del ims[:serder.size]

smids = hab.db.signingMembers(pre=hab.pre)
smids.remove(hab.mhab.pre)

for recp in smids: # this goes to other participants
agent.postman.send(hab=agent.agentHab,
dest=recp,
topic="multisig",
serder=serder,
attachment=ims)

rep.status = falcon.HTTP_200
rep.data = json.dumps(serder.ked).encode("utf-8")


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

@staticmethod
def on_get(req, rep, said):
""" GET method for multisig resources

Parameters:
req (falcon.Request): HTTP request object
rep (falcon.Response): HTTP response object
said (str): qb64 SAID of EXN multisig message.

"""
agent = req.context.agent
exn = agent.hby.db.exns.get(keys=(said,))
if exn is None:
raise falcon.HTTPNotFound(f"no multisig request with said={said} found")

route = exn.ked['r']
if not route.startswith("/multisig"):
raise falcon.HTTPBadRequest(f"invalid mutlsig conversation with said={said}")

payload = exn.ked['a']
match route.split("/"):
case ["", "multisig", "icp"]:
pass
case ["", "multisig", *_]:
gid = payload["gid"]
if gid not in agent.hby.habs:
raise falcon.HTTPBadRequest(f"multisig request for non-local group pre={gid}")

esaid = exn.ked['e']['d']
exns = agent.mux.get(esaid=esaid)

for d in exns:
exn = d['exn']
serder = coring.Serder(ked=exn)

route = serder.ked['r']
payload = serder.ked['a']
match route.split("/"):
case ["", "multisig", "icp"]:
pass
case ["", "multisig", "vcp"]:
gid = payload["gid"]
ghab = agent.hby.habs[gid]
d['groupName'] = ghab.name
d['memberName'] = ghab.mhab.name

sender = serder.ked['i']
if (c := agent.org.get(sender)) is not None:
d['sender'] = c['alias']

rep.status = falcon.HTTP_200
rep.data = json.dumps(exns).encode("utf-8")
4 changes: 3 additions & 1 deletion src/keria/app/notifying.py
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,9 @@ def on_get(req, rep):

count = agent.notifier.getNoteCnt()
notes = agent.notifier.getNotes(start=start, end=end)
out = [note.pad for note in notes]
out = []
for note in notes:
out.append(note.pad)

end = start + (len(out) - 1) if len(out) > 0 else 0
rep.set_header("Accept-Ranges", "notes")
Expand Down
18 changes: 4 additions & 14 deletions src/keria/end/ending.py
Original file line number Diff line number Diff line change
Expand Up @@ -51,32 +51,22 @@ def on_get(self, _, rep, aid=None, role=None, eid=None):
"""
if aid is None:
if self.default is None:
rep.status = falcon.HTTP_NOT_FOUND
rep.text = "no blind oobi for this node"
return
raise falcon.HTTPNotFound(description="no blind oobi for this node")

aid = self.default

agent = self.agency.lookup(pre=aid)
if agent is None:
rep.status = falcon.HTTP_NOT_FOUND
rep.text = "AID not found for this OOBI"
return

if aid not in agent.hby.kevers:
rep.status = falcon.HTTP_NOT_FOUND
return
raise falcon.HTTPNotFound(description="AID not found for this OOBI")

kever = agent.hby.kevers[aid]
if not agent.hby.db.fullyWitnessed(kever.serder):
rep.status = falcon.HTTP_NOT_FOUND
return
raise falcon.HTTPNotFound(description=f"{aid} not available")

if kever.prefixer.qb64 in agent.hby.prefixes: # One of our identifiers
hab = agent.hby.habs[kever.prefixer.qb64]
else: # Not allowed to respond
rep.status = falcon.HTTP_NOT_ACCEPTABLE
return
raise falcon.HTTPNotAcceptable(description=f"{aid} is not a local identifier")

eids = []
if eid:
Expand Down
Empty file added src/keria/peer/__init__.py
Empty file.
74 changes: 74 additions & 0 deletions src/keria/peer/exchanging.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
# -*- encoding: utf-8 -*-
"""
KERIA
keria.app.exchanging module

"""
import json

import falcon
from keri.core import coring, eventing

from keria.core import httping


def loadEnds(app):
exnColEnd = ExchangeCollectionEnd()
app.add_route("/identifiers/{name}/exchanges", exnColEnd)


class ExchangeCollectionEnd:

@staticmethod
def on_post(req, rep, name):
""" POST endpoint for exchange message collection """
agent = req.context.agent

body = req.get_media()

# Get the hab
hab = agent.hby.habByName(name)
if hab is None:
raise falcon.HTTPNotFound(description=f"alias={name} is not a valid reference to an identfier")

# Get the exn, sigs, additional attachments and recipients from the request
ked = httping.getRequiredParam(body, "exn")
sigs = httping.getRequiredParam(body, "sigs")
atc = httping.getRequiredParam(body, "atc")
rec = httping.getRequiredParam(body, "rec")
topic = httping.getRequiredParam(body, "tpc")

for recp in rec: # Have to verify we already know all the recipients.
if recp not in agent.hby.kevers:
raise falcon.HTTPBadRequest(f"attempt to send to unknown AID={recp}")

# use that data to create th Serder and Sigers for the exn
serder = coring.Serder(ked=ked)
sigers = [coring.Siger(qb64=sig) for sig in sigs]

# Now create the stream to send, need the signer seal
kever = hab.kever
seal = eventing.SealEvent(i=hab.pre, s=hex(kever.lastEst.s), d=kever.lastEst.d)

ims = eventing.messagize(serder=serder, sigers=sigers, seal=seal)

# Have to add the atc to the end... this will be Pathed signatures for embeds
ims.extend(atc.encode("utf-8")) # add the pathed attachments

# make a copy and parse
agent.hby.psr.parseOne(ims=bytearray(ims))

# now get rid of the event so we can pass it as atc to send
del ims[:serder.size]

for recp in rec: # now let's send it off the all the recipients
agent.postman.send(hab=agent.agentHab,
dest=recp,
topic=topic,
serder=serder,
attachment=ims)

rep.status = falcon.HTTP_200
rep.data = json.dumps(serder.ked).encode("utf-8")


Loading