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

feat: multi-sig IPEX apply, offer, agree endpoints #278

Merged
Merged
Show file tree
Hide file tree
Changes from 1 commit
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
168 changes: 161 additions & 7 deletions src/keria/app/ipexing.py
Original file line number Diff line number Diff line change
Expand Up @@ -119,7 +119,7 @@ def sendMultisigExn(agent, hab, ked, sigs, atc, rec):

# Have to add the atc to the end... this will be Pathed signatures for embeds
if not atc:
raise falcon.HTTPBadRequest(description=f"attachment missing for ACDC, unable to process request.")
raise falcon.HTTPBadRequest(description=f"attachment missing for multi-sig admit, unable to process request.")

# use that data to create th Serder and Sigers for the exn
serder = serdering.SerderKERI(sad=ked)
Expand All @@ -134,7 +134,6 @@ def sendMultisigExn(agent, hab, ked, sigs, atc, rec):

# make a copy and parse
agent.hby.psr.parseOne(ims=bytearray(ims))
agent.exchanges.append(dict(said=serder.said, pre=hab.pre, rec=rec, topic='credential'))

exn, pathed = exchanging.cloneMessage(agent.hby, serder.said)
if not exn:
Expand All @@ -144,6 +143,8 @@ def sendMultisigExn(agent, hab, ked, sigs, atc, rec):
if grant is None:
raise falcon.HTTPBadRequest(description=f"attempt to admit an invalid grant {admitked['p']}")

agent.exchanges.append(dict(said=serder.said, pre=hab.pre, rec=rec, topic='credential'))
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've moved this below the above checks for the exn message and link to grant etc - presume we don't want to send the /multisig/exn message to others until we have passed those checks


embeds = grant.ked['e']
acdc = embeds["acdc"]
issr = acdc['i']
Expand Down Expand Up @@ -245,6 +246,9 @@ def sendMultisigExn(agent, hab, ked, sigs, atc, rec):
if grant['r'] != "/ipex/grant":
raise falcon.HTTPBadRequest(description=f"invalid route for embedded ipex grant {ked['r']}")

if not atc:
raise falcon.HTTPBadRequest(description=f"attachment missing for multi-sig grant, unable to process request.")

# use that data to create th Serder and Sigers for the exn
serder = serdering.SerderKERI(sad=ked)
sigers = [core.Siger(qb64=sig) for sig in sigs]
Expand All @@ -259,18 +263,19 @@ def sendMultisigExn(agent, hab, ked, sigs, atc, rec):

# make a copy and parse
agent.hby.psr.parseOne(ims=bytearray(ims))
agent.exchanges.append(dict(said=serder.said, pre=hab.pre, rec=rec, topic='credential'))
holder = grant['a']['i']

exn, pathed = exchanging.cloneMessage(agent.hby, serder.said)
if not exn:
raise falcon.HTTPBadRequest(description=f"invalid exn request message {serder.said}")

agent.exchanges.append(dict(said=serder.said, pre=hab.pre, rec=rec, topic='credential'))

grantRec = grant['a']['i']
serder = serdering.SerderKERI(sad=grant)
ims = bytearray(serder.raw) + pathed['exn']
agent.hby.psr.parseOne(ims=ims)
agent.exchanges.append(dict(said=serder.said, pre=hab.pre, rec=[holder], topic="credential"))
agent.grants.append(dict(said=grant['d'], pre=hab.pre, rec=[holder]))
agent.exchanges.append(dict(said=serder.said, pre=hab.pre, rec=[grantRec], topic="credential"))
agent.grants.append(dict(said=grant['d'], pre=hab.pre, rec=[grantRec]))

return agent.monitor.submit(serder.pre, longrunning.OpTypes.exchange, metadata=dict(said=serder.said))

Expand Down Expand Up @@ -306,13 +311,16 @@ def on_post(req, rep, name):

ked = httping.getRequiredParam(body, "exn")
sigs = httping.getRequiredParam(body, "sigs")
atc = httping.getRequiredParam(body, "atc")
rec = httping.getRequiredParam(body, "rec")

route = ked['r']

