From 22e5fd6e6e1c89131fd384695f26642a4f907037 Mon Sep 17 00:00:00 2001 From: Jacob Karlsson Date: Tue, 9 May 2023 12:55:37 +0200 Subject: [PATCH 01/14] Test adding someone back into the group --- test/add-member.test.js | 57 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 57 insertions(+) diff --git a/test/add-member.test.js b/test/add-member.test.js index dba7171..d72c3ce 100644 --- a/test/add-member.test.js +++ b/test/add-member.test.js @@ -272,3 +272,60 @@ test('addMembers too many members', async (t) => { await p(alice.close)(true) }) + +test.only('can or cannot add someone back into a group', async (t) => { + const alice = Testbot({ + keys: ssbKeys.generate(null, 'alice'), + mfSeed: Buffer.from( + '000000000000000000000000000000000000000000000000000000000000a1ce', + 'hex' + ), + }) + const bob = Testbot({ + keys: ssbKeys.generate(null, 'bob'), + mfSeed: Buffer.from( + '0000000000000000000000000000000000000000000000000000000000000b0b', + 'hex' + ), + }) + + await Promise.all([alice.tribes2.start(), bob.tribes2.start()]) + + const bobRoot = await p(bob.metafeeds.findOrCreate)() + + await replicate(alice, bob).catch(t.error) + + const { id: groupId } = await alice.tribes2 + .create() + .catch((err) => t.error(err, 'alice failed to create group')) + + await alice.tribes2 + .addMembers(groupId, [bobRoot.id]) + .then(() => t.pass('added bob')) + .catch((err) => t.error(err, 'add bob fail')) + + await replicate(alice, bob).catch(t.error) + + await bob.tribes2.acceptInvite(groupId) + + await replicate(alice, bob).catch(t.error) + + await alice.tribes2 + .excludeMembers(groupId, [bobRoot.id]) + .then(() => t.pass('alice excluded bob')) + .catch((err) => t.error(err, 'remove member fail')) + + await replicate(alice, bob).catch(t.error) + + await alice.tribes2 + .addMembers(groupId, [bobRoot.id]) + .then(() => t.pass('added bob back in again')) + .catch((err) => t.error(err, 'add bob back fail')) + + await replicate(alice, bob).catch(t.error) + + await bob.tribes2.acceptInvite(groupId).catch(t.error) + + await p(alice.close)(true) + await p(bob.close)(true) +}) From acfaa9b9b3f1b32e74ffa86271cd2605d1c8dda3 Mon Sep 17 00:00:00 2001 From: Jacob Karlsson Date: Tue, 9 May 2023 15:12:44 +0200 Subject: [PATCH 02/14] Update listInvites code to allow joining groups again --- index.js | 29 ++++++++++++++++++----------- test/add-member.test.js | 2 +- 2 files changed, 19 insertions(+), 12 deletions(-) diff --git a/index.js b/index.js index ab883ab..099fc3d 100644 --- a/index.js +++ b/index.js @@ -353,23 +353,25 @@ module.exports = { function listInvites() { const deferedSource = pullDefer.source() - getMyGroups((err, myGroups) => { + getMyReadKeys((err, myReadKeys) => { // prettier-ignore - if (err) return deferedSource.abort(clarify(err, 'Failed to list group IDs when listing invites')) + if (err) return deferedSource.abort(clarify(err, 'Failed to list group readKeys when listing invites')) const source = pull( - // get all the groupIds we've heard of from invites + // get all the additions we've heard of ssb.db.query( where(and(isDecrypted('box2'), type('group/add-member'))), toPullStream() ), pull.filter((msg) => isAddMember(msg)), - pull.map((msg) => msg.value.content.recps[0]), - pull.unique(), + pull.map((msg) => msg.value.content), + pull.unique('groupKey'), - // drop those we're a part of already - pull.filter((groupId) => !myGroups.has(groupId)), + // drop those we're grabbed secrets from already (in case we've been in the group before) + pull.filter((content) => !myReadKeys.has(content.groupKey)), + // groupId + pull.map((content) => content.recps[0]), // gather all the data required for each group-invite pull.asyncMap(getGroupInviteData) ) @@ -381,8 +383,8 @@ module.exports = { // listInvites helpers - function getMyGroups(cb) { - const myGroups = new Set() + function getMyReadKeys(cb) { + const myReadKeys = new Set() pull( pullMany([ @@ -390,11 +392,15 @@ module.exports = { ssb.box2.listGroupIds({ excluded: true }), ]), pull.flatten(), + pull.asyncMap((groupId, cb) => ssb.box2.getGroupInfo(groupId, cb)), pull.drain( - (groupId) => myGroups.add(groupId), + (groupInfo) => + groupInfo.readKeys + .map((readKey) => readKey.key.toString('base64')) + .forEach((readKeyString) => myReadKeys.add(readKeyString)), (err) => { if (err) return cb(err) - return cb(null, myGroups) + return cb(null, myReadKeys) } ) ) @@ -503,6 +509,7 @@ module.exports = { pull.drain( (msg) => { const groupId = msg.value.content.recps[0] + // TODO: also check if it's in one of the epoch tips ssb.box2.excludeGroupInfo(groupId, (err) => { // prettier-ignore if (err) return cb(clarify(err, 'Error on excluding group info after finding exclusion of ourselves')) diff --git a/test/add-member.test.js b/test/add-member.test.js index d72c3ce..f06b225 100644 --- a/test/add-member.test.js +++ b/test/add-member.test.js @@ -273,7 +273,7 @@ test('addMembers too many members', async (t) => { await p(alice.close)(true) }) -test.only('can or cannot add someone back into a group', async (t) => { +test('can or cannot add someone back into a group', async (t) => { const alice = Testbot({ keys: ssbKeys.generate(null, 'alice'), mfSeed: Buffer.from( From 90d9f0e24b71ea620e74e26b493fa9cb716fd226 Mon Sep 17 00:00:00 2001 From: Jacob Karlsson Date: Tue, 9 May 2023 16:17:30 +0200 Subject: [PATCH 03/14] Test restarting bob after un-exclude --- index.js | 1 + package.json | 2 +- test/add-member.test.js | 38 +++++++++++++++++++++++++++++++++++++- 3 files changed, 39 insertions(+), 2 deletions(-) diff --git a/index.js b/index.js index 099fc3d..ed719f4 100644 --- a/index.js +++ b/index.js @@ -501,6 +501,7 @@ module.exports = { live({ old: true }), toPullStream() ), + pull.through((msg) => console.log('exclude msg', msg.value.content)), pull.filter(isExcludeMember), pull.filter((msg) => // it's an exclusion of us diff --git a/package.json b/package.json index 88dc7f7..5ec9271 100644 --- a/package.json +++ b/package.json @@ -49,7 +49,7 @@ "pull-stream": "^3.7.0", "set.prototype.difference": "^1.0.2", "ssb-bfe": "^3.7.0", - "ssb-box2": "^7.0.0", + "ssb-box2": "ssbc/ssb-box2#un-exclude", "ssb-crut": "^4.6.2", "ssb-db2": "^7.1.0", "ssb-meta-feeds": "^0.39.0", diff --git a/test/add-member.test.js b/test/add-member.test.js index f06b225..0df866d 100644 --- a/test/add-member.test.js +++ b/test/add-member.test.js @@ -281,7 +281,8 @@ test('can or cannot add someone back into a group', async (t) => { 'hex' ), }) - const bob = Testbot({ + let bob = Testbot({ + name: 'bobrestart', keys: ssbKeys.generate(null, 'bob'), mfSeed: Buffer.from( '0000000000000000000000000000000000000000000000000000000000000b0b', @@ -324,8 +325,43 @@ test('can or cannot add someone back into a group', async (t) => { await replicate(alice, bob).catch(t.error) + // TODO: test listinvite await bob.tribes2.acceptInvite(groupId).catch(t.error) + async function verifyInGroup(peer) { + // TODO: test listInvites + + await peer.tribes2 + .acceptInvite(groupId) + .then(() => t.fail('consumed invite twice')) + .catch(() => t.pass("can't consume invite twice")) + + const list = await pull(peer.tribes2.list(), pull.collectAsPromise()) + t.equal(list.length, 1, 'one group') + t.equal(list[0].id, groupId, 'id in list is correct') + + // TODO: test if writeKey is there + + // TODO: test if `excluded` is there + } + + await verifyInGroup(bob) + + await p(bob.close)(true).then(() => t.pass("bob's client was closed")) + bob = Testbot({ + rimraf: false, + name: 'bobrestart', + keys: ssbKeys.generate(null, 'bob'), + mfSeed: Buffer.from( + '0000000000000000000000000000000000000000000000000000000000000b0b', + 'hex' + ), + }) + t.pass('bob got a new client') + await bob.tribes2.start().then(() => t.pass('bob restarted')) + + await verifyInGroup(bob) + await p(alice.close)(true) await p(bob.close)(true) }) From 61269d3c6c9df75339ed0c029345ef78306f1fbc Mon Sep 17 00:00:00 2001 From: Jacob Karlsson Date: Wed, 10 May 2023 13:12:29 +0200 Subject: [PATCH 04/14] Change groupKey->secret in unexclude pr --- index.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/index.js b/index.js index 4495f5a..e850cde 100644 --- a/index.js +++ b/index.js @@ -396,10 +396,10 @@ module.exports = { ), pull.filter((msg) => isAddMember(msg)), pull.map((msg) => msg.value.content), - pull.unique('groupKey'), + pull.unique('secret'), // drop those we're grabbed secrets from already (in case we've been in the group before) - pull.filter((content) => !myReadKeys.has(content.groupKey)), + pull.filter((content) => !myReadKeys.has(content.secret)), // groupId pull.map((content) => content.recps[0]), From 9123c5c56232b551adc34129b77c4ff7cd193141 Mon Sep 17 00:00:00 2001 From: Jacob Karlsson Date: Wed, 10 May 2023 13:17:42 +0200 Subject: [PATCH 05/14] Fix duplicate invites --- index.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/index.js b/index.js index e850cde..733baab 100644 --- a/index.js +++ b/index.js @@ -396,13 +396,13 @@ module.exports = { ), pull.filter((msg) => isAddMember(msg)), pull.map((msg) => msg.value.content), - pull.unique('secret'), // drop those we're grabbed secrets from already (in case we've been in the group before) pull.filter((content) => !myReadKeys.has(content.secret)), // groupId pull.map((content) => content.recps[0]), + pull.unique(), // gather all the data required for each group-invite pull.asyncMap(getGroupInviteData) ) From 438cfec02a567a7f4ff9996b9f2df6fffb4381ed Mon Sep 17 00:00:00 2001 From: Jacob Karlsson Date: Wed, 10 May 2023 13:38:50 +0200 Subject: [PATCH 06/14] Don't exclude ourselves on old exclude msgs --- index.js | 21 +++++++++++++++++---- 1 file changed, 17 insertions(+), 4 deletions(-) diff --git a/index.js b/index.js index 733baab..20aa744 100644 --- a/index.js +++ b/index.js @@ -548,7 +548,6 @@ module.exports = { live({ old: true }), toPullStream() ), - pull.through((msg) => console.log('exclude msg', msg.value.content)), pull.filter(isExcludeMember), pull.filter((msg) => // it's an exclusion of us @@ -557,10 +556,24 @@ module.exports = { pull.drain( (msg) => { const groupId = msg.value.content.recps[0] - // TODO: also check if it's in one of the epoch tips - ssb.box2.excludeGroupInfo(groupId, (err) => { + getTipEpochs(groupId, (err, tipEpochs) => { // prettier-ignore - if (err) return cb(clarify(err, 'Error on excluding group info after finding exclusion of ourselves')) + if (err) return cb(clarify(err, 'Error on getting tip epochs after finding exclusion of ourselves')) + + const excludeEpochRootId = + msg.value.content.tangles.members.root + + const excludeIsInTipEpoch = tipEpochs + .map((tip) => tip.id) + .includes(excludeEpochRootId) + + // ignore the exclude if it's an old one (we were added back to the group later) + if (!excludeIsInTipEpoch) return + + ssb.box2.excludeGroupInfo(groupId, (err) => { + // prettier-ignore + if (err) return cb(clarify(err, 'Error on excluding group info after finding exclusion of ourselves')) + }) }) }, (err) => { From a1cd25fb5baa25bcc503bf2803c5f9af3c1ba758 Mon Sep 17 00:00:00 2001 From: Jacob Karlsson Date: Wed, 10 May 2023 14:24:26 +0200 Subject: [PATCH 07/14] Try fixing box2 flake with timeout --- test/add-member.test.js | 1 + 1 file changed, 1 insertion(+) diff --git a/test/add-member.test.js b/test/add-member.test.js index 0db96d6..d67dcaf 100644 --- a/test/add-member.test.js +++ b/test/add-member.test.js @@ -522,6 +522,7 @@ test('can or cannot add someone back into a group', async (t) => { await verifyInGroup(bob) + await p(setTimeout)(500) await p(bob.close)(true).then(() => t.pass("bob's client was closed")) bob = Testbot({ rimraf: false, From c47037e26b0d879613718657b3db998d738dad8a Mon Sep 17 00:00:00 2001 From: Jacob Karlsson Date: Wed, 10 May 2023 14:50:01 +0200 Subject: [PATCH 08/14] Test that bob is back in the group --- test/add-member.test.js | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/test/add-member.test.js b/test/add-member.test.js index d67dcaf..bbbf9e3 100644 --- a/test/add-member.test.js +++ b/test/add-member.test.js @@ -448,7 +448,7 @@ test('addMembers adds to all the tip epochs and gives keys to all the old epochs ]) }) -test('can or cannot add someone back into a group', async (t) => { +test.only('can or cannot add someone back into a group', async (t) => { const alice = Testbot({ keys: ssbKeys.generate(null, 'alice'), mfSeed: Buffer.from( @@ -500,11 +500,17 @@ test('can or cannot add someone back into a group', async (t) => { await replicate(alice, bob).catch(t.error) - // TODO: test listinvite + const invites = await pull(bob.tribes2.listInvites(), pull.collectAsPromise()) + t.equal(invites.length, 1, 'got a reinvite') + t.equal(invites[0].id, groupId, 'got invite to correct group') await bob.tribes2.acceptInvite(groupId).catch(t.error) async function verifyInGroup(peer) { - // TODO: test listInvites + const noInvites = await pull( + bob.tribes2.listInvites(), + pull.collectAsPromise() + ) + t.deepEqual(noInvites, [], "we used the invite so there aren't any left") await peer.tribes2 .acceptInvite(groupId) @@ -515,9 +521,9 @@ test('can or cannot add someone back into a group', async (t) => { t.equal(list.length, 1, 'one group') t.equal(list[0].id, groupId, 'id in list is correct') - // TODO: test if writeKey is there - - // TODO: test if `excluded` is there + const group = await bob.tribes2.get(groupId) + t.notEqual(group.writeKey, undefined, 'bob got writeKey back') + t.equal(group.excluded, undefined, 'bob is not excluded') } await verifyInGroup(bob) From 7a7e1091e2ab9c588390f803e65667c1abf9eec8 Mon Sep 17 00:00:00 2001 From: Jacob Karlsson Date: Wed, 10 May 2023 14:51:15 +0200 Subject: [PATCH 09/14] Remove .only --- test/add-member.test.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/add-member.test.js b/test/add-member.test.js index bbbf9e3..f175e73 100644 --- a/test/add-member.test.js +++ b/test/add-member.test.js @@ -448,7 +448,7 @@ test('addMembers adds to all the tip epochs and gives keys to all the old epochs ]) }) -test.only('can or cannot add someone back into a group', async (t) => { +test('can or cannot add someone back into a group', async (t) => { const alice = Testbot({ keys: ssbKeys.generate(null, 'alice'), mfSeed: Buffer.from( From 5457c458baf42bf5245c7b723a856bf21d25e7f9 Mon Sep 17 00:00:00 2001 From: Jacob Karlsson Date: Thu, 11 May 2023 13:54:36 +0200 Subject: [PATCH 10/14] Use new unexclude box2 release --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 47b10c1..e8b4867 100644 --- a/package.json +++ b/package.json @@ -49,7 +49,7 @@ "pull-stream": "^3.7.0", "set.prototype.difference": "^1.0.2", "ssb-bfe": "^3.7.0", - "ssb-box2": "ssbc/ssb-box2#un-exclude", + "ssb-box2": "^7.1.0", "ssb-crut": "^4.6.2", "ssb-db2": "^7.1.0", "ssb-meta-feeds": "^0.39.0", From fc442e37ae67a66537ff2bb38a0239fcb99153c3 Mon Sep 17 00:00:00 2001 From: Jacob Karlsson Date: Thu, 11 May 2023 19:57:25 +0200 Subject: [PATCH 11/14] Fix lint warnings --- .eslintrc.js | 2 +- lib/operators.js | 2 +- package.json | 1 + test/lib/epochs.test.js | 2 +- test/list-members.test.js | 5 +---- 5 files changed, 5 insertions(+), 7 deletions(-) diff --git a/.eslintrc.js b/.eslintrc.js index bf7549e..0f8eb2e 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -17,6 +17,6 @@ module.exports = { sourceType: 'module', }, rules: { - 'no-console': ['error', { allow: ['error'] }], + 'no-console': ['error', { allow: ['error', 'time', 'timeEnd'] }], }, } diff --git a/lib/operators.js b/lib/operators.js index fa927bd..08ec72a 100644 --- a/lib/operators.js +++ b/lib/operators.js @@ -55,7 +55,7 @@ function seekFirstRecp(buffer, start, p) { if (p < 0) return -1 let pValueFirstRecp - const error = iterate(buffer, p, (_, pointer, key) => { + const error = iterate(buffer, p, (_, pointer) => { pValueFirstRecp = pointer return true }) diff --git a/package.json b/package.json index b250884..2a09ba0 100644 --- a/package.json +++ b/package.json @@ -80,6 +80,7 @@ "test:only": "if grep -r --exclude-dir=node_modules --exclude-dir=.git --color 'test\\.only' ; then exit 1; fi", "test:bail": "npm run test:raw | tap-arc --bail", "test": "npm run test:raw | tap-arc && npm run test:only", + "lint": "eslint .", "format-code": "prettier --write \"**/*.js\"", "format-code-staged": "pretty-quick --staged --pattern \"**/*.js\"", "coverage": "c8 --reporter=lcov npm run test" diff --git a/test/lib/epochs.test.js b/test/lib/epochs.test.js index b41bdd6..57b1f39 100644 --- a/test/lib/epochs.test.js +++ b/test/lib/epochs.test.js @@ -188,7 +188,7 @@ test('lib/epochs (getMissingMembers)', async (t) => { const rootFeeds = await Promise.all( peers.map((peer) => p(peer.metafeeds.findOrCreate)()) ) - const [aliceId, bobId, oscarId] = rootFeeds.map((feed) => feed.id) + const [, bobId, oscarId] = rootFeeds.map((feed) => feed.id) const group = await run('alice creates a group', alice.tribes2.create({})) await sync('to get Additions feeds') diff --git a/test/list-members.test.js b/test/list-members.test.js index a0c8664..194e001 100644 --- a/test/list-members.test.js +++ b/test/list-members.test.js @@ -286,10 +286,7 @@ test('listMembers works with exclusion', async (t) => { const msg = "Bob gets an error when trying to list members of the group he's excluded from" await pull(bob.tribes2.listMembers(groupId), pull.collectAsPromise()) - .then((res) => { - console.log(res) - t.fail(msg) - }) + .then(() => t.fail(msg)) .catch(() => t.pass(msg)) await p(setTimeout)(500) From f438299ca2c67e4e85c50001914e54a83b85596d Mon Sep 17 00:00:00 2001 From: Jacob Karlsson Date: Tue, 16 May 2023 14:51:53 +0200 Subject: [PATCH 12/14] Add lint check to test run --- package.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/package.json b/package.json index 2a09ba0..4266ae2 100644 --- a/package.json +++ b/package.json @@ -19,7 +19,7 @@ "ssb" ], "engines": { - "node": ">=12" + "node": ">=16" }, "main": "index.js", "files": [ @@ -79,7 +79,7 @@ "test:raw": "tape 'test/**/*.test.js'", "test:only": "if grep -r --exclude-dir=node_modules --exclude-dir=.git --color 'test\\.only' ; then exit 1; fi", "test:bail": "npm run test:raw | tap-arc --bail", - "test": "npm run test:raw | tap-arc && npm run test:only", + "test": "npm run test:raw | tap-arc && npm run test:only && npm run lint", "lint": "eslint .", "format-code": "prettier --write \"**/*.js\"", "format-code-staged": "pretty-quick --staged --pattern \"**/*.js\"", From a89e9e43fc336891503d93cab65b96c1c7777f11 Mon Sep 17 00:00:00 2001 From: Jacob Karlsson Date: Tue, 16 May 2023 14:56:16 +0200 Subject: [PATCH 13/14] Don't repeat bob restart config --- test/add-member.test.js | 14 +++++--------- 1 file changed, 5 insertions(+), 9 deletions(-) diff --git a/test/add-member.test.js b/test/add-member.test.js index f175e73..856e452 100644 --- a/test/add-member.test.js +++ b/test/add-member.test.js @@ -448,7 +448,7 @@ test('addMembers adds to all the tip epochs and gives keys to all the old epochs ]) }) -test('can or cannot add someone back into a group', async (t) => { +test('can add someone back into a group', async (t) => { const alice = Testbot({ keys: ssbKeys.generate(null, 'alice'), mfSeed: Buffer.from( @@ -456,14 +456,15 @@ test('can or cannot add someone back into a group', async (t) => { 'hex' ), }) - let bob = Testbot({ + const bobConfig = { name: 'bobrestart', keys: ssbKeys.generate(null, 'bob'), mfSeed: Buffer.from( '0000000000000000000000000000000000000000000000000000000000000b0b', 'hex' ), - }) + } + let bob = Testbot(bobConfig) await Promise.all([alice.tribes2.start(), bob.tribes2.start()]) @@ -531,13 +532,8 @@ test('can or cannot add someone back into a group', async (t) => { await p(setTimeout)(500) await p(bob.close)(true).then(() => t.pass("bob's client was closed")) bob = Testbot({ + ...bobConfig, rimraf: false, - name: 'bobrestart', - keys: ssbKeys.generate(null, 'bob'), - mfSeed: Buffer.from( - '0000000000000000000000000000000000000000000000000000000000000b0b', - 'hex' - ), }) t.pass('bob got a new client') await bob.tribes2.start().then(() => t.pass('bob restarted')) From 21276023e5318907e06eeeb0a7bd5156b7ff0af0 Mon Sep 17 00:00:00 2001 From: Jacob Karlsson Date: Tue, 16 May 2023 14:57:47 +0200 Subject: [PATCH 14/14] Parallel client close --- test/add-member.test.js | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/test/add-member.test.js b/test/add-member.test.js index 856e452..2e2f189 100644 --- a/test/add-member.test.js +++ b/test/add-member.test.js @@ -540,6 +540,5 @@ test('can add someone back into a group', async (t) => { await verifyInGroup(bob) - await p(alice.close)(true) - await p(bob.close)(true) + await Promise.all([p(alice.close)(true), p(bob.close)(true)]) })