diff --git a/integration/app/integration_clienting.py b/integration/app/integration_clienting.py index 584655c..4bed4ab 100644 --- a/integration/app/integration_clienting.py +++ b/integration/app/integration_clienting.py @@ -391,145 +391,6 @@ def test_delegation(): assert icp1.pre == pre -def test_multisig(): - """ This test assumes a running Demo Witnesses and KERIA agent with the following comands: - - `kli witness demo` - `keria start --config-file demo-witness-oobis --config-dir /scripts` - - """ - url = "http://localhost:3901" - bran = b'0123456789abcdefghijk' - tier = Tiers.low - - client = SignifyClient(passcode=bran, tier=tier) - print(client.controller) - assert client.controller == "ELI7pg979AdhmvrjDeam2eAO2SR5niCgnjAJXJHtJose" - - evt, siger = client.ctrl.event() - res = requests.post(url="http://localhost:3903/boot", - json=dict( - icp=evt.ked, - sig=siger.qb64, - stem=client.ctrl.stem, - pidx=1, - tier=client.ctrl.tier)) - - client.connect(url=url) - assert client.agent is not None - assert client.agent.delpre == "ELI7pg979AdhmvrjDeam2eAO2SR5niCgnjAJXJHtJose" - assert client.agent.pre == "EEXekkGu9IAzav6pZVJhkLnjtjM5v3AcyA-pdKUcaGei" - # assert client.ctrl.ridx == 0 - - if res.status_code != requests.codes.accepted: - raise kering.AuthNError(f"unable to initialize cloud agent connection, {res.status_code}, {res.text}") - - identifiers = client.identifiers() - operations = client.operations() - oobis = client.oobis() - - op = identifiers.create("multisig3", bran="0123456789lmnopqrstuv") - icp = op["response"] - serder = coring.Serder(ked=icp) - assert serder.pre == "EOGvmhJDBbJP4zeXaRun5vSz0O3_1zB10DwNMyjXlJEv" - print(f"created AID {serder.pre}") - - identifiers.addEndRole("multisig3", eid=client.agent.pre) - - print(f"OOBI for {serder.pre}:") - oobi = oobis.get("multisig3") - print(oobi) - - op = oobis.resolve(oobi="http://127.0.0.1:5642/oobi/EKYLUMmNPZeEs77Zvclf0bSN5IN-mLfLpx2ySb-HDlk4/witness/BBilc4" - "-L3tFUnfM_wJr4S4OJanAv_VmF_dJNN6vkf2Ha", - alias="multisig1") - - print("resolving oobi for multisig1") - while not op["done"]: - op = operations.get(op["name"]) - sleep(1) - - multisig1 = op["response"] - print("resolving oobi for multisig2") - op = oobis.resolve(oobi="http://127.0.0.1:5642/oobi/EJccSRTfXYF6wrUVuenAIHzwcx3hJugeiJsEKmndi5q1/witness/BBilc4" - "-L3tFUnfM_wJr4S4OJanAv_VmF_dJNN6vkf2Ha", - alias="multisig2") - - while not op["done"]: - op = operations.get(op["name"]) - sleep(1) - multisig2 = op["response"] - - m3 = identifiers.get("multisig3") - agent0 = m3["state"] - print(f"agent is {agent0}") - - states = rstates = [multisig2, multisig1, agent0] - - op = identifiers.create("multisig", algo=Algos.group, mhab=m3, - isith=["1/3", "1/3", "1/3"], nsith=["1/3", "1/3", "1/3"], - toad=3, - wits=[ - "BBilc4-L3tFUnfM_wJr4S4OJanAv_VmF_dJNN6vkf2Ha", - "BLskRTInXnMxWaGqcpSyMgo0nYbalW99cGZESrz3zapM", - "BIKKuvBwpmDVA4Ds-EpL5bt9OqPzWPja2LigFYZN2YfX" - ], - states=states, - rstates=rstates) - print("waiting on multisig creation...") - while not op["done"]: - op = operations.get(op["name"]) - sleep(1) - gAid = op["response"] - print(f"group multisig created {gAid}") - - # Join an interaction event with the group - data = {"i": "EE77q3_zWb5ojgJr-R1vzsL5yiL4Nzm-bfSOQzQl02dy"} - op = identifiers.interact("multisig", data=data) - while not op["done"]: - op = operations.get(op["name"]) - sleep(1) - - ixn = coring.Serder(ked=op["response"]) - events = client.keyEvents() - log = events.get(pre=ixn.pre) - assert len(log) == 2 - - for event in log: - print(coring.Serder(ked=event).pretty()) - - op2 = identifiers.rotate("multisig3") - rot = op2["response"] - serder = coring.Serder(ked=rot) - print(f"rotated multisig3 to {serder.sn}") - - aid1 = identifiers.get("multisig3") - agent0 = aid1["state"] - keyState = client.keyStates() - op = keyState.query(pre="EKYLUMmNPZeEs77Zvclf0bSN5IN-mLfLpx2ySb-HDlk4", sn=1) - while not op["done"]: - op = operations.get(op["name"]) - sleep(1) - - multisig1 = op["response"] - print(f"using key {multisig1['k'][0]}") - print(f"using dig {multisig1['n'][0]}") - - op = keyState.query(pre="EJccSRTfXYF6wrUVuenAIHzwcx3hJugeiJsEKmndi5q1", sn=1) - while not op["done"]: - op = operations.get(op["name"]) - sleep(1) - - multisig2 = op["response"] - print(f"using key {multisig2['k'][0]}") - print(f"using dig {multisig2['n'][0]}") - - states = rstates = [multisig1, multisig2, agent0] - - op = identifiers.rotate("multisig", states=states, rstates=rstates) - print(op) - - def test_randy(): """ This test assumes a running KERIA agent with the following comand: diff --git a/scripts/multisig-endrole.py b/scripts/multisig-endrole.py index d14db93..bdcb145 100644 --- a/scripts/multisig-endrole.py +++ b/scripts/multisig-endrole.py @@ -6,10 +6,6 @@ Testing clienting with integration tests that require a running KERIA Cloud Agent """ -import json -from time import sleep - -from keri.app.keeping import Algos from keri.core.coring import Tiers from signify.app.clienting import SignifyClient diff --git a/scripts/multisig-kli-rotation.py b/scripts/multisig-kli-rotation.py new file mode 100644 index 0000000..f87a2a5 --- /dev/null +++ b/scripts/multisig-kli-rotation.py @@ -0,0 +1,186 @@ +from time import sleep + +import requests +from keri import kering +from keri.app.keeping import Algos +from keri.core import coring, eventing +from keri.core.coring import Tiers +from signify.app.clienting import SignifyClient + + +def create_multisig(): + url = "http://localhost:3901" + bran = b'0123456789abcdefghijk' + tier = Tiers.low + + client = SignifyClient(passcode=bran, tier=tier) + print(client.controller) + assert client.controller == "ELI7pg979AdhmvrjDeam2eAO2SR5niCgnjAJXJHtJose" + + evt, siger = client.ctrl.event() + res = requests.post(url="http://localhost:3903/boot", + json=dict( + icp=evt.ked, + sig=siger.qb64, + stem=client.ctrl.stem, + pidx=1, + tier=client.ctrl.tier)) + + client.connect(url=url) + assert client.agent is not None + assert client.agent.delpre == "ELI7pg979AdhmvrjDeam2eAO2SR5niCgnjAJXJHtJose" + assert client.agent.pre == "EEXekkGu9IAzav6pZVJhkLnjtjM5v3AcyA-pdKUcaGei" + # assert client.ctrl.ridx == 0 + + if res.status_code != requests.codes.accepted: + raise kering.AuthNError(f"unable to initialize cloud agent connection, {res.status_code}, {res.text}") + + identifiers = client.identifiers() + operations = client.operations() + oobis = client.oobis() + exchanges = client.exchanges() + + (_, _, op) = identifiers.create("multisig3", bran="0123456789lmnopqrstuv") + icp = op["response"] + serder = coring.Serder(ked=icp) + assert serder.pre == "EOGvmhJDBbJP4zeXaRun5vSz0O3_1zB10DwNMyjXlJEv" + print(f"created AID {serder.pre}") + + identifiers.addEndRole("multisig3", eid=client.agent.pre) + + print(f"OOBI for {serder.pre}:") + oobi = oobis.get("multisig3") + print(oobi) + + op = oobis.resolve(oobi="http://127.0.0.1:5642/oobi/EKYLUMmNPZeEs77Zvclf0bSN5IN-mLfLpx2ySb-HDlk4/witness", + alias="multisig1") + + print("resolving oobi for multisig1") + while not op["done"]: + op = operations.get(op["name"]) + sleep(1) + print("... done") + + multisig1 = op["response"] + print("resolving oobi for multisig2") + op = oobis.resolve(oobi="http://127.0.0.1:5642/oobi/EJccSRTfXYF6wrUVuenAIHzwcx3hJugeiJsEKmndi5q1/witness", + alias="multisig2") + print("... done") + + while not op["done"]: + op = operations.get(op["name"]) + sleep(1) + multisig2 = op["response"] + + m3 = identifiers.get("multisig3") + agent0 = m3["state"] + print(f"agent is {agent0}") + + states = rstates = [multisig2, multisig1, agent0] + + icp, isigs, op = identifiers.create("multisig", algo=Algos.group, mhab=m3, + isith=["1/3", "1/3", "1/3"], nsith=["1/3", "1/3", "1/3"], + toad=3, + wits=[ + "BBilc4-L3tFUnfM_wJr4S4OJanAv_VmF_dJNN6vkf2Ha", + "BLskRTInXnMxWaGqcpSyMgo0nYbalW99cGZESrz3zapM", + "BIKKuvBwpmDVA4Ds-EpL5bt9OqPzWPja2LigFYZN2YfX" + ], + states=states, + rstates=rstates) + + smids = [state['i'] for state in states] + recp = [state['i'] for state in [multisig2, multisig1]] + + embeds = dict( + icp=eventing.messagize(serder=icp, sigers=[coring.Siger(qb64=sig) for sig in isigs]) + ) + + exchanges.send("multisig3", "multisig", sender=m3, route="/multisig/icp", + payload=dict(gid=icp.pre, smids=smids, rmids=smids), + embeds=embeds, recipients=recp) + + print("waiting on multisig creation...") + while not op["done"]: + op = operations.get(op["name"]) + sleep(1) + gAid = op["response"] + print(f"group multisig created {gAid}") + + # Join an interaction event with the group + data = {"i": "EE77q3_zWb5ojgJr-R1vzsL5yiL4Nzm-bfSOQzQl02dy"} + ixn, xsigs, op = identifiers.interact("multisig", data=data) + + embeds = dict( + ixn=eventing.messagize(serder=ixn, sigers=[coring.Siger(qb64=sig) for sig in xsigs]) + ) + + exchanges.send("multisig3", "multisig", sender=m3, route="/multisig/ixn", + payload=dict(gid=icp.pre, smids=smids), + embeds=embeds, recipients=recp) + + print("waiting for ixn to finish...") + while not op["done"]: + op = operations.get(op["name"]) + sleep(1) + + ixn = coring.Serder(ked=op["response"]) + events = client.keyEvents() + log = events.get(pre=ixn.pre) + assert len(log) == 2 + + for event in log: + print(coring.Serder(ked=event).pretty()) + + (_, _, op2) = identifiers.rotate("multisig3") + rot = op2["response"] + serder = coring.Serder(ked=rot) + print(f"rotated multisig3 to {serder.sn}") + + input("hit any key when other two participants have rotated their AIDs") + + m3 = identifiers.get("multisig3") + agent0 = m3["state"] + + keyState = client.keyStates() + op = keyState.query(pre="EJccSRTfXYF6wrUVuenAIHzwcx3hJugeiJsEKmndi5q1", sn=1) + while not op["done"]: + op = operations.get(op["name"]) + sleep(1) + + multisig2 = op["response"] + print(f"using key {multisig2['k'][0]}") + print(f"using dig {multisig2['n'][0]}") + + op = keyState.query(pre="EKYLUMmNPZeEs77Zvclf0bSN5IN-mLfLpx2ySb-HDlk4", sn=1) + while not op["done"]: + op = operations.get(op["name"]) + sleep(1) + + multisig1 = op["response"] + print(f"using key {multisig1['k'][0]}") + print(f"using dig {multisig1['n'][0]}") + + states = rstates = [multisig1, multisig2, agent0] + + rot, rsigs, op = identifiers.rotate("multisig", states=states, rstates=rstates) + embeds = dict( + rot=eventing.messagize(serder=rot, sigers=[coring.Siger(qb64=sig) for sig in rsigs]) + ) + + smids = [state['i'] for state in states] + recp = [state['i'] for state in [multisig1, multisig2]] + + rexn, _, _ = exchanges.send("multisig3", "multisig", sender=m3, route="/multisig/rot", + payload=dict(gid=icp.pre, smids=smids, rmids=smids), + embeds=embeds, recipients=recp) + + print(rexn.pretty(size=5000)) + print("Waiting for multisig rotation...") + while not op["done"]: + op = operations.get(op["name"]) + sleep(1) + + +if __name__ == "__main__": + create_multisig() diff --git a/src/signify/app/aiding.py b/src/signify/app/aiding.py index 2316d42..2cf01e6 100644 --- a/src/signify/app/aiding.py +++ b/src/signify/app/aiding.py @@ -118,7 +118,7 @@ def interact(self, name, data=None): json[keeper.algo] = keeper.params() res = self.client.put(f"/identifiers/{name}?type=ixn", json=json) - return res.json() + return serder, sigs, res.json() def rotate(self, name, *, transferable=True, nsith=None, toad=None, cuts=None, adds=None, data=None, ncode=MtrDex.Ed25519_Seed, ncount=1, ncodes=None, states=None, rstates=None): @@ -181,7 +181,7 @@ def rotate(self, name, *, transferable=True, nsith=None, toad=None, cuts=None, a json['rmids'] = [state['i'] for state in rstates] res = self.client.put(f"/identifiers/{name}", json=json) - return res.json() + return serder, sigs, res.json() def addEndRole(self, name, *, role=Roles.agent, eid=None, stamp=None): hab = self.get(name) diff --git a/src/signify/core/keeping.py b/src/signify/core/keeping.py index 4509ca4..ae5f117 100644 --- a/src/signify/core/keeping.py +++ b/src/signify/core/keeping.py @@ -53,10 +53,10 @@ def new(self, algo, pidx, **kwargs): def get(self, aid): pre = coring.Prefixer(qb64=aid["prefix"]) if keeping.Algos.salty in aid: - if "pidx" not in aid: - raise kering.ConfigurationError(f"missing pidx in {aid}") kwargs = aid[keeping.Algos.salty] - return SaltyKeeper(salter=self.salter, pidx=aid["pidx"], **kwargs) + if "pidx" not in kwargs: + raise kering.ConfigurationError(f"missing pidx in {kwargs}") + return SaltyKeeper(salter=self.salter, **kwargs) elif keeping.Algos.randy in aid: kwargs = aid[keeping.Algos.randy] diff --git a/src/signify/peer/exchanging.py b/src/signify/peer/exchanging.py index 0eb6439..12c7073 100644 --- a/src/signify/peer/exchanging.py +++ b/src/signify/peer/exchanging.py @@ -50,7 +50,7 @@ def send(self, name, topic, sender, route, payload, embeds, recipients): rec=recipients ) - self.client.post(f"/identifiers/{name}/exchanges", json=body) + return exn, sigs, self.client.post(f"/identifiers/{name}/exchanges", json=body) def createExchangeMessage(self, sender, route, payload, embeds): """ Create exn message from parameters and return Serder with signatures and additional attachments. diff --git a/tests/app/test_aiding.py b/tests/app/test_aiding.py index 9143e27..4e1d137 100644 --- a/tests/app/test_aiding.py +++ b/tests/app/test_aiding.py @@ -339,7 +339,7 @@ def test_aiding_rotate(): expect(mock_client, times=1).put('/identifiers/aid1', json=expected_data).thenReturn(mock_response) expect(mock_response, times=1).json().thenReturn({'success': 'yay'}) - out = ids.rotate(name='aid1', states=[{'i': 'state 1'}, {'i': 'state 2'}], + _, _, out = ids.rotate(name='aid1', states=[{'i': 'state 1'}, {'i': 'state 2'}], rstates=[{'i': 'rstate 1'}, {'i': 'rstate 2'}]) assert out['success'] == 'yay' diff --git a/tests/core/test_keeping.py b/tests/core/test_keeping.py index 17538a6..fdc2dfd 100644 --- a/tests/core/test_keeping.py +++ b/tests/core/test_keeping.py @@ -130,7 +130,7 @@ def test_keeping_manager_get_salty(): mock_keeper = mock(spec=keeping.SaltyKeeper, strict=True) expect(keeping, times=1).SaltyKeeper(salter=mock_salter, pidx=0, dcode='E').thenReturn(mock_keeper) - actual = manager.get({'prefix': 'aid1 prefix', 'pidx': 0, 'salty': {'dcode': 'E'}}) + actual = manager.get({'prefix': 'aid1 prefix', 'salty': {'dcode': 'E', 'pidx': 0}}) assert actual is mock_keeper @@ -151,7 +151,7 @@ def test_keeping_manager_get_salty_pidx(): expect(coring, times=1).Prefixer(qb64='aid1 prefix').thenReturn(mock_prefixer) from keri.kering import ConfigurationError - with pytest.raises(ConfigurationError, match="missing pidx in {'prefix': 'aid1 prefix', 'salty': {'dcode': 'E'}}"): + with pytest.raises(ConfigurationError, match="missing pidx in {'dcode': 'E'}"): manager.get({'prefix': 'aid1 prefix', 'salty': {'dcode': 'E'}}) verifyNoUnwantedInteractions()