Skip to content
This repository has been archived by the owner on Aug 9, 2021. It is now read-only.

Commit

Permalink
Merge pull request #637 from 3box/release/1.13.1
Browse files Browse the repository at this point in the history
Release/1.13.1
  • Loading branch information
oed authored Oct 25, 2019
2 parents 444b024 + 8c162ca commit 094391e
Show file tree
Hide file tree
Showing 9 changed files with 103 additions and 23 deletions.
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -882,6 +882,7 @@ Join a thread. Use this to start receiving updates from, and to post in threads
| opts.noAutoSub | <code>Boolean</code> | Disable auto subscription to the thread when posting to it (default false) |
| opts.ghost | <code>Boolean</code> | Enable ephemeral messaging via Ghost Thread |
| opts.ghostBacklogLimit | <code>Number</code> | The number of posts to maintain in the ghost backlog |
| opts.ghostFilters | <code>Array.&lt;function()&gt;</code> | Array of functions for filtering messages |
<a name="Space+joinThreadByAddress"></a>
Expand Down
6 changes: 6 additions & 0 deletions RELEASE-NOTES.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,11 @@
# Release Notes

## v1.13.1 - 2019-10-25
* feat: return all messages seen in a ghost thread on getPost
* feat: add filters to ghost threads
* fix: issue with window being referenced in node.js
* fix: disconnect from pinning-room when finished

## v1.13.0 - 2019-10-18
**IdentityWallet v0.2.0** has support for a JSON-RPC provider interface. This release makes use of this interface, which allows wallets to connect to 3box in more ways.

