Skip to content

Commit

Permalink
feat: multi-sig IPEX apply, offer, agree endpoints (#278)
Browse files Browse the repository at this point in the history
* feat: multi-sig IPEX apply, offer, agree

* fix: atc should only be required for multi-sig

* build: lock keripy version
  • Loading branch information
iFergal committed Aug 20, 2024
1 parent f7e0890 commit 0026757
Show file tree
Hide file tree
Showing 3 changed files with 743 additions and 21 deletions.
2 changes: 1 addition & 1 deletion setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@
python_requires='>=3.12.2',
install_requires=[
'hio>=0.6.14',
'keri>=1.2.0.dev11',
'keri==1.2.0.dev11',
'mnemonic>=0.21',
'multicommand>=1.0.0',
'falcon>=3.1.3',
Expand Down
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'))

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 @@ -313,6 +318,9 @@ def on_post(req, rep, name):
match route:
case "/ipex/apply":
op = IpexApplyCollectionEnd.sendApply(agent, hab, ked, sigs, rec)
case "/multisig/exn":
atc = httping.getRequiredParam(body, "atc")
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 @@ -446,6 +549,9 @@ def on_post(req, rep, name):
match route:
case "/ipex/agree":
op = IpexAgreeCollectionEnd.sendAgree(agent, hab, ked, sigs, rec)
case "/multisig/exn":
atc = httping.getRequiredParam(body, "atc")
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

0 comments on commit 0026757

Please sign in to comment.