match route:
case "/ipex/apply":
op = IpexApplyCollectionEnd.sendApply(agent, hab, ked, sigs, rec)
case "/multisig/exn":
op = IpexApplyCollectionEnd.sendMultisigExn(agent, hab, ked, sigs, atc, rec)
case _:
raise falcon.HTTPBadRequest(description=f"invalid message route {route}")

Expand All @@ -333,7 +341,6 @@ def sendApply(agent, hab, ked, sigs, rec):
kever = hab.kever
seal = eventing.SealEvent(i=hab.pre, s="{:x}".format(kever.lastEst.s), d=kever.lastEst.d)

# in this case, ims is a message is a sealed and signed message - signed by Signify (KERIA can't sign anything here...)
ims = eventing.messagize(serder=serder, sigers=sigers, seal=seal)

# make a copy and parse
Expand All @@ -342,6 +349,51 @@ def sendApply(agent, hab, ked, sigs, rec):
agent.exchanges.append(dict(said=serder.said, pre=hab.pre, rec=rec, topic='credential'))
return agent.monitor.submit(serder.pre, longrunning.OpTypes.exchange, metadata=dict(said=serder.said))

@staticmethod
def sendMultisigExn(agent, hab, ked, sigs, atc, rec):
if not isinstance(hab, habbing.SignifyGroupHab):
raise falcon.HTTPBadRequest(description=f"attempt to send multisig message with non-group AID={hab.pre}")

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

embeds = ked['e']
applyked = embeds['exn']
if applyked['r'] != "/ipex/apply":
raise falcon.HTTPBadRequest(description=f"invalid route for embedded ipex apply {ked['r']}")

if not atc:
raise falcon.HTTPBadRequest(description=f"attachment missing for multi-sig apply, unable to process request.")

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

# Now create the stream to send, need the signer seal
kever = hab.mhab.kever
seal = eventing.SealEvent(i=hab.mhab.pre, s="{:x}".format(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))
exn, pathed = exchanging.cloneMessage(agent.hby, serder.said)
if not exn:
raise falcon.HTTPBadRequest(description=f"invalid exn request message {serder.said}")

agent.exchanges.append(dict(said=serder.said, pre=hab.pre, rec=rec, topic='credential'))

applyRec = applyked['a']['i']
serder = serdering.SerderKERI(sad=applyked)
ims = bytearray(serder.raw) + pathed['exn']
agent.hby.psr.parseOne(ims=ims)
agent.exchanges.append(dict(said=serder.said, pre=hab.pre, rec=[applyRec], topic="credential"))

return agent.monitor.submit(serder.pre, longrunning.OpTypes.exchange, metadata=dict(said=serder.said))


class IpexOfferCollectionEnd:

@staticmethod
Expand Down Expand Up @@ -380,6 +432,8 @@ def on_post(req, rep, name):
match route:
case "/ipex/offer":
op = IpexOfferCollectionEnd.sendOffer(agent, hab, ked, sigs, atc, rec)
case "/multisig/exn":
op = IpexOfferCollectionEnd.sendMultisigExn(agent, hab, ked, sigs, atc, rec)
case _:
raise falcon.HTTPBadRequest(description=f"invalid route {route}")

Expand Down Expand Up @@ -409,6 +463,55 @@ def sendOffer(agent, hab, ked, sigs, atc, rec):
agent.exchanges.append(dict(said=serder.said, pre=hab.pre, rec=rec, topic='credential'))
return agent.monitor.submit(serder.pre, longrunning.OpTypes.exchange, metadata=dict(said=serder.said))

@staticmethod
def sendMultisigExn(agent, hab, ked, sigs, atc, rec):
if not isinstance(hab, habbing.SignifyGroupHab):
raise falcon.HTTPBadRequest(description=f"attempt to send multisig message with non-group AID={hab.pre}")

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

embeds = ked['e']
offerked = embeds['exn']
if offerked['r'] != "/ipex/offer":
raise falcon.HTTPBadRequest(description=f"invalid route for embedded ipex offer {ked['r']}")

