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

Adapt listMembers to exclusion and test it #95

Merged
merged 38 commits into from
May 11, 2023
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
Show all changes
38 commits
Select commit Hold shift + click to select a range
fe81368
Add failing listMembers exclusion test and getPickedEpoch
Powersource Apr 18, 2023
fe01c0e
Use epoch functions to fix listMembers
Powersource Apr 19, 2023
6350e08
Remove .only
Powersource Apr 19, 2023
1c5ee7b
Move listMembers exclude tests to listMembers file
Powersource Apr 19, 2023
78f2931
Start to integrate getGroupInfoUpdates
Powersource Apr 20, 2023
23f87f6
Merge branch 'master' of github.com:ssbc/ssb-tribes2 into listmembers…
Powersource Apr 20, 2023
df4556d
Use new box2 version
Powersource Apr 21, 2023
362fa2d
Make getPickedEpoch a stream
Powersource Apr 22, 2023
f54684d
Try to make live listMembers work
Powersource Apr 22, 2023
929c279
Use new getMemberUpdates stream in listMembers instead
Powersource Apr 24, 2023
d1ce6c1
Fix getting empty excluded group object
Powersource Apr 24, 2023
3482198
Test listMembers live with exclude
Powersource Apr 24, 2023
d37c83a
Update listMembers readme for live exclude
Powersource Apr 24, 2023
c6ec4ec
Remove comment
Powersource Apr 24, 2023
bcea87e
Remove .only
Powersource Apr 24, 2023
4d7f63f
Merge branch 'master' of github.com:ssbc/ssb-tribes2 into listmembers…
Powersource May 2, 2023
68b262d
Test adding person to new epoch and checking live list
Powersource May 2, 2023
e309902
Fix small review suggestions
Powersource May 2, 2023
4758a34
Stop live stream in stream to fix switching between epochs
Powersource May 2, 2023
19c2b19
refactor epochs to consolidate getPreferredEpoch
mixmix May 3, 2023
145f853
tests passing
mixmix May 3, 2023
3c75122
Merge branch 'master' of github.com:ssbc/ssb-tribes2 into refactor_epoch
mixmix May 10, 2023
b68fc0f
Merge branch 'master' of github.com:ssbc/ssb-tribes2 into listmembers…
mixmix May 10, 2023
5288576
updates listMembers output, README
mixmix May 10, 2023
366dbf5
listen for new epochs
mixmix May 10, 2023
70cbecf
remove logs
mixmix May 10, 2023
fa49579
Merge branch 'listmembers-exclusion' of github.com:ssbc/ssb-tribes2 i…
mixmix May 10, 2023
833ad63
only log listener errors if ssb is still "open"
mixmix May 10, 2023
850e1c2
add err handler on listener drain
mixmix May 10, 2023
1bd9446
add timeout on bob close
mixmix May 10, 2023
3b6d921
extract ./listeners
mixmix May 11, 2023
430cae7
Merge pull request #104 from ssbc/refactor_epoch
mixmix May 11, 2023
25bdbce
Apply suggestions from code review
mixmix May 11, 2023
a67f6ee
test fixes: safer ssb.close, explicit t.end
mixmix May 11, 2023
bbc964d
Merge branch 'listmembers-exclusion' of github.com:ssbc/ssb-tribes2 i…
mixmix May 11, 2023
d613a48
preferredEpoch (live) now emits on membership updates
mixmix May 11, 2023
5d451ba
Merge pull request #112 from ssbc/listmembers-exclusions-fixes
Powersource May 11, 2023
2fd0c34
Update box2
Powersource May 11, 2023
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
35 changes: 10 additions & 25 deletions index.js
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ const buildGroupId = require('./lib/build-group-id')
const addTangles = require('./lib/tangles/add-tangles')
const publishAndPrune = require('./lib/prune-publish')
const MetaFeedHelpers = require('./lib/meta-feed-helpers')
// const Epochs = require('./lib/epochs')
const Epochs = require('./lib/epochs')

