corestore 6.0.1-alpha.11 → 6.0.1-alpha.14
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.github/workflows/test-node.yml +5 -6
- package/index.js +56 -11
- package/lib/keys.js +6 -1
- package/package.json +3 -4
- package/test/all.js +61 -16
- package/test/keys.js +4 -1
|
@@ -1,23 +1,22 @@
|
|
|
1
|
-
name:
|
|
2
|
-
|
|
1
|
+
name: Build Status
|
|
3
2
|
on:
|
|
4
3
|
push:
|
|
5
4
|
branches:
|
|
6
|
-
-
|
|
5
|
+
- master
|
|
7
6
|
pull_request:
|
|
8
7
|
branches:
|
|
9
|
-
-
|
|
8
|
+
- master
|
|
10
9
|
jobs:
|
|
11
10
|
build:
|
|
12
11
|
strategy:
|
|
13
12
|
matrix:
|
|
14
|
-
node-version: [
|
|
13
|
+
node-version: [lts/*]
|
|
15
14
|
os: [ubuntu-latest, macos-latest, windows-latest]
|
|
16
15
|
runs-on: ${{ matrix.os }}
|
|
17
16
|
steps:
|
|
18
17
|
- uses: actions/checkout@v2
|
|
19
18
|
- name: Use Node.js ${{ matrix.node-version }}
|
|
20
|
-
uses: actions/setup-node@
|
|
19
|
+
uses: actions/setup-node@v2
|
|
21
20
|
with:
|
|
22
21
|
node-version: ${{ matrix.node-version }}
|
|
23
22
|
- run: npm install
|
package/index.js
CHANGED
|
@@ -25,12 +25,43 @@ module.exports = class Corestore extends EventEmitter {
|
|
|
25
25
|
this._namespace = opts._namespace || DEFAULT_NAMESPACE
|
|
26
26
|
this._replicationStreams = opts._streams || []
|
|
27
27
|
this._streamSessions = opts._streamSessions || new Map()
|
|
28
|
+
this._sessions = new Set() // sessions for THIS namespace
|
|
29
|
+
|
|
30
|
+
this._findingPeersCount = 0
|
|
31
|
+
this._findingPeers = []
|
|
28
32
|
|
|
29
33
|
this._opening = opts._opening ? opts._opening.then(() => this._open()) : this._open()
|
|
30
34
|
this._opening.catch(safetyCatch)
|
|
31
35
|
this.ready = () => this._opening
|
|
32
36
|
}
|
|
33
37
|
|
|
38
|
+
findingPeers () {
|
|
39
|
+
let done = false
|
|
40
|
+
this._incFindingPeers()
|
|
41
|
+
|
|
42
|
+
return () => {
|
|
43
|
+
if (done) return
|
|
44
|
+
done = true
|
|
45
|
+
this._decFindingPeers()
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
_incFindingPeers () {
|
|
50
|
+
if (++this._findingPeersCount !== 1) return
|
|
51
|
+
|
|
52
|
+
for (const core of this._sessions) {
|
|
53
|
+
this._findingPeers.push(core.findingPeers())
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
_decFindingPeers () {
|
|
58
|
+
if (--this._findingPeersCount !== 0) return
|
|
59
|
+
|
|
60
|
+
while (this._findingPeers.length > 0) {
|
|
61
|
+
this._findingPeers.pop()()
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
|
|
34
65
|
async _open () {
|
|
35
66
|
if (this.keys) {
|
|
36
67
|
this.keys = await this.keys // opts.keys can be a Promise that resolves to a KeyManager
|
|
@@ -43,7 +74,7 @@ module.exports = class Corestore extends EventEmitter {
|
|
|
43
74
|
if (opts._discoveryKey) {
|
|
44
75
|
return {
|
|
45
76
|
keyPair: null,
|
|
46
|
-
|
|
77
|
+
auth: null,
|
|
47
78
|
discoveryKey: opts._discoveryKey
|
|
48
79
|
}
|
|
49
80
|
}
|
|
@@ -54,16 +85,17 @@ module.exports = class Corestore extends EventEmitter {
|
|
|
54
85
|
secretKey: opts.secretKey
|
|
55
86
|
},
|
|
56
87
|
sign: opts.sign,
|
|
88
|
+
auth: opts.auth,
|
|
57
89
|
discoveryKey: crypto.discoveryKey(opts.publicKey)
|
|
58
90
|
}
|
|
59
91
|
}
|
|
60
|
-
const { publicKey,
|
|
92
|
+
const { publicKey, auth } = await this.keys.createHypercoreKeyPair(opts.name, this._namespace)
|
|
61
93
|
return {
|
|
62
94
|
keyPair: {
|
|
63
95
|
publicKey,
|
|
64
96
|
secretKey: null
|
|
65
97
|
},
|
|
66
|
-
|
|
98
|
+
auth,
|
|
67
99
|
discoveryKey: crypto.discoveryKey(publicKey)
|
|
68
100
|
}
|
|
69
101
|
}
|
|
@@ -80,11 +112,11 @@ module.exports = class Corestore extends EventEmitter {
|
|
|
80
112
|
if (!name) return
|
|
81
113
|
|
|
82
114
|
const namespace = this._getPrereadyUserData(core, USERDATA_NAMESPACE_KEY)
|
|
83
|
-
const { publicKey,
|
|
115
|
+
const { publicKey, auth } = await this.keys.createHypercoreKeyPair(b4a.toString(name), namespace)
|
|
84
116
|
if (!b4a.equals(publicKey, core.key)) throw new Error('Stored core key does not match the provided name')
|
|
85
117
|
|
|
86
|
-
// TODO: Should Hypercore expose a helper for this, or should preready return keypair/
|
|
87
|
-
core.
|
|
118
|
+
// TODO: Should Hypercore expose a helper for this, or should preready return keypair/auth?
|
|
119
|
+
core.auth = auth
|
|
88
120
|
core.key = publicKey
|
|
89
121
|
core.writable = true
|
|
90
122
|
}
|
|
@@ -92,12 +124,12 @@ module.exports = class Corestore extends EventEmitter {
|
|
|
92
124
|
async _preload (opts) {
|
|
93
125
|
await this.ready()
|
|
94
126
|
|
|
95
|
-
const { discoveryKey, keyPair,
|
|
127
|
+
const { discoveryKey, keyPair, auth } = await this._generateKeys(opts)
|
|
96
128
|
const id = b4a.toString(discoveryKey, 'hex')
|
|
97
129
|
|
|
98
130
|
while (this.cores.has(id)) {
|
|
99
131
|
const existing = this.cores.get(id)
|
|
100
|
-
if (existing.opened && !existing.closing) return { from: existing, keyPair,
|
|
132
|
+
if (existing.opened && !existing.closing) return { from: existing, keyPair, auth }
|
|
101
133
|
if (!existing.opened) {
|
|
102
134
|
await existing.ready().catch(safetyCatch)
|
|
103
135
|
} else if (existing.closing) {
|
|
@@ -119,7 +151,7 @@ module.exports = class Corestore extends EventEmitter {
|
|
|
119
151
|
autoClose: true,
|
|
120
152
|
encryptionKey: opts.encryptionKey || null,
|
|
121
153
|
userData,
|
|
122
|
-
|
|
154
|
+
auth,
|
|
123
155
|
createIfMissing: !opts._discoveryKey,
|
|
124
156
|
keyPair: keyPair && keyPair.publicKey
|
|
125
157
|
? {
|
|
@@ -144,7 +176,7 @@ module.exports = class Corestore extends EventEmitter {
|
|
|
144
176
|
this.cores.delete(id)
|
|
145
177
|
})
|
|
146
178
|
|
|
147
|
-
return { from: core, keyPair,
|
|
179
|
+
return { from: core, keyPair, auth }
|
|
148
180
|
}
|
|
149
181
|
|
|
150
182
|
get (opts = {}) {
|
|
@@ -154,6 +186,19 @@ module.exports = class Corestore extends EventEmitter {
|
|
|
154
186
|
name: null,
|
|
155
187
|
preload: () => this._preload(opts)
|
|
156
188
|
})
|
|
189
|
+
|
|
190
|
+
this._sessions.add(core)
|
|
191
|
+
if (this._findingPeersCount > 0) {
|
|
192
|
+
this._findingPeers.push(core.findingPeers())
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
core.once('close', () => {
|
|
196
|
+
// technically better to also clear _findingPeers if we added it,
|
|
197
|
+
// but the lifecycle for those are pretty short so prob not worth the complexity
|
|
198
|
+
// as _decFindingPeers clear them all.
|
|
199
|
+
this._sessions.delete(core)
|
|
200
|
+
})
|
|
201
|
+
|
|
157
202
|
return core
|
|
158
203
|
}
|
|
159
204
|
|
|
@@ -201,7 +246,7 @@ module.exports = class Corestore extends EventEmitter {
|
|
|
201
246
|
|
|
202
247
|
async _close () {
|
|
203
248
|
await this._opening
|
|
204
|
-
if (!this._namespace
|
|
249
|
+
if (!b4a.equals(this._namespace, DEFAULT_NAMESPACE)) return // namespaces should not release resources on close
|
|
205
250
|
const closePromises = []
|
|
206
251
|
for (const core of this.cores.values()) {
|
|
207
252
|
closePromises.push(core.close())
|
package/lib/keys.js
CHANGED
|
@@ -28,7 +28,12 @@ module.exports = class KeyManager {
|
|
|
28
28
|
const keyPair = {
|
|
29
29
|
publicKey: b4a.allocUnsafe(sodium.crypto_sign_PUBLICKEYBYTES),
|
|
30
30
|
_secretKey: b4a.alloc(sodium.crypto_sign_SECRETKEYBYTES),
|
|
31
|
-
|
|
31
|
+
auth: {
|
|
32
|
+
sign: (msg) => this._sign(keyPair, msg),
|
|
33
|
+
verify: (signable, signature) => {
|
|
34
|
+
return sodium.crypto_sign_detached(signature, signable, keyPair.publicKey)
|
|
35
|
+
}
|
|
36
|
+
}
|
|
32
37
|
}
|
|
33
38
|
|
|
34
39
|
sodium.crypto_sign_seed_keypair(keyPair.publicKey, keyPair._secretKey, this.createSecret(name, token))
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "corestore",
|
|
3
|
-
"version": "6.0.1-alpha.
|
|
3
|
+
"version": "6.0.1-alpha.14",
|
|
4
4
|
"description": "A Hypercore factory that simplifies managing collections of cores.",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"scripts": {
|
|
@@ -22,9 +22,8 @@
|
|
|
22
22
|
"devDependencies": {
|
|
23
23
|
"brittle": "^1.6.0",
|
|
24
24
|
"random-access-file": "^2.2.0",
|
|
25
|
-
"random-access-memory": "^
|
|
26
|
-
"standardx": "^7.0.0"
|
|
27
|
-
"tmp-promise": "^3.0.2"
|
|
25
|
+
"random-access-memory": "^4.0.0",
|
|
26
|
+
"standardx": "^7.0.0"
|
|
28
27
|
},
|
|
29
28
|
"dependencies": {
|
|
30
29
|
"b4a": "^1.3.1",
|
package/test/all.js
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
const { test, configure } = require('brittle')
|
|
2
2
|
const crypto = require('hypercore-crypto')
|
|
3
3
|
const ram = require('random-access-memory')
|
|
4
|
-
const
|
|
4
|
+
const os = require('os')
|
|
5
|
+
const path = require('path')
|
|
5
6
|
|
|
6
7
|
const Corestore = require('..')
|
|
7
8
|
|
|
@@ -99,8 +100,8 @@ test('replicating cores created after replication begins', async function (t) {
|
|
|
99
100
|
})
|
|
100
101
|
|
|
101
102
|
test('replicating cores using discovery key hook', async function (t) {
|
|
102
|
-
const dir =
|
|
103
|
-
let store1 = new Corestore(dir
|
|
103
|
+
const dir = tmpdir()
|
|
104
|
+
let store1 = new Corestore(dir)
|
|
104
105
|
const store2 = new Corestore(ram)
|
|
105
106
|
|
|
106
107
|
const core = store1.get({ name: 'main' })
|
|
@@ -108,15 +109,13 @@ test('replicating cores using discovery key hook', async function (t) {
|
|
|
108
109
|
const key = core.key
|
|
109
110
|
|
|
110
111
|
await store1.close()
|
|
111
|
-
store1 = new Corestore(dir
|
|
112
|
+
store1 = new Corestore(dir)
|
|
112
113
|
|
|
113
114
|
const s = store1.replicate(true, { live: true })
|
|
114
115
|
s.pipe(store2.replicate(false, { live: true })).pipe(s)
|
|
115
116
|
|
|
116
117
|
const core2 = store2.get(key)
|
|
117
118
|
t.alike(await core2.get(0), Buffer.from('hello'))
|
|
118
|
-
|
|
119
|
-
await dir.cleanup()
|
|
120
119
|
})
|
|
121
120
|
|
|
122
121
|
test('nested namespaces', async function (t) {
|
|
@@ -144,9 +143,9 @@ test('core uncached when all sessions close', async function (t) {
|
|
|
144
143
|
})
|
|
145
144
|
|
|
146
145
|
test('writable core loaded from name userData', async function (t) {
|
|
147
|
-
const dir =
|
|
146
|
+
const dir = tmpdir()
|
|
148
147
|
|
|
149
|
-
let store = new Corestore(dir
|
|
148
|
+
let store = new Corestore(dir)
|
|
150
149
|
let core = store.get({ name: 'main' })
|
|
151
150
|
await core.ready()
|
|
152
151
|
const key = core.key
|
|
@@ -156,7 +155,7 @@ test('writable core loaded from name userData', async function (t) {
|
|
|
156
155
|
t.is(core.length, 1)
|
|
157
156
|
|
|
158
157
|
await store.close()
|
|
159
|
-
store = new Corestore(dir
|
|
158
|
+
store = new Corestore(dir)
|
|
160
159
|
core = store.get(key)
|
|
161
160
|
await core.ready()
|
|
162
161
|
|
|
@@ -165,25 +164,21 @@ test('writable core loaded from name userData', async function (t) {
|
|
|
165
164
|
t.is(core.length, 2)
|
|
166
165
|
t.alike(await core.get(0), Buffer.from('hello'))
|
|
167
166
|
t.alike(await core.get(1), Buffer.from('world'))
|
|
168
|
-
|
|
169
|
-
await dir.cleanup()
|
|
170
167
|
})
|
|
171
168
|
|
|
172
169
|
test('storage locking', async function (t) {
|
|
173
|
-
const dir =
|
|
170
|
+
const dir = tmpdir()
|
|
174
171
|
|
|
175
|
-
const store1 = new Corestore(dir
|
|
172
|
+
const store1 = new Corestore(dir)
|
|
176
173
|
await store1.ready()
|
|
177
174
|
|
|
178
|
-
const store2 = new Corestore(dir
|
|
175
|
+
const store2 = new Corestore(dir)
|
|
179
176
|
try {
|
|
180
177
|
await store2.ready()
|
|
181
178
|
t.fail('dir should have been locked')
|
|
182
179
|
} catch {
|
|
183
180
|
t.pass('dir was locked')
|
|
184
181
|
}
|
|
185
|
-
|
|
186
|
-
await dir.cleanup()
|
|
187
182
|
})
|
|
188
183
|
|
|
189
184
|
test('closing a namespace does not close cores', async function (t) {
|
|
@@ -205,3 +200,53 @@ test('closing a namespace does not close cores', async function (t) {
|
|
|
205
200
|
t.ok(core1.closed)
|
|
206
201
|
t.ok(core2.closed)
|
|
207
202
|
})
|
|
203
|
+
|
|
204
|
+
test('findingPeers', async function (t) {
|
|
205
|
+
t.plan(6)
|
|
206
|
+
|
|
207
|
+
const store = new Corestore(ram)
|
|
208
|
+
|
|
209
|
+
const ns1 = store.namespace('ns1')
|
|
210
|
+
const ns2 = store.namespace('ns2')
|
|
211
|
+
|
|
212
|
+
const a = ns1.get(Buffer.alloc(32).fill('a'))
|
|
213
|
+
const b = ns2.get(Buffer.alloc(32).fill('b'))
|
|
214
|
+
|
|
215
|
+
const done = ns1.findingPeers()
|
|
216
|
+
|
|
217
|
+
let aUpdated = false
|
|
218
|
+
let bUpdated = false
|
|
219
|
+
let cUpdated = false
|
|
220
|
+
|
|
221
|
+
const c = ns1.get(Buffer.alloc(32).fill('c'))
|
|
222
|
+
|
|
223
|
+
a.update().then(function (bool) {
|
|
224
|
+
aUpdated = true
|
|
225
|
+
})
|
|
226
|
+
|
|
227
|
+
b.update().then(function (bool) {
|
|
228
|
+
bUpdated = true
|
|
229
|
+
})
|
|
230
|
+
|
|
231
|
+
c.update().then(function (bool) {
|
|
232
|
+
cUpdated = true
|
|
233
|
+
})
|
|
234
|
+
|
|
235
|
+
await new Promise(resolve => setImmediate(resolve))
|
|
236
|
+
|
|
237
|
+
t.is(aUpdated, false)
|
|
238
|
+
t.is(bUpdated, true)
|
|
239
|
+
t.is(cUpdated, false)
|
|
240
|
+
|
|
241
|
+
done()
|
|
242
|
+
|
|
243
|
+
await new Promise(resolve => setImmediate(resolve))
|
|
244
|
+
|
|
245
|
+
t.is(aUpdated, true)
|
|
246
|
+
t.is(bUpdated, true)
|
|
247
|
+
t.is(cUpdated, true)
|
|
248
|
+
})
|
|
249
|
+
|
|
250
|
+
function tmpdir () {
|
|
251
|
+
return path.join(os.tmpdir(), 'corestore-' + Math.random().toString(16).slice(2))
|
|
252
|
+
}
|
package/test/keys.js
CHANGED
|
@@ -41,7 +41,7 @@ test('short user-provided token will throw', async t => {
|
|
|
41
41
|
})
|
|
42
42
|
|
|
43
43
|
test('persistent storage regenerates keys correctly', async t => {
|
|
44
|
-
const testPath = p.
|
|
44
|
+
const testPath = p.join(__dirname, 'test-data')
|
|
45
45
|
|
|
46
46
|
const keys1 = await KeyManager.fromStorage((name) => raf(testPath, { directory: testPath }))
|
|
47
47
|
const kp1 = await keys1.createHypercoreKeyPair('core1')
|
|
@@ -51,6 +51,9 @@ test('persistent storage regenerates keys correctly', async t => {
|
|
|
51
51
|
|
|
52
52
|
t.alike(kp1.publicKey, kp2.publicKey)
|
|
53
53
|
|
|
54
|
+
await keys1.close()
|
|
55
|
+
await keys2.close()
|
|
56
|
+
|
|
54
57
|
await fs.promises.rm(testPath, { recursive: true })
|
|
55
58
|
})
|
|
56
59
|
|