if not atc:
raise falcon.HTTPBadRequest(description=f"attachment missing for multi-sig offer, unable to process request.")

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

# Now create the stream to send, need the signer seal
kever = hab.mhab.kever
seal = eventing.SealEvent(i=hab.mhab.pre, s="{:x}".format(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))
exn, pathed = exchanging.cloneMessage(agent.hby, serder.said)
if not exn:
raise falcon.HTTPBadRequest(description=f"invalid exn request message {serder.said}")

apply, _ = exchanging.cloneMessage(agent.hby, offerked['p'])
if apply is None:
raise falcon.HTTPBadRequest(description=f"attempt to offer linked to an invalid apply {offerked['p']}")

agent.exchanges.append(dict(said=serder.said, pre=hab.pre, rec=rec, topic='credential'))

offerRec = offerked['a']['i']
serder = serdering.SerderKERI(sad=offerked)
ims = bytearray(serder.raw) + pathed['exn']
agent.hby.psr.parseOne(ims=ims)
agent.exchanges.append(dict(said=serder.said, pre=hab.pre, rec=[offerRec], topic="credential"))

return agent.monitor.submit(serder.pre, longrunning.OpTypes.exchange, metadata=dict(said=serder.said))


class IpexAgreeCollectionEnd:

@staticmethod
Expand Down Expand Up @@ -439,13 +542,16 @@ def on_post(req, rep, name):

ked = httping.getRequiredParam(body, "exn")
sigs = httping.getRequiredParam(body, "sigs")
atc = httping.getRequiredParam(body, "atc")
rec = httping.getRequiredParam(body, "rec")

route = ked['r']

match route:
case "/ipex/agree":
op = IpexAgreeCollectionEnd.sendAgree(agent, hab, ked, sigs, rec)
case "/multisig/exn":
op = IpexAgreeCollectionEnd.sendMultisigExn(agent, hab, ked, sigs, atc, rec)
case _:
raise falcon.HTTPBadRequest(description=f"invalid route {route}")

Expand Down Expand Up @@ -473,3 +579,51 @@ def sendAgree(agent, hab, ked, sigs, rec):

agent.exchanges.append(dict(said=serder.said, pre=hab.pre, rec=rec, topic='credential'))
return agent.monitor.submit(serder.pre, longrunning.OpTypes.exchange, metadata=dict(said=serder.said))

@staticmethod
def sendMultisigExn(agent, hab, ked, sigs, atc, rec):
if not isinstance(hab, habbing.SignifyGroupHab):
raise falcon.HTTPBadRequest(description=f"attempt to send multisig message with non-group AID={hab.pre}")

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

embeds = ked['e']
agreeKed = embeds['exn']
if agreeKed['r'] != "/ipex/agree":
raise falcon.HTTPBadRequest(description=f"invalid route for embedded ipex agree {ked['r']}")

if not atc:
raise falcon.HTTPBadRequest(description=f"attachment missing for multi-sig agree, unable to process request.")

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

# Now create the stream to send, need the signer seal
kever = hab.mhab.kever
seal = eventing.SealEvent(i=hab.mhab.pre, s="{:x}".format(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))
exn, pathed = exchanging.cloneMessage(agent.hby, serder.said)
if not exn:
raise falcon.HTTPBadRequest(description=f"invalid exn request message {serder.said}")

apply, _ = exchanging.cloneMessage(agent.hby, agreeKed['p'])
if apply is None:
raise falcon.HTTPBadRequest(description=f"attempt to agree linked to an invalid offer {agreeKed['p']}")

agent.exchanges.append(dict(said=serder.said, pre=hab.pre, rec=rec, topic='credential'))

agreeRec = agreeKed['a']['i']
serder = serdering.SerderKERI(sad=agreeKed)
ims = bytearray(serder.raw) + pathed['exn']
agent.hby.psr.parseOne(ims=ims)
agent.exchanges.append(dict(said=serder.said, pre=hab.pre, rec=[agreeRec], topic="credential"))

return agent.monitor.submit(serder.pre, longrunning.OpTypes.exchange, metadata=dict(said=serder.said))
Loading
Loading