module.exports = {
name: 'tribes2',
Expand All @@ -58,7 +58,7 @@ module.exports = {
findOrCreateGroupWithoutMembers,
getRootFeedIdFromMsgId,
} = MetaFeedHelpers(ssb)
// const { getEpochs } = Epochs(ssb)
const { getPickedEpoch, getMembers } = Epochs(ssb)

function create(opts = {}, cb) {
if (cb === undefined) return promisify(create)(opts)
Expand Down Expand Up @@ -313,31 +313,16 @@ module.exports = {
return pull(
pull.values([0]),
pull.asyncMap((n, cb) => {
get(groupId, (err, group) => {
getPickedEpoch(groupId, (err, pickedEpoch) => {
// prettier-ignore
if (err) return cb(clarify(err, 'Failed to get group info when listing members'))
if (err) return cb(clarify(err, 'todo'))

if (group.excluded) {
return cb(
new Error("We're excluded from this group, can't list members")
)
} else {
const source = pull(
ssb.db.query(
where(and(isDecrypted('box2'), type('group/add-member'))),
opts.live ? live({ old: true }) : null,
Powersource marked this conversation as resolved.
Show resolved Hide resolved
toPullStream()
),
pull.map((msg) => lodashGet(msg, 'value.content.recps', [])),
pull.filter(
(recps) => recps.length > 1 && recps[0] === groupId
),
pull.map((recps) => recps.slice(1)),
pull.flatten(),
pull.unique()
)
return cb(null, source)
}
getMembers(pickedEpoch.id, (err, pickedEpochMembers) => {
// prettier-ignore
if (err) return cb(clarify(err, 'todo'))

cb(null, pickedEpochMembers.members)
})
})
}),
pull.flatten()
Expand Down
103 changes: 66 additions & 37 deletions lib/epochs.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,8 @@ const {
initEpoch: isInitEpoch,
addMember: isAddMember,
exclude: isExclude,
}
}
},
},
} = require('private-group-spec')

const getTangleUpdates = require('./tangles/get-tangle-updates')
Expand All @@ -27,7 +27,7 @@ const msgPattern = toPattern(new Butt64('ssb:message/[a-zA-Z0-9-]+/', null, 32))
const feedPattern = toPattern(new Butt64('ssb:feed/[a-zA-Z0-9-]+/', null, 32))
const secretPattern = toPattern(isCanonicalBase64(null, null, 32))

function toPattern (regexp) {
function toPattern(regexp) {
return regexp.toString().replace(/^\//, '').replace(/\/$/, '')
}

Expand All @@ -39,69 +39,97 @@ const strategy = OverwriteFields({
required: ['author', 'epochKey'],
author: {
type: 'string',
pattern: feedPattern
pattern: feedPattern,
},
epochKey: {
type: 'string',
pattern: secretPattern
}
}
pattern: secretPattern,
},
},
})
strategy.mapToPure = T => T || strategy.identity() // function @tangle/reduce needs
strategy.mapToPure = (T) => T || strategy.identity() // function @tangle/reduce needs

module.exports = function Epochs (ssb) {
function getEpochs (groupId, cb) {
module.exports = function Epochs(ssb) {
function getEpochs(groupId, cb) {
if (cb === undefined) return p(getEpochs)(groupId)

reduceEpochs(ssb, groupId, (err, reduce) => {
if (err) return cb(clarify(err, 'Failed to resolve epoch @tangle/reduce'))

const epochs = reduce.graph.connectedNodes
.map(node => {
return {
id: node.key,
previous: node.previous,
author: node.data[node.key].author,
epochKey: Buffer.from(node.data[node.key].epochKey, 'base64')
}
})
const epochs = reduce.graph.connectedNodes.map((node) => {
return {
id: node.key,
previous: node.previous,
author: node.data[node.key].author,
epochKey: Buffer.from(node.data[node.key].epochKey, 'base64'),
}
})

cb(null, epochs)
})
}

function getMembers (epochRoot, cb) {
/**
* Get the epoch matching the currently picked write key for the group.
*/
function getPickedEpoch(groupId, cb) {
if (cb === undefined) return p(getPickedEpoch)(groupId)

ssb.box2.getGroupInfo(groupId, (err, info) => {
// prettier-ignore
if (err) return cb(clarify(err, 'Failed to get group info for ' + groupId))

// prettier-ignore
if (info.excluded) return cb(new Error("Can't get picked epoch for group we're excluded from"))

getEpochs(groupId, (err, epochs) => {
// prettier-ignore
if (err) return cb(clarify(err, 'todo'))

cb(
null,
epochs.find((epoch) => epoch.epochKey.equals(info.writeKey.key))
)
})
})
}

function getMembers(epochRoot, cb) {
if (cb === undefined) return p(getMembers)(epochRoot)

getEpochMembers(ssb, epochRoot, cb)
}

return {
getEpochs,
getPickedEpoch,
getMembers,
}
}

function reduceEpochs (ssb, groupId, cb) {
function reduceEpochs(ssb, groupId, cb) {
ssb.box2.getGroupInfo(groupId, (err, info) => {
if (err) return cb(clarify(err, 'Failed to get group info for ' + groupId))

ssb.db.get(info.root, (err, rootVal) => {
// prettier-ignore
if (err) return cb(clarify(err, 'Failed to load group root with id ' + info.root))
if (!isInitRoot(rootVal)) return cb(clarify(new Error(isInitRoot.string), 'Malformed group/init root message'))

if (!isInitRoot(rootVal))
// prettier-ignore
return cb(clarify(new Error(isInitRoot.string), 'Malformed group/init root message'))

const root = { key: info.root, value: rootVal }

getTangleUpdates(ssb, 'epoch', info.root, (err, updates) => {
// prettier-ignore
if (err) return cb(clarify(err, 'Failed to updates of group epoch tangle'))

pull(
pull.values([
root,
...updates.filter(isInitEpoch)
]),
pull.values([root, ...updates.filter(isInitEpoch)]),
pull.asyncMap((msg, cb) => {
ssb.metafeeds.findRootFeedId(msg.value.author, (err, feedId) => {
// prettier-ignore
if (err) return cb(clarify(err, 'Failed find root feed id of ' + msg.value.author))

const key = toMsgURI(msg.key)
Expand All @@ -111,13 +139,14 @@ function reduceEpochs (ssb, groupId, cb) {
data: {
[key]: {
author: feedId,
epochKey: msg.value.content.groupKey
}
}
epochKey: msg.value.content.groupKey,
},
},
})
})
}),
pull.collect((err, nodes) => {
// prettier-ignore
if (err) return cb(clarify(err, 'Failure collecting epoch messages'))

const reduce = new Reduce(strategy, { nodes })
Expand All @@ -131,26 +160,26 @@ function reduceEpochs (ssb, groupId, cb) {
})
})
}
function toMsgURI (id) {
function toMsgURI(id) {
return id.startsWith('%') ? fromMessageSigil(id) : id
}

function getEpochMembers (ssb, epochRoot, cb) {
function getEpochMembers(ssb, epochRoot, cb) {
const members = new Set()
const toExclude = new Set()

pull(
getTangleUpdates.stream(ssb, 'members', epochRoot),
pull.filter(msg => isAddMember(msg) || isExclude(msg)),
pull.filter((msg) => isAddMember(msg) || isExclude(msg)),
pull.drain(
msg => {
(msg) => {
const { type, recps, excludes } = msg.value.content
if (type === 'group/add-member')
recps.slice(1).forEach(feedId => members.add(feedId))
else
excludes.forEach(feedId => toExclude.add(feedId))
recps.slice(1).forEach((feedId) => members.add(feedId))
else excludes.forEach((feedId) => toExclude.add(feedId))
},
err => {
(err) => {
// prettier-ignore
if (err) return cb(clarify(err, 'Failed to resolve epoch membership'))
cb(null, {
members: [...members],
Expand Down
24 changes: 22 additions & 2 deletions test/exclude-members.test.js
Powersource marked this conversation as resolved.
Show resolved Hide resolved
Original file line number Diff line number Diff line change
Expand Up @@ -290,7 +290,7 @@ test('Verify that you actually get excluded from a group', async (t) => {
await p(bob.close)(true)
})

test("If you're not the excluder nor the excludee then you should still be in the group and have access to the new epoch", async (t) => {
test.only("If you're not the excluder nor the excludee then you should still be in the group and have access to the new epoch", async (t) => {
// alice creates the group. adds bob and carol. removes bob.
// bob gets added and is removed
// carol stays in the group
Expand Down Expand Up @@ -321,7 +321,7 @@ test("If you're not the excluder nor the excludee then you should still be in th
await carol.tribes2.start()
t.pass('tribes2 started for everyone')

await p(alice.metafeeds.findOrCreate)()
const aliceRoot = await p(alice.metafeeds.findOrCreate)()
const bobRoot = await p(bob.metafeeds.findOrCreate)()
const carolRoot = await p(carol.metafeeds.findOrCreate)()

Expand Down Expand Up @@ -413,6 +413,26 @@ test("If you're not the excluder nor the excludee then you should still be in th
'Carol has a feed for the new key'
)

const aliceMembers = await pull(
alice.tribes2.listMembers(groupId),
pull.collectAsPromise()
)
t.deepEquals(
aliceMembers.sort(),
[aliceRoot.id, carolRoot.id].sort(),
'alice gets the correct members list'
)

const carolMembers = await pull(
carol.tribes2.listMembers(groupId),
pull.collectAsPromise()
)
t.deepEquals(
carolMembers.sort(),
[aliceRoot.id, carolRoot.id].sort(),
'carol gets the correct members list'
)

Powersource marked this conversation as resolved.
Show resolved Hide resolved
await p(alice.close)(true)
await p(bob.close)(true)
await p(carol.close)(true)
Expand Down