corestore 6.0.0-alpha.4 → 6.0.1-alpha.10
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/index.js +79 -49
- package/lib/keys.js +11 -10
- package/package.json +6 -4
- package/test/all.js +91 -40
- package/test/keys.js +7 -16
package/index.js
CHANGED
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
const { EventEmitter } = require('events')
|
|
2
|
+
const safetyCatch = require('safety-catch')
|
|
2
3
|
const crypto = require('hypercore-crypto')
|
|
3
4
|
const sodium = require('sodium-universal')
|
|
4
5
|
const Hypercore = require('hypercore')
|
|
6
|
+
const b4a = require('b4a')
|
|
5
7
|
|
|
6
8
|
const KeyManager = require('./lib/keys')
|
|
7
9
|
|
|
@@ -22,9 +24,10 @@ module.exports = class Corestore extends EventEmitter {
|
|
|
22
24
|
|
|
23
25
|
this._namespace = opts._namespace || DEFAULT_NAMESPACE
|
|
24
26
|
this._replicationStreams = opts._streams || []
|
|
27
|
+
this._streamSessions = opts._streamSessions || new Map()
|
|
25
28
|
|
|
26
29
|
this._opening = opts._opening ? opts._opening.then(() => this._open()) : this._open()
|
|
27
|
-
this._opening.catch(
|
|
30
|
+
this._opening.catch(safetyCatch)
|
|
28
31
|
this.ready = () => this._opening
|
|
29
32
|
}
|
|
30
33
|
|
|
@@ -37,11 +40,11 @@ module.exports = class Corestore extends EventEmitter {
|
|
|
37
40
|
}
|
|
38
41
|
|
|
39
42
|
async _generateKeys (opts) {
|
|
40
|
-
if (opts.
|
|
43
|
+
if (opts._discoveryKey) {
|
|
41
44
|
return {
|
|
42
45
|
keyPair: null,
|
|
43
46
|
sign: null,
|
|
44
|
-
discoveryKey: opts.
|
|
47
|
+
discoveryKey: opts._discoveryKey
|
|
45
48
|
}
|
|
46
49
|
}
|
|
47
50
|
if (!opts.name) {
|
|
@@ -65,15 +68,22 @@ module.exports = class Corestore extends EventEmitter {
|
|
|
65
68
|
}
|
|
66
69
|
}
|
|
67
70
|
|
|
68
|
-
|
|
69
|
-
const
|
|
71
|
+
_getPrereadyUserData (core, key) {
|
|
72
|
+
for (const { key: savedKey, value } of core.core.header.userData) {
|
|
73
|
+
if (key === savedKey) return value
|
|
74
|
+
}
|
|
75
|
+
return null
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
async _preready (core) {
|
|
79
|
+
const name = this._getPrereadyUserData(core, USERDATA_NAME_KEY)
|
|
70
80
|
if (!name) return
|
|
71
81
|
|
|
72
|
-
const namespace =
|
|
73
|
-
const { publicKey, sign } = await this.keys.createHypercoreKeyPair(
|
|
82
|
+
const namespace = this._getPrereadyUserData(core, USERDATA_NAMESPACE_KEY)
|
|
83
|
+
const { publicKey, sign } = await this.keys.createHypercoreKeyPair(b4a.toString(name), namespace)
|
|
74
84
|
if (!publicKey.equals(core.key)) throw new Error('Stored core key does not match the provided name')
|
|
75
85
|
|
|
76
|
-
// TODO: Should Hypercore expose a helper for this, or should
|
|
86
|
+
// TODO: Should Hypercore expose a helper for this, or should preready return keypair/sign?
|
|
77
87
|
core.sign = sign
|
|
78
88
|
core.key = publicKey
|
|
79
89
|
core.writable = true
|
|
@@ -83,19 +93,21 @@ module.exports = class Corestore extends EventEmitter {
|
|
|
83
93
|
await this.ready()
|
|
84
94
|
|
|
85
95
|
const { discoveryKey, keyPair, sign } = await this._generateKeys(opts)
|
|
86
|
-
const id =
|
|
96
|
+
const id = b4a.toString(discoveryKey, 'hex')
|
|
87
97
|
|
|
88
98
|
while (this.cores.has(id)) {
|
|
89
99
|
const existing = this.cores.get(id)
|
|
90
|
-
if (existing) {
|
|
91
|
-
|
|
100
|
+
if (existing.opened && !existing.closing) return { from: existing, keyPair, sign }
|
|
101
|
+
if (!existing.opened) {
|
|
102
|
+
await existing.ready().catch(safetyCatch)
|
|
103
|
+
} else if (existing.closing) {
|
|
92
104
|
await existing.close()
|
|
93
105
|
}
|
|
94
106
|
}
|
|
95
107
|
|
|
96
108
|
const userData = {}
|
|
97
109
|
if (opts.name) {
|
|
98
|
-
userData[USERDATA_NAME_KEY] =
|
|
110
|
+
userData[USERDATA_NAME_KEY] = b4a.from(opts.name)
|
|
99
111
|
userData[USERDATA_NAMESPACE_KEY] = this._namespace
|
|
100
112
|
}
|
|
101
113
|
|
|
@@ -103,22 +115,31 @@ module.exports = class Corestore extends EventEmitter {
|
|
|
103
115
|
|
|
104
116
|
const storageRoot = [CORES_DIR, id.slice(0, 2), id.slice(2, 4), id].join('/')
|
|
105
117
|
const core = new Hypercore(p => this.storage(storageRoot + '/' + p), {
|
|
118
|
+
_preready: this._preready.bind(this),
|
|
106
119
|
autoClose: true,
|
|
107
120
|
encryptionKey: opts.encryptionKey || null,
|
|
108
|
-
keyPair: {
|
|
109
|
-
publicKey: keyPair.publicKey,
|
|
110
|
-
secretKey: null
|
|
111
|
-
},
|
|
112
121
|
userData,
|
|
113
122
|
sign: null,
|
|
114
|
-
|
|
115
|
-
|
|
123
|
+
createIfMissing: !opts._discoveryKey,
|
|
124
|
+
keyPair: keyPair && keyPair.publicKey
|
|
125
|
+
? {
|
|
126
|
+
publicKey: keyPair.publicKey,
|
|
127
|
+
secretKey: null
|
|
128
|
+
}
|
|
129
|
+
: null
|
|
116
130
|
})
|
|
117
131
|
|
|
118
132
|
this.cores.set(id, core)
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
133
|
+
core.ready().then(() => {
|
|
134
|
+
for (const { stream } of this._replicationStreams) {
|
|
135
|
+
const sessions = this._streamSessions.get(stream)
|
|
136
|
+
const session = core.session()
|
|
137
|
+
sessions.push(session)
|
|
138
|
+
core.replicate(stream)
|
|
139
|
+
}
|
|
140
|
+
}, () => {
|
|
141
|
+
this.cores.delete(id)
|
|
142
|
+
})
|
|
122
143
|
core.once('close', () => {
|
|
123
144
|
this.cores.delete(id)
|
|
124
145
|
})
|
|
@@ -136,47 +157,59 @@ module.exports = class Corestore extends EventEmitter {
|
|
|
136
157
|
return core
|
|
137
158
|
}
|
|
138
159
|
|
|
139
|
-
replicate (opts
|
|
140
|
-
const
|
|
160
|
+
replicate (isInitiator, opts) {
|
|
161
|
+
const isExternal = isStream(isInitiator) || !!(opts && opts.stream)
|
|
162
|
+
const stream = Hypercore.createProtocolStream(isInitiator, {
|
|
163
|
+
...opts,
|
|
164
|
+
ondiscoverykey: discoveryKey => {
|
|
165
|
+
const core = this.get({ _discoveryKey: discoveryKey })
|
|
166
|
+
return core.ready().catch(safetyCatch)
|
|
167
|
+
}
|
|
168
|
+
})
|
|
169
|
+
|
|
170
|
+
const sessions = []
|
|
141
171
|
for (const core of this.cores.values()) {
|
|
172
|
+
if (!core.opened) continue // If the core is not opened, it will be replicated in preload.
|
|
173
|
+
const session = core.session()
|
|
174
|
+
sessions.push(session)
|
|
142
175
|
core.replicate(stream)
|
|
143
176
|
}
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
stream.close(discoveryKey)
|
|
150
|
-
})
|
|
151
|
-
})
|
|
152
|
-
this._replicationStreams.push(stream)
|
|
177
|
+
|
|
178
|
+
const streamRecord = { stream, isExternal }
|
|
179
|
+
this._replicationStreams.push(streamRecord)
|
|
180
|
+
this._streamSessions.set(stream, sessions)
|
|
181
|
+
|
|
153
182
|
stream.once('close', () => {
|
|
154
|
-
this._replicationStreams.splice(this._replicationStreams.indexOf(
|
|
183
|
+
this._replicationStreams.splice(this._replicationStreams.indexOf(streamRecord), 1)
|
|
184
|
+
this._streamSessions.delete(stream)
|
|
185
|
+
Promise.all(sessions.map(s => s.close())).catch(safetyCatch)
|
|
155
186
|
})
|
|
156
187
|
return stream
|
|
157
188
|
}
|
|
158
189
|
|
|
159
190
|
namespace (name) {
|
|
160
|
-
if (!
|
|
191
|
+
if (!b4a.isBuffer(name)) name = b4a.from(name)
|
|
161
192
|
return new Corestore(this.storage, {
|
|
162
193
|
_namespace: generateNamespace(this._namespace, name),
|
|
163
194
|
_opening: this._opening,
|
|
164
195
|
_cores: this.cores,
|
|
165
196
|
_streams: this._replicationStreams,
|
|
197
|
+
_streamSessions: this._streamSessions,
|
|
166
198
|
keys: this._opening.then(() => this.keys)
|
|
167
199
|
})
|
|
168
200
|
}
|
|
169
201
|
|
|
170
202
|
async _close () {
|
|
171
|
-
if (this._closing) return this._closing
|
|
172
203
|
await this._opening
|
|
204
|
+
if (!this._namespace.equals(DEFAULT_NAMESPACE)) return // namespaces should not release resources on close
|
|
173
205
|
const closePromises = []
|
|
174
206
|
for (const core of this.cores.values()) {
|
|
175
207
|
closePromises.push(core.close())
|
|
176
208
|
}
|
|
177
209
|
await Promise.allSettled(closePromises)
|
|
178
|
-
for (const stream of this._replicationStreams) {
|
|
179
|
-
|
|
210
|
+
for (const { stream, isExternal } of this._replicationStreams) {
|
|
211
|
+
// Only close streams that were created by the Corestore
|
|
212
|
+
if (!isExternal) stream.destroy()
|
|
180
213
|
}
|
|
181
214
|
await this.keys.close()
|
|
182
215
|
}
|
|
@@ -184,7 +217,7 @@ module.exports = class Corestore extends EventEmitter {
|
|
|
184
217
|
close () {
|
|
185
218
|
if (this._closing) return this._closing
|
|
186
219
|
this._closing = this._close()
|
|
187
|
-
this._closing.catch(
|
|
220
|
+
this._closing.catch(safetyCatch)
|
|
188
221
|
return this._closing
|
|
189
222
|
}
|
|
190
223
|
|
|
@@ -194,7 +227,7 @@ module.exports = class Corestore extends EventEmitter {
|
|
|
194
227
|
}
|
|
195
228
|
|
|
196
229
|
function validateGetOptions (opts) {
|
|
197
|
-
if (
|
|
230
|
+
if (b4a.isBuffer(opts)) return { key: opts, publicKey: opts }
|
|
198
231
|
if (opts.key) {
|
|
199
232
|
opts.publicKey = opts.key
|
|
200
233
|
}
|
|
@@ -204,23 +237,20 @@ function validateGetOptions (opts) {
|
|
|
204
237
|
}
|
|
205
238
|
if (opts.name && typeof opts.name !== 'string') throw new Error('name option must be a String')
|
|
206
239
|
if (opts.name && opts.secretKey) throw new Error('Cannot provide both a name and a secret key')
|
|
207
|
-
if (opts.publicKey && !
|
|
208
|
-
if (opts.secretKey && !
|
|
209
|
-
if (opts.
|
|
210
|
-
if (!opts.name && !opts.publicKey) throw new Error('Must provide either a name or a publicKey')
|
|
240
|
+
if (opts.publicKey && !b4a.isBuffer(opts.publicKey)) throw new Error('publicKey option must be a Buffer or Uint8Array')
|
|
241
|
+
if (opts.secretKey && !b4a.isBuffer(opts.secretKey)) throw new Error('secretKey option must be a Buffer or Uint8Array')
|
|
242
|
+
if (!opts._discoveryKey && (!opts.name && !opts.publicKey)) throw new Error('Must provide either a name or a publicKey')
|
|
211
243
|
return opts
|
|
212
244
|
}
|
|
213
245
|
|
|
214
246
|
function generateNamespace (first, second) {
|
|
215
|
-
if (!
|
|
216
|
-
if (second && !
|
|
217
|
-
const out =
|
|
218
|
-
sodium.crypto_generichash(out, second ?
|
|
247
|
+
if (!b4a.isBuffer(first)) first = b4a.from(first)
|
|
248
|
+
if (second && !b4a.isBuffer(second)) second = b4a.from(second)
|
|
249
|
+
const out = b4a.allocUnsafe(32)
|
|
250
|
+
sodium.crypto_generichash(out, second ? b4a.concat([first, second]) : first)
|
|
219
251
|
return out
|
|
220
252
|
}
|
|
221
253
|
|
|
222
254
|
function isStream (s) {
|
|
223
255
|
return typeof s === 'object' && s && typeof s.pipe === 'function'
|
|
224
256
|
}
|
|
225
|
-
|
|
226
|
-
function noop () {}
|
package/lib/keys.js
CHANGED
|
@@ -2,9 +2,10 @@
|
|
|
2
2
|
|
|
3
3
|
const sodium = require('sodium-universal')
|
|
4
4
|
const blake2b = require('blake2b-universal')
|
|
5
|
+
const b4a = require('b4a')
|
|
5
6
|
|
|
6
|
-
const DEFAULT_TOKEN =
|
|
7
|
-
const NAMESPACE =
|
|
7
|
+
const DEFAULT_TOKEN = b4a.alloc(0)
|
|
8
|
+
const NAMESPACE = b4a.from('@hyperspace/key-manager')
|
|
8
9
|
|
|
9
10
|
module.exports = class KeyManager {
|
|
10
11
|
constructor (storage, profile, opts = {}) {
|
|
@@ -14,7 +15,7 @@ module.exports = class KeyManager {
|
|
|
14
15
|
|
|
15
16
|
_sign (keyPair, message) {
|
|
16
17
|
if (!keyPair._secretKey) throw new Error('Invalid key pair')
|
|
17
|
-
const signature =
|
|
18
|
+
const signature = b4a.allocUnsafe(sodium.crypto_sign_BYTES)
|
|
18
19
|
sodium.crypto_sign_detached(signature, message, keyPair._secretKey)
|
|
19
20
|
return signature
|
|
20
21
|
}
|
|
@@ -25,8 +26,8 @@ module.exports = class KeyManager {
|
|
|
25
26
|
|
|
26
27
|
createHypercoreKeyPair (name, token) {
|
|
27
28
|
const keyPair = {
|
|
28
|
-
publicKey:
|
|
29
|
-
_secretKey:
|
|
29
|
+
publicKey: b4a.allocUnsafe(sodium.crypto_sign_PUBLICKEYBYTES),
|
|
30
|
+
_secretKey: b4a.alloc(sodium.crypto_sign_SECRETKEYBYTES),
|
|
30
31
|
sign: (msg) => this._sign(keyPair, msg)
|
|
31
32
|
}
|
|
32
33
|
|
|
@@ -37,8 +38,8 @@ module.exports = class KeyManager {
|
|
|
37
38
|
|
|
38
39
|
createNetworkIdentity (name, token) {
|
|
39
40
|
const keyPair = {
|
|
40
|
-
publicKey:
|
|
41
|
-
secretKey:
|
|
41
|
+
publicKey: b4a.alloc(32),
|
|
42
|
+
secretKey: b4a.alloc(64)
|
|
42
43
|
}
|
|
43
44
|
|
|
44
45
|
sodium.crypto_sign_seed_keypair(keyPair.publicKey, keyPair.secretKey, this.createSecret(name, token))
|
|
@@ -86,19 +87,19 @@ module.exports = class KeyManager {
|
|
|
86
87
|
function deriveSeed (profile, token, name, output) {
|
|
87
88
|
if (token && token.length < 32) throw new Error('Token must be a Buffer with length >= 32')
|
|
88
89
|
if (!name || typeof name !== 'string') throw new Error('name must be a String')
|
|
89
|
-
if (!output) output =
|
|
90
|
+
if (!output) output = b4a.alloc(32)
|
|
90
91
|
|
|
91
92
|
blake2b.batch(output, [
|
|
92
93
|
NAMESPACE,
|
|
93
94
|
token || DEFAULT_TOKEN,
|
|
94
|
-
|
|
95
|
+
b4a.from(b4a.byteLength(name, 'ascii') + '\n' + name, 'ascii')
|
|
95
96
|
], profile)
|
|
96
97
|
|
|
97
98
|
return output
|
|
98
99
|
}
|
|
99
100
|
|
|
100
101
|
function randomBytes (n) {
|
|
101
|
-
const buf =
|
|
102
|
+
const buf = b4a.allocUnsafe(n)
|
|
102
103
|
sodium.randombytes_buf(buf)
|
|
103
104
|
return buf
|
|
104
105
|
}
|
package/package.json
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "corestore",
|
|
3
|
-
"version": "6.0.
|
|
3
|
+
"version": "6.0.1-alpha.10",
|
|
4
4
|
"description": "A Hypercore factory that simplifies managing collections of cores.",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"scripts": {
|
|
7
|
-
"test": "standard &&
|
|
7
|
+
"test": "standard && brittle test/*.js"
|
|
8
8
|
},
|
|
9
9
|
"repository": {
|
|
10
10
|
"type": "git",
|
|
@@ -20,17 +20,19 @@
|
|
|
20
20
|
},
|
|
21
21
|
"homepage": "https://github.com/hypercore-protocol/corestore#readme",
|
|
22
22
|
"devDependencies": {
|
|
23
|
+
"brittle": "^1.6.0",
|
|
23
24
|
"random-access-file": "^2.2.0",
|
|
24
25
|
"random-access-memory": "^3.1.2",
|
|
25
26
|
"standardx": "^7.0.0",
|
|
26
|
-
"tape": "^5.3.1",
|
|
27
27
|
"tmp-promise": "^3.0.2"
|
|
28
28
|
},
|
|
29
29
|
"dependencies": {
|
|
30
|
+
"b4a": "^1.3.1",
|
|
31
|
+
"hypercore": "next",
|
|
30
32
|
"blake2b-universal": "^1.0.1",
|
|
31
33
|
"derive-key": "^1.0.1",
|
|
32
|
-
"hypercore": "next",
|
|
33
34
|
"hypercore-crypto": "^2.3.0",
|
|
35
|
+
"safety-catch": "^1.0.1",
|
|
34
36
|
"sodium-universal": "^3.0.4"
|
|
35
37
|
}
|
|
36
38
|
}
|
package/test/all.js
CHANGED
|
@@ -1,10 +1,12 @@
|
|
|
1
|
-
const test = require('
|
|
1
|
+
const { test, configure } = require('brittle')
|
|
2
2
|
const crypto = require('hypercore-crypto')
|
|
3
3
|
const ram = require('random-access-memory')
|
|
4
4
|
const tmp = require('tmp-promise')
|
|
5
5
|
|
|
6
6
|
const Corestore = require('..')
|
|
7
7
|
|
|
8
|
+
configure({ serial: true })
|
|
9
|
+
|
|
8
10
|
test('basic get with caching', async function (t) {
|
|
9
11
|
const store = new Corestore(ram)
|
|
10
12
|
const core1a = store.get({ name: 'core-1' })
|
|
@@ -13,15 +15,13 @@ test('basic get with caching', async function (t) {
|
|
|
13
15
|
|
|
14
16
|
await Promise.all([core1a.ready(), core1b.ready(), core2.ready()])
|
|
15
17
|
|
|
16
|
-
t.
|
|
17
|
-
t.
|
|
18
|
-
|
|
19
|
-
t.true(core1a.writable)
|
|
20
|
-
t.true(core1b.writable)
|
|
18
|
+
t.alike(core1a.key, core1b.key)
|
|
19
|
+
t.unlike(core1a.key, core2.key)
|
|
21
20
|
|
|
22
|
-
t.
|
|
21
|
+
t.ok(core1a.writable)
|
|
22
|
+
t.ok(core1b.writable)
|
|
23
23
|
|
|
24
|
-
t.
|
|
24
|
+
t.is(store.cores.size, 2)
|
|
25
25
|
})
|
|
26
26
|
|
|
27
27
|
test('basic get with custom keypair', async function (t) {
|
|
@@ -33,12 +33,10 @@ test('basic get with custom keypair', async function (t) {
|
|
|
33
33
|
const core2 = store.get(kp2)
|
|
34
34
|
await Promise.all([core1.ready(), core2.ready()])
|
|
35
35
|
|
|
36
|
-
t.
|
|
37
|
-
t.
|
|
38
|
-
t.
|
|
39
|
-
t.
|
|
40
|
-
|
|
41
|
-
t.end()
|
|
36
|
+
t.alike(core1.key, kp1.publicKey)
|
|
37
|
+
t.alike(core2.key, kp2.publicKey)
|
|
38
|
+
t.ok(core1.writable)
|
|
39
|
+
t.ok(core2.writable)
|
|
42
40
|
})
|
|
43
41
|
|
|
44
42
|
test('basic namespaces', async function (t) {
|
|
@@ -52,12 +50,12 @@ test('basic namespaces', async function (t) {
|
|
|
52
50
|
const core3 = ns3.get({ name: 'main' })
|
|
53
51
|
await Promise.all([core1.ready(), core2.ready(), core3.ready()])
|
|
54
52
|
|
|
55
|
-
t.
|
|
56
|
-
t.
|
|
57
|
-
t.
|
|
58
|
-
t.
|
|
59
|
-
t.
|
|
60
|
-
t.
|
|
53
|
+
t.absent(core1.key.equals(core2.key))
|
|
54
|
+
t.ok(core1.key.equals(core3.key))
|
|
55
|
+
t.ok(core1.writable)
|
|
56
|
+
t.ok(core2.writable)
|
|
57
|
+
t.ok(core3.writable)
|
|
58
|
+
t.is(store.cores.size, 2)
|
|
61
59
|
|
|
62
60
|
t.end()
|
|
63
61
|
})
|
|
@@ -77,10 +75,48 @@ test('basic replication', async function (t) {
|
|
|
77
75
|
const s = store1.replicate(true)
|
|
78
76
|
s.pipe(store2.replicate(false)).pipe(s)
|
|
79
77
|
|
|
80
|
-
t.
|
|
81
|
-
t.
|
|
78
|
+
t.alike(await core3.get(0), Buffer.from('hello'))
|
|
79
|
+
t.alike(await core4.get(0), Buffer.from('world'))
|
|
80
|
+
})
|
|
81
|
+
|
|
82
|
+
test('replicating cores created after replication begins', async function (t) {
|
|
83
|
+
const store1 = new Corestore(ram)
|
|
84
|
+
const store2 = new Corestore(ram)
|
|
85
|
+
|
|
86
|
+
const s = store1.replicate(true, { live: true })
|
|
87
|
+
s.pipe(store2.replicate(false, { live: true })).pipe(s)
|
|
82
88
|
|
|
83
|
-
|
|
89
|
+
const core1 = store1.get({ name: 'core-1' })
|
|
90
|
+
const core2 = store1.get({ name: 'core-2' })
|
|
91
|
+
await core1.append('hello')
|
|
92
|
+
await core2.append('world')
|
|
93
|
+
|
|
94
|
+
const core3 = store2.get({ key: core1.key })
|
|
95
|
+
const core4 = store2.get({ key: core2.key })
|
|
96
|
+
|
|
97
|
+
t.alike(await core3.get(0), Buffer.from('hello'))
|
|
98
|
+
t.alike(await core4.get(0), Buffer.from('world'))
|
|
99
|
+
})
|
|
100
|
+
|
|
101
|
+
test('replicating cores using discovery key hook', async function (t) {
|
|
102
|
+
const dir = await tmp.dir({ unsafeCleanup: true })
|
|
103
|
+
let store1 = new Corestore(dir.path)
|
|
104
|
+
const store2 = new Corestore(ram)
|
|
105
|
+
|
|
106
|
+
const core = store1.get({ name: 'main' })
|
|
107
|
+
await core.append('hello')
|
|
108
|
+
const key = core.key
|
|
109
|
+
|
|
110
|
+
await store1.close()
|
|
111
|
+
store1 = new Corestore(dir.path)
|
|
112
|
+
|
|
113
|
+
const s = store1.replicate(true, { live: true })
|
|
114
|
+
s.pipe(store2.replicate(false, { live: true })).pipe(s)
|
|
115
|
+
|
|
116
|
+
const core2 = store2.get(key)
|
|
117
|
+
t.alike(await core2.get(0), Buffer.from('hello'))
|
|
118
|
+
|
|
119
|
+
await dir.cleanup()
|
|
84
120
|
})
|
|
85
121
|
|
|
86
122
|
test('nested namespaces', async function (t) {
|
|
@@ -92,22 +128,19 @@ test('nested namespaces', async function (t) {
|
|
|
92
128
|
const core2 = ns1b.get({ name: 'main' })
|
|
93
129
|
await Promise.all([core1.ready(), core2.ready()])
|
|
94
130
|
|
|
95
|
-
t.
|
|
96
|
-
t.
|
|
97
|
-
t.
|
|
98
|
-
t.
|
|
99
|
-
|
|
100
|
-
t.end()
|
|
131
|
+
t.not(core1.key.equals(core2.key))
|
|
132
|
+
t.ok(core1.writable)
|
|
133
|
+
t.ok(core2.writable)
|
|
134
|
+
t.is(store.cores.size, 2)
|
|
101
135
|
})
|
|
102
136
|
|
|
103
137
|
test('core uncached when all sessions close', async function (t) {
|
|
104
138
|
const store = new Corestore(ram)
|
|
105
139
|
const core1 = store.get({ name: 'main' })
|
|
106
140
|
await core1.ready()
|
|
107
|
-
t.
|
|
141
|
+
t.is(store.cores.size, 1)
|
|
108
142
|
await core1.close()
|
|
109
|
-
t.
|
|
110
|
-
t.end()
|
|
143
|
+
t.is(store.cores.size, 0)
|
|
111
144
|
})
|
|
112
145
|
|
|
113
146
|
test('writable core loaded from name userData', async function (t) {
|
|
@@ -118,23 +151,22 @@ test('writable core loaded from name userData', async function (t) {
|
|
|
118
151
|
await core.ready()
|
|
119
152
|
const key = core.key
|
|
120
153
|
|
|
121
|
-
t.
|
|
154
|
+
t.ok(core.writable)
|
|
122
155
|
await core.append('hello')
|
|
123
|
-
t.
|
|
156
|
+
t.is(core.length, 1)
|
|
124
157
|
|
|
125
158
|
await store.close()
|
|
126
159
|
store = new Corestore(dir.path)
|
|
127
160
|
core = store.get(key)
|
|
128
161
|
await core.ready()
|
|
129
162
|
|
|
130
|
-
t.
|
|
163
|
+
t.ok(core.writable)
|
|
131
164
|
await core.append('world')
|
|
132
|
-
t.
|
|
133
|
-
t.
|
|
134
|
-
t.
|
|
165
|
+
t.is(core.length, 2)
|
|
166
|
+
t.alike(await core.get(0), Buffer.from('hello'))
|
|
167
|
+
t.alike(await core.get(1), Buffer.from('world'))
|
|
135
168
|
|
|
136
169
|
await dir.cleanup()
|
|
137
|
-
t.end()
|
|
138
170
|
})
|
|
139
171
|
|
|
140
172
|
test('storage locking', async function (t) {
|
|
@@ -152,5 +184,24 @@ test('storage locking', async function (t) {
|
|
|
152
184
|
}
|
|
153
185
|
|
|
154
186
|
await dir.cleanup()
|
|
155
|
-
|
|
187
|
+
})
|
|
188
|
+
|
|
189
|
+
test('closing a namespace does not close cores', async function (t) {
|
|
190
|
+
const store = new Corestore(ram)
|
|
191
|
+
const ns1 = store.namespace('ns1')
|
|
192
|
+
const core1 = ns1.get({ name: 'core-1' })
|
|
193
|
+
const core2 = ns1.get({ name: 'core-2' })
|
|
194
|
+
await Promise.all([core1.ready(), core2.ready()])
|
|
195
|
+
|
|
196
|
+
await ns1.close()
|
|
197
|
+
|
|
198
|
+
t.is(store.cores.size, 2)
|
|
199
|
+
t.not(core1.closed)
|
|
200
|
+
t.not(core1.closed)
|
|
201
|
+
|
|
202
|
+
await store.close()
|
|
203
|
+
|
|
204
|
+
t.is(store.cores.size, 0)
|
|
205
|
+
t.ok(core1.closed)
|
|
206
|
+
t.ok(core2.closed)
|
|
156
207
|
})
|
package/test/keys.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
const p = require('path')
|
|
2
2
|
const fs = require('fs')
|
|
3
3
|
|
|
4
|
-
const test = require('
|
|
4
|
+
const test = require('brittle')
|
|
5
5
|
const ram = require('random-access-memory')
|
|
6
6
|
const raf = require('random-access-file')
|
|
7
7
|
|
|
@@ -13,11 +13,9 @@ test('can create hypercore keypairs', async t => {
|
|
|
13
13
|
const kp1 = await keys.createHypercoreKeyPair('core1')
|
|
14
14
|
const kp2 = await keys.createHypercoreKeyPair('core2')
|
|
15
15
|
|
|
16
|
-
t.
|
|
17
|
-
t.
|
|
18
|
-
t.
|
|
19
|
-
|
|
20
|
-
t.end()
|
|
16
|
+
t.is(kp1.publicKey.length, 32)
|
|
17
|
+
t.is(kp2.publicKey.length, 32)
|
|
18
|
+
t.unlike(kp1.publicKey, kp2.publicKey)
|
|
21
19
|
})
|
|
22
20
|
|
|
23
21
|
test('distinct tokens create distinct hypercore keypairs', async t => {
|
|
@@ -28,9 +26,7 @@ test('distinct tokens create distinct hypercore keypairs', async t => {
|
|
|
28
26
|
const kp1 = await keys.createHypercoreKeyPair('core1', token1)
|
|
29
27
|
const kp2 = await keys.createHypercoreKeyPair('core1', token2)
|
|
30
28
|
|
|
31
|
-
t.
|
|
32
|
-
|
|
33
|
-
t.end()
|
|
29
|
+
t.unlike(kp1.publicKey, kp2.publicKey)
|
|
34
30
|
})
|
|
35
31
|
|
|
36
32
|
test('short user-provided token will throw', async t => {
|
|
@@ -42,8 +38,6 @@ test('short user-provided token will throw', async t => {
|
|
|
42
38
|
} catch {
|
|
43
39
|
t.pass('threw correctly')
|
|
44
40
|
}
|
|
45
|
-
|
|
46
|
-
t.end()
|
|
47
41
|
})
|
|
48
42
|
|
|
49
43
|
test('persistent storage regenerates keys correctly', async t => {
|
|
@@ -55,10 +49,9 @@ test('persistent storage regenerates keys correctly', async t => {
|
|
|
55
49
|
const keys2 = await KeyManager.fromStorage((name) => raf(testPath, { directory: testPath }))
|
|
56
50
|
const kp2 = await keys2.createHypercoreKeyPair('core1')
|
|
57
51
|
|
|
58
|
-
t.
|
|
52
|
+
t.alike(kp1.publicKey, kp2.publicKey)
|
|
59
53
|
|
|
60
54
|
await fs.promises.rm(testPath, { recursive: true })
|
|
61
|
-
t.end()
|
|
62
55
|
})
|
|
63
56
|
|
|
64
57
|
test('different master keys -> different keys', async t => {
|
|
@@ -68,7 +61,5 @@ test('different master keys -> different keys', async t => {
|
|
|
68
61
|
const kp1 = await keys1.createHypercoreKeyPair('core1')
|
|
69
62
|
const kp2 = await keys2.createHypercoreKeyPair('core1')
|
|
70
63
|
|
|
71
|
-
t.
|
|
72
|
-
|
|
73
|
-
t.end()
|
|
64
|
+
t.unlike(kp1.publicKey, kp2.publicKey)
|
|
74
65
|
})
|