From 958caccad773d9ef4f37b2f516902371d38f5d06 Mon Sep 17 00:00:00 2001 From: Kent Bull Date: Thu, 5 Sep 2024 10:29:03 -0600 Subject: [PATCH] fix: update smids and rmids for group lead on new event The prior code would fix smids and rmids for all new rotation members on each rotation yet would not fix the smids and rmids for the group lead submitting the multisig event because of the early return when the serder arg is not present. This fixes that Signed-off-by: Kent Bull --- src/keri/app/cli/commands/multisig/rotate.py | 2 +- src/keri/app/habbing.py | 29 ++--- tests/app/test_grouping.py | 110 +++++++++++++++++-- 3 files changed, 118 insertions(+), 23 deletions(-) diff --git a/src/keri/app/cli/commands/multisig/rotate.py b/src/keri/app/cli/commands/multisig/rotate.py index 24888290..56f84ec9 100644 --- a/src/keri/app/cli/commands/multisig/rotate.py +++ b/src/keri/app/cli/commands/multisig/rotate.py @@ -205,7 +205,7 @@ def rotateDo(self, tymth, tock=0.0, **opts): seqner = coring.Seqner(sn=ghab.kever.sn+1) rot = ghab.rotate(isith=self.isith, nsith=self.nsith, toad=self.toad, cuts=list(self.cuts), adds=list(self.adds), data=self.data, - verfers=merfers, digers=migers) + verfers=merfers, digers=migers, smids=smids, rmids=rmids) rserder = serdering.SerderKERI(raw=rot) # Create a notification EXN message to send to the other agents diff --git a/src/keri/app/habbing.py b/src/keri/app/habbing.py index d9431aa4..58ab217d 100644 --- a/src/keri/app/habbing.py +++ b/src/keri/app/habbing.py @@ -2747,25 +2747,26 @@ def make(self, *, code=coring.MtrDex.Blake3_256, transferable=True, isith=None, def rotate(self, smids=None, rmids=None, serder=None, **kwargs): - if serder is None: - return super(GroupHab, self).rotate(**kwargs) - if (habord := self.db.habs.get(keys=(self.pre,))) is None: raise kering.ValidationError(f"Missing HabitatRecord for pre={self.pre}") - # sign handles group hab with .mhab case - sigers = self.sign(ser=serder.raw, verfers=serder.verfers, rotated=True) + if serder is None: + msg = super(GroupHab, self).rotate(**kwargs) + else: - # update own key event verifier state - msg = eventing.messagize(serder, sigers=sigers) + # sign handles group hab with .mhab case + sigers = self.sign(ser=serder.raw, verfers=serder.verfers, rotated=True) - try: - self.kvy.processEvent(serder=serder, sigers=sigers) - except MissingSignatureError: - pass - except Exception as ex: - raise kering.ValidationError("Improper Habitat rotation for " - "pre={self.pre}.") from ex + # update own key event verifier state + msg = eventing.messagize(serder, sigers=sigers) + + try: + self.kvy.processEvent(serder=serder, sigers=sigers) + except MissingSignatureError: + pass + except Exception as ex: + raise kering.ValidationError("Improper Habitat rotation for " + "pre={self.pre}.") from ex self.smids = smids self.rmids = rmids diff --git a/tests/app/test_grouping.py b/tests/app/test_grouping.py index c50d9402..39f8a1c4 100644 --- a/tests/app/test_grouping.py +++ b/tests/app/test_grouping.py @@ -37,7 +37,7 @@ def test_counselor(): parsing.Parser().parse(ims=bytearray(icp3), kvy=kev2, local=True) smids = [hab1.pre, hab2.pre, hab3.pre] - rmids = None # need to fixe this + rmids = [hab1.pre, hab2.pre, hab3.pre] inits = dict(isith='["1/2", "1/2", "1/2"]', nsith='["1/2", "1/2", "1/2"]', toad=0, wits=[]) # Create group hab with init params @@ -84,7 +84,7 @@ def test_counselor(): migers = [hab1.kever.ndigers[0], hab2.kever.ndigers[0]] prefixer = coring.Prefixer(qb64=ghab.pre) seqner = coring.Seqner(sn=ghab.kever.sn + 1) - rot = ghab.rotate(isith="2", nsith="2", toad=0, cuts=list(), adds=list(), verfers=merfers, digers=migers) + rot = ghab.rotate(isith="2", nsith="2", toad=0, cuts=list(), adds=list(), verfers=merfers, digers=migers, smids=smids, rmids=rmids) rserder = serdering.SerderKERI(raw=rot) counselor.start(ghab=ghab, prefixer=prefixer, seqner=seqner, saider=coring.Saider(qb64=rserder.said)) @@ -139,7 +139,7 @@ def test_counselor(): migers = [hab1.kever.ndigers[0], hab2.kever.ndigers[0], hab3.kever.ndigers[0]] prefixer = coring.Prefixer(qb64=ghab.pre) seqner = coring.Seqner(sn=ghab.kever.sn + 1) - rot = ghab.rotate(isith="2", nsith="2", toad=0, cuts=list(), adds=list(), verfers=merfers, digers=migers) + rot = ghab.rotate(isith="2", nsith="2", toad=0, cuts=list(), adds=list(), verfers=merfers, digers=migers, smids=smids, rmids=rmids) rserder = serdering.SerderKERI(raw=rot) counselor.start(ghab=ghab, prefixer=prefixer, seqner=seqner, saider=coring.Saider(qb64=rserder.said)) @@ -195,7 +195,7 @@ def test_counselor(): migers = [hab1.kever.ndigers[0], hab3.kever.ndigers[0]] prefixer = coring.Prefixer(qb64=ghab.pre) seqner = coring.Seqner(sn=ghab.kever.sn + 1) - rot = ghab.rotate(isith="2", nsith="2", toad=0, cuts=list(), adds=list(), verfers=merfers, digers=migers) + rot = ghab.rotate(isith="2", nsith="2", toad=0, cuts=list(), adds=list(), verfers=merfers, digers=migers, smids=smids, rmids=rmids) rserder = serdering.SerderKERI(raw=rot) counselor.start(ghab=ghab, prefixer=prefixer, seqner=seqner, saider=coring.Saider(qb64=rserder.said)) @@ -274,7 +274,7 @@ def test_the_seven(): parsing.Parser().parse(ims=bytearray(icp), kvy=kev, local=True) smids = [hab1.pre, hab2.pre, hab3.pre, hab4.pre, hab5.pre, hab6.pre, hab7.pre] - rmids = None # need to fixe this + rmids = [hab1.pre, hab2.pre, hab3.pre, hab4.pre, hab5.pre, hab6.pre, hab7.pre] inits = dict(isith='["1/3", "1/3", "1/3", "1/3", "1/3", "1/3", "1/3"]', nsith='["1/3", "1/3", "1/3", "1/3", "1/3", "1/3", "1/3"]', toad=0, wits=[]) @@ -377,7 +377,7 @@ def test_the_seven(): prefixer = coring.Prefixer(qb64=ghab.pre) seqner = coring.Seqner(sn=ghab.kever.sn + 1) rot = ghab.rotate(isith='["1/3", "1/3", "1/3"]', nsith='["1/3", "1/3", "1/3", "1/3", "1/3", "1/3", "1/3"]', - toad=0, cuts=list(), adds=list(), verfers=merfers, digers=migers) + toad=0, cuts=list(), adds=list(), verfers=merfers, digers=migers, smids=smids, rmids=rmids) rserder = serdering.SerderKERI(raw=rot) counselor.start(ghab=ghab, prefixer=prefixer, seqner=seqner, saider=coring.Saider(qb64=rserder.said)) @@ -441,7 +441,7 @@ def test_the_seven(): prefixer = coring.Prefixer(qb64=ghab.pre) seqner = coring.Seqner(sn=ghab.kever.sn + 1) rot = ghab.rotate(isith='["1/3", "1/3", "1/3"]', nsith='["1/3", "1/3", "1/3", "1/3", "1/3", "1/3", "1/3"]', - toad=0, cuts=list(), adds=list(), verfers=merfers, digers=migers) + toad=0, cuts=list(), adds=list(), verfers=merfers, digers=migers, smids=smids, rmids=rmids) rserder = serdering.SerderKERI(raw=rot) counselor.start(ghab=ghab, prefixer=prefixer, seqner=seqner, saider=coring.Saider(qb64=rserder.said)) @@ -520,7 +520,7 @@ def test_the_seven(): prefixer = coring.Prefixer(qb64=ghab.pre) seqner = coring.Seqner(sn=ghab.kever.sn + 1) rot = ghab4.rotate(isith='["1/3", "1/3", "1/3"]', nsith='["1/3", "1/3", "1/3"]', - toad=0, cuts=list(), adds=list(), verfers=merfers, digers=migers) + toad=0, cuts=list(), adds=list(), verfers=merfers, digers=migers, smids=smids, rmids=rmids) rserder = serdering.SerderKERI(raw=rot) counselor4.start(ghab=ghab4, prefixer=prefixer, seqner=seqner, saider=coring.Saider(qb64=rserder.said)) @@ -673,6 +673,100 @@ def test_multisig_rotate(mockHelpingNowUTC): assert data["gid"] == ghab1.pre assert "rot" in exn.ked["e"] +def test_multisig_rotate_new_group_member_updates_smids(mockHelpingNowUTC): + # Create a multisig with three members, test_1, test_2, and test_3 + with openMultiSig(prefix="smidstest") as ((hby1, ghab1), (hby2, ghab2), (hby3, ghab3)): + # Create a new member, test_4 + with habbing.openHab(name="smidstest_4", salt=b'0123456789abcdef', transferable=True, temp=True) as (hby4, hab4): + icp4 = hab4.makeOwnEvent(sn=0) # Get test_4's inception event to introduce to group members + + hab1 = hby1.habByName("smidstest_1") + hab2 = hby2.habByName("smidstest_2") + hab3 = hby3.habByName("smidstest_3") + # Create member Kevery instances to parse each other's events and update their keystate + kev1 = eventing.Kevery(db=hab1.db, lax=True, local=False) + kev2 = eventing.Kevery(db=hab2.db, lax=True, local=False) + kev3 = eventing.Kevery(db=hab3.db, lax=True, local=False) + kev4 = eventing.Kevery(db=hab4.db, lax=True, local=False) + + # Introduce test_4 member to group by parsing test_4's inception event (latest key state) + parsing.Parser().parse(ims=bytearray(icp4), kvy=kev1) + parsing.Parser().parse(ims=bytearray(icp4), kvy=kev2) + parsing.Parser().parse(ims=bytearray(icp4), kvy=kev3) + # introduce each member to 4 + parsing.Parser().parse(ims=bytearray(hab1.makeOwnEvent(sn=0)), kvy=kev4) + parsing.Parser().parse(ims=bytearray(hab2.makeOwnEvent(sn=0)), kvy=kev4) + parsing.Parser().parse(ims=bytearray(hab3.makeOwnEvent(sn=0)), kvy=kev4) + + # rotate each individual hab to satisfy the rotation threshold with new keys + hab1.rotate() + hab2.rotate() + hab3.rotate() + + # Update keystate in each hab for each other member + rot1 = hab1.makeOwnEvent(sn=1) # get latest event for hab1 and update keystate for other members + parsing.Parser().parse(ims=bytearray(rot1), kvy=kev2) + parsing.Parser().parse(ims=bytearray(rot1), kvy=kev3) + parsing.Parser().parse(ims=bytearray(rot1), kvy=kev4) + + rot2 = hab2.makeOwnEvent(sn=1) # get latest event for hab2 and update keystate for other members + parsing.Parser().parse(ims=bytearray(rot2), kvy=kev1) + parsing.Parser().parse(ims=bytearray(rot2), kvy=kev3) + parsing.Parser().parse(ims=bytearray(rot2), kvy=kev4) + + rot3 = hab3.makeOwnEvent(sn=1) # get latest event for hab3 and update keystate for other members + parsing.Parser().parse(ims=bytearray(rot3), kvy=kev1) + parsing.Parser().parse(ims=bytearray(rot3), kvy=kev2) + parsing.Parser().parse(ims=bytearray(rot3), kvy=kev4) + + # create signing and rotation member AID lists for upcoming rotation + smids = [hab1.pre, hab2.pre, hab3.pre, hab4.pre] + rmids = [hab1.pre, hab2.pre, hab3.pre, hab4.pre] + + # make group hab for test_4 + ghab4 = hby4.joinGroupHab(hab4.pre, group="smidstest_group4", mhab=hab4, smids=smids, rmids=rmids) + + isith = '["1/4", "1/4", "1/4", "1/4"]' + nsith = '["1/4", "1/4", "1/4", "1/4"]' + merfers = [hab1.kever.verfers[0], hab2.kever.verfers[0], hab3.kever.verfers[0], hab4.kever.verfers[0]] + migers = [hab1.kever.ndigers[0], hab2.kever.ndigers[0], hab3.kever.ndigers[0], hab4.kever.ndigers[0]] + rot = ghab1.rotate(smids=smids, rmids=rmids, isith=isith, nsith=nsith, toad=None, cuts=None, adds=None, data=None, + verfers=merfers, digers=migers) + rserder = serdering.SerderKERI(raw=rot) + + # start counselor for group hab 1 + prefixer = coring.Prefixer(qb64=ghab1.pre) + seqner = coring.Seqner(sn=ghab1.kever.sn + 1) + saider = coring.Saider(qb64=rserder.said) + counselor = grouping.Counselor(hby=hby1) + counselor.start(ghab=ghab1, prefixer=prefixer, seqner=seqner, saider=saider) + + # hab2 signs rotation event and parse into hab1 Kevery + sigers2 = hab2.mgr.sign(rserder.raw, verfers=hab2.kever.verfers, indexed=True, indices=[1]) + msg2 = eventing.messagize(serder=rserder, sigers=sigers2) + parsing.Parser().parse(ims=bytearray(msg2), kvy=kev1) + + # hab3 signs rotation event and parse into hab1 Kevery + sigers3 = hab3.mgr.sign(rserder.raw, verfers=hab3.kever.verfers, indexed=True, indices=[2]) + msg3 = eventing.messagize(serder=rserder, sigers=sigers3) + parsing.Parser().parse(ims=bytearray(msg3), kvy=kev1) + + # hab4 signs rotation event and parse into hab1 Kevery. This should commit the event + sigers4 = hab4.mgr.sign(rserder.raw, verfers=hab4.kever.verfers, indexed=True, indices=[3]) + msg4 = eventing.messagize(serder=rserder, sigers=sigers4) + parsing.Parser().parse(ims=bytearray(msg4), kvy=kev1) + + kev1.processEscrows() # Runs escrows for Kevery1 so he processes all sigs together + + counselor.processEscrows() # Get the rest of the way through counselor. + assert counselor.complete(prefixer=prefixer, seqner=seqner, saider=saider) + assert ghab4.smids == smids + assert ghab4.rmids == rmids + hby1.loadHabs() + ghab1 = hby1.habByName("smidstest_group1") # reload hab to get updated smids and rmids values + assert ghab1.smids == smids + assert ghab1.rmids == rmids + def test_multisig_interact(mockHelpingNowUTC): with openMultiSig(prefix="test") as ((hby1, ghab1), (_, _), (_, _)):