Skip to content

Commit

Permalink
Merge pull request #3 from ssbc/get-group-tangle
Browse files Browse the repository at this point in the history
Add getGroupTangle
  • Loading branch information
Powersource committed Oct 7, 2022
2 parents fa64676 + 11f57f0 commit 2b740c7
Show file tree
Hide file tree
Showing 10 changed files with 392 additions and 46 deletions.
27 changes: 18 additions & 9 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -39,15 +39,18 @@ Then
ssb.tribes2.start()

ssb.tribes2.create({}, (err, group) => {
ssb.db.create({
keys: group.mySubfeedKeys,
content: {
type: 'post',
text: 'welcome to the group',
recps: [group.id]
ssb.db.create(
{
keys: group.mySubfeedKeys,
content: {
type: 'post',
text: 'welcome to the group',
recps: [group.id],
},
encryptionFormat: 'box2',
},
encryptionFormat: 'box2'
}, cb)
cb
)
})
```

Expand Down Expand Up @@ -86,7 +89,13 @@ to join the group.

- `groupId` _CloakedId_ - the public-safe cipherlink which identifies the group (same as in #create)
- `feedIds` _[FeedId]_ - an Array of 1-16 different ids for peers (accepts ssb-uri or sigil feed ids)
- `cb` _Function_ - a callback of signature `(err)`
- `cb` _Function_ - a callback of signature `(err, msg)`

### `ssb.tribes2.publish(content, cb)`

Publishes any kind of message encrypted to the group. The function wraps `ssb.db.create()` but handles adding tangles and using the correct encryption for the `content.recps` that you've provided. Mutates `content`.

- `cb` _Function_ - a callback of signature `(err, msg)`

### `ssb.tribes2.start()`

Expand Down
46 changes: 34 additions & 12 deletions index.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ const { keySchemes } = require('private-group-spec')
const { SecretKey } = require('ssb-private-group-keys')
//const Crut = require('ssb-crut')
const buildGroupId = require('./lib/build-group-id')
const addGroupTangle = require('./lib/add-group-tangle')
const AddGroupTangle = require('./lib/add-group-tangle')

module.exports = {
name: 'tribes2',
Expand All @@ -30,6 +30,8 @@ module.exports = {
start: 'async',
},
init(ssb, config) {
const addGroupTangle = AddGroupTangle(ssb)

function create(opts = {}, cb) {
if (cb === undefined) return promisify(create)(opts)

Expand Down Expand Up @@ -129,22 +131,41 @@ module.exports = {

if (opts.text) content.text = opts.text

addGroupTangle(content, (err, content) => {
//if (!addMemberSpec.isValid(content))
// return cb(new Error(addMemberSpec.isValid.errorsString))

publish(content, (err, msg) => {
if (err) return cb(err)

//if (!addMemberSpec.isValid(content))
// return cb(new Error(addMemberSpec.isValid.errorsString))
//TODO: this is an optimization, use the db query instead for now
//keystore.group.registerAuthors(groupId, feedIds, (err) => {
// if (err) return cb(err)
return cb(null, msg)
//})
})
})
}

function publish(content, cb) {
if (cb === undefined) return promisify(publish)(content)

ssb.db.create({ content, recps, encryptionFormat: 'box2' }, (err) => {
if (!content) return cb(new Error('Missing content'))

const recps = content.recps
if (!recps || !Array.isArray(recps) || recps.length < 1)
return cb(new Error('Missing recps'))

addGroupTangle(content, (err, content) => {
if (err) return cb(err)

ssb.db.create(
{ content, encryptionFormat: 'box2' },
(err, msg) => {
if (err) return cb(err)

//TODO: this is an optimization, use the db query instead for now
//keystore.group.registerAuthors(groupId, feedIds, (err) => {
// if (err) return cb(err)
cb()
//})
})
})
cb(null, msg)
}
)
})
}

Expand Down Expand Up @@ -189,6 +210,7 @@ module.exports = {
get,
list,
addMembers,
publish,
start,
}
},
Expand Down
21 changes: 11 additions & 10 deletions lib/add-group-tangle.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,19 +4,19 @@

const { isCloakedMsg } = require('ssb-ref')
const set = require('lodash.set')
const GetGroupTangle = require('./get-group-tangle')

//const getGroupTangle = GetGroupTangle(ssb, keystore)
module.exports = function AddGroupTangle(server) {
const getGroupTangle = GetGroupTangle(server)

//ssb.publish.hook(function (publish, args) {
module.exports = function addGroupTangle(content, cb) {
//TODO: actually make this function work
return cb(null, content)
/**
* Note that this mutates `content`
*/
return function addGroupTangle(content, cb) {
if (!content.recps) return cb(null, content)

if (!content.recps) return cb(null, content)
if (!isCloakedMsg(content.recps[0])) return cb(null, content)

if (!isCloakedMsg(content.recps[0])) return cb(null, content)

onKeystoreReady(() => {
getGroupTangle(content.recps[0], (err, tangle) => {
// NOTE there are two ways an err can occur in getGroupTangle
// 1. recps is not a groupId
Expand All @@ -26,11 +26,12 @@ module.exports = function addGroupTangle(content, cb) {
if (err) return cb(null, content)

set(content, 'tangles.group', tangle)
//TODO: uncomment
//tanglePrune(content) // prune the group tangle down if needed

cb(null, content)
})
})
}
}

/* eslint-disable camelcase */
Expand Down
89 changes: 89 additions & 0 deletions lib/get-group-tangle.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
// SPDX-FileCopyrightText: 2022 Mix Irving
//
// SPDX-License-Identifier: LGPL-3.0-only

const { isCloakedMsg: isGroup } = require('ssb-ref')
const pull = require('pull-stream')
const Reduce = require('@tangle/reduce')
const Strategy = require('@tangle/strategy')

// for figuring out what "previous" should be for the group

const strategy = new Strategy({})

module.exports = function GetGroupTangle(server) {
const getUpdates = GetUpdates(server)

return function getGroupTangle(groupId, cb) {
if (!isGroup(groupId))
return cb(
new Error(`get-group-tangle expects valid groupId, got: ${groupId}`)
)

server.box2.getGroupKeyInfo(groupId, (err, info) => {
if (err) return cb(err)

if (!info)
return cb(new Error(`get-group-tangle: unknown groupId ${groupId}`))

getUpdates(info.root, (err, msgs) => {
if (err) return cb(err)

const nodes = msgs.map((msg) => ({
key: msg.key,
previous: msg.value.content.tangles.group.previous,
}))
// NOTE: getUpdates query does not get root node
nodes.push({ key: info.root, previous: null })

// Create a Reduce using the message contents
// NOTE - do NOT store the whole msg (node)
// we're not doing any reducing of transformations, we care only about
// reducing the graph to find the tips
// each node should be pruned down to e.g. { key: '%D', previous: ['%B', '%C'] }

const reduce = new Reduce(strategy, { nodes })
cb(null, {
root: info.root,
previous: Object.keys(reduce.state),
})
})
})
}
}

const B_VALUE = Buffer.from('value')
const B_CONTENT = Buffer.from('content')
const B_TANGLES = Buffer.from('tangles')
const B_ROOT = Buffer.from('root')

function GetUpdates(ssb) {
const { seekKey } = require('bipf')
const B_TANGLE = Buffer.from('group')

function tangleRoot(value) {
return ssb.db.operators.equal(seekTangleRoot, value, {
indexType: `value.content.tangles.group.root`,
})
}
function seekTangleRoot(buffer) {
let p = 0 // note you pass in p!
p = seekKey(buffer, p, B_VALUE)
if (p < 0) return
p = seekKey(buffer, p, B_CONTENT)
if (p < 0) return
p = seekKey(buffer, p, B_TANGLES)
if (p < 0) return
p = seekKey(buffer, p, B_TANGLE)
if (p < 0) return
return seekKey(buffer, p, B_ROOT)
}

return function getUpdates(id, cb) {
const { where, and, toPullStream } = ssb.db.operators
pull(
ssb.db.query(where(and(tangleRoot(id))), toPullStream()),
pull.collect(cb)
)
}
}
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@
"pull-stream": "^3.6.14",
"ssb-bfe": "^3.6.0",
"ssb-box2": "^3.0.0",
"ssb-crut": "^4.6.0",
"ssb-crut": "^4.6.1",
"ssb-meta-feeds": "~0.30.0",
"ssb-private-group-keys": "^1.0.0"
},
Expand Down
14 changes: 2 additions & 12 deletions test/add-member.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,20 +3,10 @@
// SPDX-License-Identifier: CC0-1.0

const test = require('tape')
const ref = require('ssb-ref')
const Testbot = require('./testbot')
const { promisify: p } = require('util')
const pull = require('pull-stream')
const ssbKeys = require('ssb-keys')

async function replicate(person1, person2) {
person1.ebt.request(person1.id, true)
person2.ebt.request(person2.id, true)
person1.ebt.request(person2.id, true)
person2.ebt.request(person1.id, true)
await p(person1.connect)(person2.getAddress())
await new Promise((res) => setTimeout(res, 3000))
}
const Testbot = require('./helpers/testbot')
const replicate = require('./helpers/replicate')

test('get added to a group', async (t) => {
const alice = Testbot({ keys: ssbKeys.generate(null, 'alice') })
Expand Down
2 changes: 1 addition & 1 deletion test/create.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

const test = require('tape')
const ref = require('ssb-ref')
const Testbot = require('./testbot')
const Testbot = require('./helpers/testbot')
const { promisify: p } = require('util')
const pull = require('pull-stream')

Expand Down

0 comments on commit 2b740c7

Please sign in to comment.