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

Add getGroupTangle #3

Merged
merged 19 commits into from
Oct 7, 2022
Merged
Show file tree
Hide file tree
Changes from 15 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
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, recps, encryptionFormat: 'box2' },
Powersource marked this conversation as resolved.
Show resolved Hide resolved
(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
95 changes: 95 additions & 0 deletions lib/get-group-tangle.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
// 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),
})
})
//)
Powersource marked this conversation as resolved.
Show resolved Hide resolved
})
}
}

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 { spec } = crut
Powersource marked this conversation as resolved.
Show resolved Hide resolved
//const isUpdate = (msg) =>
Powersource marked this conversation as resolved.
Show resolved Hide resolved
// crut.isUpdate(getCanonicalContent(msg, spec.getTransformation))
Powersource marked this conversation as resolved.
Show resolved Hide resolved
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()),
//TODO: do we want this?
//pull.filter(isUpdate),
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