corestore 6.0.1-alpha.5 → 6.0.1-alpha.9
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 +50 -34
- package/package.json +4 -3
- package/test/all.js +69 -40
- package/test/keys.js +7 -16
package/index.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
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')
|
|
@@ -37,11 +38,11 @@ module.exports = class Corestore extends EventEmitter {
|
|
|
37
38
|
}
|
|
38
39
|
|
|
39
40
|
async _generateKeys (opts) {
|
|
40
|
-
if (opts.
|
|
41
|
+
if (opts._discoveryKey) {
|
|
41
42
|
return {
|
|
42
43
|
keyPair: null,
|
|
43
44
|
sign: null,
|
|
44
|
-
discoveryKey: opts.
|
|
45
|
+
discoveryKey: opts._discoveryKey
|
|
45
46
|
}
|
|
46
47
|
}
|
|
47
48
|
if (!opts.name) {
|
|
@@ -65,15 +66,22 @@ module.exports = class Corestore extends EventEmitter {
|
|
|
65
66
|
}
|
|
66
67
|
}
|
|
67
68
|
|
|
68
|
-
|
|
69
|
-
const
|
|
69
|
+
_getPrereadyUserData (core, key) {
|
|
70
|
+
for (const { key: savedKey, value } of core.core.header.userData) {
|
|
71
|
+
if (key === savedKey) return value
|
|
72
|
+
}
|
|
73
|
+
return null
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
async _preready (core) {
|
|
77
|
+
const name = this._getPrereadyUserData(core, USERDATA_NAME_KEY)
|
|
70
78
|
if (!name) return
|
|
71
79
|
|
|
72
|
-
const namespace =
|
|
80
|
+
const namespace = this._getPrereadyUserData(core, USERDATA_NAMESPACE_KEY)
|
|
73
81
|
const { publicKey, sign } = await this.keys.createHypercoreKeyPair(name.toString(), namespace)
|
|
74
82
|
if (!publicKey.equals(core.key)) throw new Error('Stored core key does not match the provided name')
|
|
75
83
|
|
|
76
|
-
// TODO: Should Hypercore expose a helper for this, or should
|
|
84
|
+
// TODO: Should Hypercore expose a helper for this, or should preready return keypair/sign?
|
|
77
85
|
core.sign = sign
|
|
78
86
|
core.key = publicKey
|
|
79
87
|
core.writable = true
|
|
@@ -87,8 +95,10 @@ module.exports = class Corestore extends EventEmitter {
|
|
|
87
95
|
|
|
88
96
|
while (this.cores.has(id)) {
|
|
89
97
|
const existing = this.cores.get(id)
|
|
90
|
-
if (existing) {
|
|
91
|
-
|
|
98
|
+
if (existing.opened && !existing.closing) return { from: existing, keyPair, sign }
|
|
99
|
+
if (!existing.opened) {
|
|
100
|
+
await existing.ready().catch(safetyCatch)
|
|
101
|
+
} else if (existing.closing) {
|
|
92
102
|
await existing.close()
|
|
93
103
|
}
|
|
94
104
|
}
|
|
@@ -103,22 +113,28 @@ module.exports = class Corestore extends EventEmitter {
|
|
|
103
113
|
|
|
104
114
|
const storageRoot = [CORES_DIR, id.slice(0, 2), id.slice(2, 4), id].join('/')
|
|
105
115
|
const core = new Hypercore(p => this.storage(storageRoot + '/' + p), {
|
|
116
|
+
_preready: this._preready.bind(this),
|
|
106
117
|
autoClose: true,
|
|
107
118
|
encryptionKey: opts.encryptionKey || null,
|
|
108
|
-
keyPair: {
|
|
109
|
-
publicKey: keyPair.publicKey,
|
|
110
|
-
secretKey: null
|
|
111
|
-
},
|
|
112
119
|
userData,
|
|
113
120
|
sign: null,
|
|
114
|
-
|
|
115
|
-
|
|
121
|
+
createIfMissing: !opts._discoveryKey,
|
|
122
|
+
keyPair: keyPair && keyPair.publicKey
|
|
123
|
+
? {
|
|
124
|
+
publicKey: keyPair.publicKey,
|
|
125
|
+
secretKey: null
|
|
126
|
+
}
|
|
127
|
+
: null
|
|
116
128
|
})
|
|
117
129
|
|
|
118
130
|
this.cores.set(id, core)
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
131
|
+
core.ready().then(() => {
|
|
132
|
+
for (const { stream } of this._replicationStreams) {
|
|
133
|
+
core.replicate(stream)
|
|
134
|
+
}
|
|
135
|
+
}, () => {
|
|
136
|
+
this.cores.delete(id)
|
|
137
|
+
})
|
|
122
138
|
core.once('close', () => {
|
|
123
139
|
this.cores.delete(id)
|
|
124
140
|
})
|
|
@@ -136,22 +152,22 @@ module.exports = class Corestore extends EventEmitter {
|
|
|
136
152
|
return core
|
|
137
153
|
}
|
|
138
154
|
|
|
139
|
-
replicate (opts
|
|
140
|
-
const
|
|
155
|
+
replicate (isInitiator, opts) {
|
|
156
|
+
const isExternal = isStream(isInitiator) || !!(opts && opts.stream)
|
|
157
|
+
const stream = Hypercore.createProtocolStream(isInitiator, {
|
|
158
|
+
...opts,
|
|
159
|
+
ondiscoverykey: discoveryKey => {
|
|
160
|
+
const core = this.get({ _discoveryKey: discoveryKey })
|
|
161
|
+
return core.ready().catch(safetyCatch)
|
|
162
|
+
}
|
|
163
|
+
})
|
|
141
164
|
for (const core of this.cores.values()) {
|
|
142
|
-
core.replicate(stream)
|
|
165
|
+
if (core.opened) core.replicate(stream) // If the core is not opened, it will be replicated in preload.
|
|
143
166
|
}
|
|
144
|
-
stream
|
|
145
|
-
|
|
146
|
-
core.ready().then(() => {
|
|
147
|
-
core.replicate(stream)
|
|
148
|
-
}, () => {
|
|
149
|
-
stream.close(discoveryKey)
|
|
150
|
-
})
|
|
151
|
-
})
|
|
152
|
-
this._replicationStreams.push(stream)
|
|
167
|
+
const streamRecord = { stream, isExternal }
|
|
168
|
+
this._replicationStreams.push(streamRecord)
|
|
153
169
|
stream.once('close', () => {
|
|
154
|
-
this._replicationStreams.splice(this._replicationStreams.indexOf(
|
|
170
|
+
this._replicationStreams.splice(this._replicationStreams.indexOf(streamRecord), 1)
|
|
155
171
|
})
|
|
156
172
|
return stream
|
|
157
173
|
}
|
|
@@ -175,8 +191,9 @@ module.exports = class Corestore extends EventEmitter {
|
|
|
175
191
|
closePromises.push(core.close())
|
|
176
192
|
}
|
|
177
193
|
await Promise.allSettled(closePromises)
|
|
178
|
-
for (const stream of this._replicationStreams) {
|
|
179
|
-
|
|
194
|
+
for (const { stream, isExternal } of this._replicationStreams) {
|
|
195
|
+
// Only close streams that were created by the Corestore
|
|
196
|
+
if (!isExternal) stream.destroy()
|
|
180
197
|
}
|
|
181
198
|
await this.keys.close()
|
|
182
199
|
}
|
|
@@ -206,8 +223,7 @@ function validateGetOptions (opts) {
|
|
|
206
223
|
if (opts.name && opts.secretKey) throw new Error('Cannot provide both a name and a secret key')
|
|
207
224
|
if (opts.publicKey && !Buffer.isBuffer(opts.publicKey)) throw new Error('publicKey option must be a Buffer')
|
|
208
225
|
if (opts.secretKey && !Buffer.isBuffer(opts.secretKey)) throw new Error('secretKey option must be a Buffer')
|
|
209
|
-
if (opts.
|
|
210
|
-
if (!opts.name && !opts.publicKey) throw new Error('Must provide either a name or a publicKey')
|
|
226
|
+
if (!opts._discoveryKey && (!opts.name && !opts.publicKey)) throw new Error('Must provide either a name or a publicKey')
|
|
211
227
|
return opts
|
|
212
228
|
}
|
|
213
229
|
|
package/package.json
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "corestore",
|
|
3
|
-
"version": "6.0.1-alpha.
|
|
3
|
+
"version": "6.0.1-alpha.9",
|
|
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,10 +20,10 @@
|
|
|
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": {
|
|
@@ -31,6 +31,7 @@
|
|
|
31
31
|
"derive-key": "^1.0.1",
|
|
32
32
|
"hypercore": "next",
|
|
33
33
|
"hypercore-crypto": "^2.3.0",
|
|
34
|
+
"safety-catch": "^1.0.1",
|
|
34
35
|
"sodium-universal": "^3.0.4"
|
|
35
36
|
}
|
|
36
37
|
}
|
package/test/all.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
const test = require('
|
|
1
|
+
const test = require('brittle')
|
|
2
2
|
const crypto = require('hypercore-crypto')
|
|
3
3
|
const ram = require('random-access-memory')
|
|
4
4
|
const tmp = require('tmp-promise')
|
|
@@ -13,15 +13,13 @@ test('basic get with caching', async function (t) {
|
|
|
13
13
|
|
|
14
14
|
await Promise.all([core1a.ready(), core1b.ready(), core2.ready()])
|
|
15
15
|
|
|
16
|
-
t.
|
|
17
|
-
t.
|
|
16
|
+
t.alike(core1a.key, core1b.key)
|
|
17
|
+
t.unlike(core1a.key, core2.key)
|
|
18
18
|
|
|
19
|
-
t.
|
|
20
|
-
t.
|
|
19
|
+
t.ok(core1a.writable)
|
|
20
|
+
t.ok(core1b.writable)
|
|
21
21
|
|
|
22
|
-
t.
|
|
23
|
-
|
|
24
|
-
t.end()
|
|
22
|
+
t.is(store.cores.size, 2)
|
|
25
23
|
})
|
|
26
24
|
|
|
27
25
|
test('basic get with custom keypair', async function (t) {
|
|
@@ -33,12 +31,10 @@ test('basic get with custom keypair', async function (t) {
|
|
|
33
31
|
const core2 = store.get(kp2)
|
|
34
32
|
await Promise.all([core1.ready(), core2.ready()])
|
|
35
33
|
|
|
36
|
-
t.
|
|
37
|
-
t.
|
|
38
|
-
t.
|
|
39
|
-
t.
|
|
40
|
-
|
|
41
|
-
t.end()
|
|
34
|
+
t.alike(core1.key, kp1.publicKey)
|
|
35
|
+
t.alike(core2.key, kp2.publicKey)
|
|
36
|
+
t.ok(core1.writable)
|
|
37
|
+
t.ok(core2.writable)
|
|
42
38
|
})
|
|
43
39
|
|
|
44
40
|
test('basic namespaces', async function (t) {
|
|
@@ -52,12 +48,12 @@ test('basic namespaces', async function (t) {
|
|
|
52
48
|
const core3 = ns3.get({ name: 'main' })
|
|
53
49
|
await Promise.all([core1.ready(), core2.ready(), core3.ready()])
|
|
54
50
|
|
|
55
|
-
t.
|
|
56
|
-
t.
|
|
57
|
-
t.
|
|
58
|
-
t.
|
|
59
|
-
t.
|
|
60
|
-
t.
|
|
51
|
+
t.absent(core1.key.equals(core2.key))
|
|
52
|
+
t.ok(core1.key.equals(core3.key))
|
|
53
|
+
t.ok(core1.writable)
|
|
54
|
+
t.ok(core2.writable)
|
|
55
|
+
t.ok(core3.writable)
|
|
56
|
+
t.is(store.cores.size, 2)
|
|
61
57
|
|
|
62
58
|
t.end()
|
|
63
59
|
})
|
|
@@ -77,10 +73,48 @@ test('basic replication', async function (t) {
|
|
|
77
73
|
const s = store1.replicate(true)
|
|
78
74
|
s.pipe(store2.replicate(false)).pipe(s)
|
|
79
75
|
|
|
80
|
-
t.
|
|
81
|
-
t.
|
|
76
|
+
t.alike(await core3.get(0), Buffer.from('hello'))
|
|
77
|
+
t.alike(await core4.get(0), Buffer.from('world'))
|
|
78
|
+
})
|
|
82
79
|
|
|
83
|
-
|
|
80
|
+
test('replicating cores created after replication begins', async function (t) {
|
|
81
|
+
const store1 = new Corestore(ram)
|
|
82
|
+
const store2 = new Corestore(ram)
|
|
83
|
+
|
|
84
|
+
const s = store1.replicate(true, { live: true })
|
|
85
|
+
s.pipe(store2.replicate(false, { live: true })).pipe(s)
|
|
86
|
+
|
|
87
|
+
const core1 = store1.get({ name: 'core-1' })
|
|
88
|
+
const core2 = store1.get({ name: 'core-2' })
|
|
89
|
+
await core1.append('hello')
|
|
90
|
+
await core2.append('world')
|
|
91
|
+
|
|
92
|
+
const core3 = store2.get({ key: core1.key })
|
|
93
|
+
const core4 = store2.get({ key: core2.key })
|
|
94
|
+
|
|
95
|
+
t.alike(await core3.get(0), Buffer.from('hello'))
|
|
96
|
+
t.alike(await core4.get(0), Buffer.from('world'))
|
|
97
|
+
})
|
|
98
|
+
|
|
99
|
+
test('replicating cores using discovery key hook', async function (t) {
|
|
100
|
+
const dir = await tmp.dir({ unsafeCleanup: true })
|
|
101
|
+
let store1 = new Corestore(dir.path)
|
|
102
|
+
const store2 = new Corestore(ram)
|
|
103
|
+
|
|
104
|
+
const core = store1.get({ name: 'main' })
|
|
105
|
+
await core.append('hello')
|
|
106
|
+
const key = core.key
|
|
107
|
+
|
|
108
|
+
await store1.close()
|
|
109
|
+
store1 = new Corestore(dir.path)
|
|
110
|
+
|
|
111
|
+
const s = store1.replicate(true, { live: true })
|
|
112
|
+
s.pipe(store2.replicate(false, { live: true })).pipe(s)
|
|
113
|
+
|
|
114
|
+
const core2 = store2.get(key)
|
|
115
|
+
t.alike(await core2.get(0), Buffer.from('hello'))
|
|
116
|
+
|
|
117
|
+
await dir.cleanup()
|
|
84
118
|
})
|
|
85
119
|
|
|
86
120
|
test('nested namespaces', async function (t) {
|
|
@@ -92,22 +126,19 @@ test('nested namespaces', async function (t) {
|
|
|
92
126
|
const core2 = ns1b.get({ name: 'main' })
|
|
93
127
|
await Promise.all([core1.ready(), core2.ready()])
|
|
94
128
|
|
|
95
|
-
t.
|
|
96
|
-
t.
|
|
97
|
-
t.
|
|
98
|
-
t.
|
|
99
|
-
|
|
100
|
-
t.end()
|
|
129
|
+
t.not(core1.key.equals(core2.key))
|
|
130
|
+
t.ok(core1.writable)
|
|
131
|
+
t.ok(core2.writable)
|
|
132
|
+
t.is(store.cores.size, 2)
|
|
101
133
|
})
|
|
102
134
|
|
|
103
135
|
test('core uncached when all sessions close', async function (t) {
|
|
104
136
|
const store = new Corestore(ram)
|
|
105
137
|
const core1 = store.get({ name: 'main' })
|
|
106
138
|
await core1.ready()
|
|
107
|
-
t.
|
|
139
|
+
t.is(store.cores.size, 1)
|
|
108
140
|
await core1.close()
|
|
109
|
-
t.
|
|
110
|
-
t.end()
|
|
141
|
+
t.is(store.cores.size, 0)
|
|
111
142
|
})
|
|
112
143
|
|
|
113
144
|
test('writable core loaded from name userData', async function (t) {
|
|
@@ -118,23 +149,22 @@ test('writable core loaded from name userData', async function (t) {
|
|
|
118
149
|
await core.ready()
|
|
119
150
|
const key = core.key
|
|
120
151
|
|
|
121
|
-
t.
|
|
152
|
+
t.ok(core.writable)
|
|
122
153
|
await core.append('hello')
|
|
123
|
-
t.
|
|
154
|
+
t.is(core.length, 1)
|
|
124
155
|
|
|
125
156
|
await store.close()
|
|
126
157
|
store = new Corestore(dir.path)
|
|
127
158
|
core = store.get(key)
|
|
128
159
|
await core.ready()
|
|
129
160
|
|
|
130
|
-
t.
|
|
161
|
+
t.ok(core.writable)
|
|
131
162
|
await core.append('world')
|
|
132
|
-
t.
|
|
133
|
-
t.
|
|
134
|
-
t.
|
|
163
|
+
t.is(core.length, 2)
|
|
164
|
+
t.alike(await core.get(0), Buffer.from('hello'))
|
|
165
|
+
t.alike(await core.get(1), Buffer.from('world'))
|
|
135
166
|
|
|
136
167
|
await dir.cleanup()
|
|
137
|
-
t.end()
|
|
138
168
|
})
|
|
139
169
|
|
|
140
170
|
test('storage locking', async function (t) {
|
|
@@ -152,5 +182,4 @@ test('storage locking', async function (t) {
|
|
|
152
182
|
}
|
|
153
183
|
|
|
154
184
|
await dir.cleanup()
|
|
155
|
-
t.end()
|
|
156
185
|
})
|
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
|
})
|