Expand Down
5 changes: 1 addition & 4 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "3box",
"version": "1.13.0",
"version": "1.13.1",
"description": "Interact with user data",
"main": "lib/3box.js",
"directories": {
Expand Down Expand Up @@ -60,7 +60,6 @@
"muport-did-resolver": "^0.3.0",
"node-fetch": "^2.6.0",
"orbit-db": "~0.21.4",
"orbit-db-cache-postmsg-proxy": "^0.1.1",
"store": "^2.0.12",
"tweetnacl": "^1.0.1",
"tweetnacl-util": "^0.15.0"
Expand All @@ -76,9 +75,7 @@
"babel-core": "7.0.0-bridge.0",
"babel-loader": "^8.0.6",
"express": "^4.17.0",
"ganache-cli": "^6.4.3",
"identity-wallet": "^0.2.0",
"ipfsd-ctl": "^0.40.3",
"jest": "^23.6.0",
"jsdoc-to-markdown": "^5.0.0",
"standard": "^14.3.1",
Expand Down
7 changes: 2 additions & 5 deletions src/3box.js
Original file line number Diff line number Diff line change
Expand Up @@ -75,8 +75,6 @@ class Box {
*/
this.syncDone = null

// local store of all pinning server pubsub messages seen related to spaces
this.spacesPubSubMessages = {}
this.hasPublishedLink = {}
}

Expand Down Expand Up @@ -286,7 +284,6 @@ class Box {
if (!this.spaces[name]) {
this.spaces[name] = new Space(name, this.replicator, this._3id)
try {
opts = Object.assign({ numEntriesMessages: this.spacesPubSubMessages }, opts)
await this.spaces[name].open(opts)
if (!await this.isAddressLinked()) this.linkAddress()
} catch (e) {
Expand Down Expand Up @@ -630,7 +627,7 @@ function initIPFSRepo () {
let ipfsRootPath

// if in browser, create unique root storage, and ipfs id on each instance
if (window && window.indexedDB) {
if (typeof window !== 'undefined' && window.indexedDB) {
const sessionID = utils.randInt(10000)
ipfsRootPath = 'ipfs/root/' + sessionID
const levelInstance = new LevelStore(ipfsRootPath)
Expand Down Expand Up @@ -666,7 +663,7 @@ async function initIPFS (ipfs, iframeStore, ipfsOptions) {
})
ipfs.on('ready', () => {
resolve(ipfs)
if (ipfsRepo && window && window.indexedDB) {
if (ipfsRepo && typeof window !== 'undefined' && window.indexedDB) {
// deletes once db is closed again
window.indexedDB.deleteDatabase(ipfsRepo.rootPath)
}
Expand Down
54 changes: 52 additions & 2 deletions src/__tests__/ghost.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -138,11 +138,61 @@ describe('Ghost Chat', () => {
})

afterAll(async () => {
await utils.stopIPFS(ipfs2, 5)
await utils.stopIPFS(ipfs2, 12)
})
})

describe('ghost filter tests', () => {
let chat3
let ipfs3
let filter = (payload, issuer, from) => {
if (issuer == DID1) {
return false
}
return true
}

it('creates third chat correctly', async (done) => {
chat.removeAllListeners()
chat3 = new GhostThread(CHAT_NAME, { ipfs: ipfs3 }, THREEID3_MOCK, { ghostFilters: [filter] });
expect(chat3._name).toEqual(CHAT_NAME)
expect(chat3._3id).toEqual(THREEID3_MOCK)
expect(chat3.listMembers()).toBeDefined()
expect(chat3.getPosts()).toBeDefined()

// checks if chat3 joined properly
chat.on('user-joined', async (_event, did, peerId) => {
expect(_event).toEqual('joined')
expect(did).toEqual(DID3)
done()
})
})

it('chat3 should not catch broadcasts from chat', async (done) => {
chat.removeAllListeners()
chat.onUpdate(async ({ type, author, message }) => {
// chat3 should not have the same backlog as chat
// because messages from DID1 (and by extension chat) are being ignored
if (message = 'wide') {
const posts = await chat.getPosts()
const posts3 = await chat3.getPosts()
expect(posts).not.toEqual(posts3)
done()
}
})
await chat.post('wide')
})

beforeAll(async () => {
ipfs3 = await utils.initIPFS(12)
})

afterAll(async () => {
await utils.stopIPFS(ipfs3, 12)
})
})

afterAll(async () => {
await utils.stopIPFS(ipfs, 4)
await utils.stopIPFS(ipfs, 11)
})
})
1 change: 1 addition & 0 deletions src/__tests__/replicator.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -168,6 +168,7 @@ describe('Replicator', () => {

it('pinningRoomFilter works as expected', async () => {
await new Promise((resolve, reject) => pubsub2.subscribe(PINNING_ROOM, () => {}, resolve))
replicator1._hasPubsubMsgs = {}
replicator1._pinningRoomFilter = null
pubsub2.publish(PINNING_ROOM, { type: 'HAS_ENTRIES', odbAddress: 'fake address 1'})
pubsub2.publish(PINNING_ROOM, { type: 'HAS_ENTRIES', odbAddress: 'fake address 2'})
Expand Down
18 changes: 11 additions & 7 deletions src/ghost.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ const EventEmitter = require('events').EventEmitter
const { verifyJWT } = require('did-jwt')
const Room = require('ipfs-pubsub-room')

const DEFAULT_BACKLOG_LIMIT = 50
const DEFAULT_BACKLOG_LIMIT = 100

class GhostThread extends EventEmitter {
constructor (name, { ipfs }, threeId, opts = {}) {
Expand All @@ -17,15 +17,22 @@ class GhostThread extends EventEmitter {
this._backlog = new Set() // set of past messages
this._backlogLimit = opts.ghostBacklogLimit || DEFAULT_BACKLOG_LIMIT

this._filters = opts.ghostFilters || []

this._room.on('message', async ({ from, data }) => {
const { payload, issuer } = await this._verifyData(data)
if (payload) {

// we pass the payload, issuer and peerID (from) to each filter in our filters array and reduce the value to a single boolean
// this boolean indicates whether the message passed the filters
const passesFilters = this._filters.reduce((acc, filter) => acc && filter(payload, issuer, from), true)

if (payload && passesFilters) {
switch (payload.type) {
case 'join':
this._userJoined(issuer, from)
break
case 'request_backlog':
this.getPosts()
this.getPosts(this._backlogLimit)
.then(posts => this._sendDirect({ type: 'backlog_response', message: posts }, from))
break
case 'backlog_response':
Expand Down Expand Up @@ -70,15 +77,12 @@ class GhostThread extends EventEmitter {
*
* @return {Array<Object>} users online
*/
async getPosts (num = this._backlogLimit) {
async getPosts (num = 0) {
const posts = [...this._backlog]
.map(msg => JSON.parse(msg))
.sort((p1, p2) => p1.timestamp - p2.timestamp)
.slice(-num)

const stringifiedPosts = posts.map(msg => JSON.stringify(msg))
this._backlog = new Set(stringifiedPosts)

return posts
}

Expand Down
33 changes: 28 additions & 5 deletions src/replicator.js
Original file line number Diff line number Diff line change
Expand Up @@ -52,9 +52,20 @@ class Replicator {
this.events.on('pinning-room-message', (topic, data) => {
if (data.type === 'HAS_ENTRIES' && data.odbAddress) {
const odbAddress = data.odbAddress
// Before the pinning room filter is created we will keep all has messages
// in memory. After we only care about the ones that are relevant.
if (this._pinningRoomFilter && !this._pinningRoomFilter.includes(odbAddress)) return
if (this._pinningRoomFilter) {
const hasMsgFor = Object.keys(this._hasPubsubMsgs)
if (this._pinningRoomFilter.length <= hasMsgFor.length) {
const haveAllMsgs = this._pinningRoomFilter.reduce((acc, addr) => acc && hasMsgFor.includes(addr), true)
if (haveAllMsgs) {
this._pubsub.unsubscribe(PINNING_ROOM)
}
}
// Before the pinning room filter is created we will keep all has messages
// in memory. After we only care about the ones that are relevant.
if (!this._pinningRoomFilter.includes(odbAddress)) {
return
}
}
this._hasPubsubMsgs[odbAddress] = data
this.events.emit(`has-${odbAddress}`, data)
}
Expand All @@ -74,6 +85,11 @@ class Replicator {
async _init (opts) {
this._pubsub = new Pubsub(this.ipfs, (await this.ipfs.id()).id)
this._orbitdb = await OrbitDB.createInstance(this.ipfs, { directory: opts.orbitPath })
await this._joinPinningRoom(true)
}

async _joinPinningRoom (firstJoin) {
if (!firstJoin && (await this.ipfs.pubsub.ls()).includes(PINNING_ROOM)) return
this._pubsub.subscribe(PINNING_ROOM, (topic, data) => {
// console.log('message', topic, data)
this.events.emit('pinning-room-message', topic, data)
Expand Down Expand Up @@ -152,6 +168,9 @@ class Replicator {
} else if (!entry) {
await this.rootstore.add({ odbAddress: storeAddress })
}
if (!this._hasPubsubMsgs[storeAddress]) {
this._hasPubsubMsgs[storeAddress] = { numEntries: 0 }
}
return store
}

Expand Down Expand Up @@ -238,11 +257,12 @@ class Replicator {
if (!roomPeers.find(p => p === this._pinningNodePeerId)) {
this.ipfs.swarm.connect(this._pinningNode, () => {})
odbAddress = isThread ? odbAddress : this.rootstore.address.toString()
this._publishDB({ odbAddress, isThread })
this._publishDB({ odbAddress, isThread }, true)
}
}

async _publishDB ({ odbAddress, did, isThread }) {
async _publishDB ({ odbAddress, did, isThread }, unsubscribe) {
this._joinPinningRoom()
odbAddress = odbAddress || this.rootstore.address.toString()
// make sure that the pinning node is in the pubsub room before publishing
const pinningNodeJoined = new Promise((resolve, reject) => {
Expand All @@ -262,6 +282,9 @@ class Replicator {
thread: isThread
})
this.events.removeAllListeners('pinning-room-peer')
if (unsubscribe) {
this._pubsub.unsubscribe(PINNING_ROOM)
}
}

async _getNumEntries (odbAddress) {
Expand Down
1 change: 1 addition & 0 deletions src/space.js
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,7 @@ class Space {
* @param {Boolean} opts.noAutoSub Disable auto subscription to the thread when posting to it (default false)
* @param {Boolean} opts.ghost Enable ephemeral messaging via Ghost Thread
* @param {Number} opts.ghostBacklogLimit The number of posts to maintain in the ghost backlog
* @param {Array<Function>} opts.ghostFilters Array of functions for filtering messages
*
* @return {Thread} An instance of the thread class for the joined thread
*/
Expand Down

0 comments on commit 094391e

Please sign in to comment.