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 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.discoveryKey) {
41
+ if (opts._discoveryKey) {
41
42
  return {
42
43
  keyPair: null,
43
44
  sign: null,
44
- discoveryKey: opts.discoveryKey
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
- async _postload (core) {
69
- const name = await core.getUserData(USERDATA_NAME_KEY)
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 = await core.getUserData(USERDATA_NAMESPACE_KEY)
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 postload return keypair/sign?
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
- if (!existing.closing) return { from: existing, keyPair, sign }
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
- postload: this._postload.bind(this),
115
- createIfMissing: !!opts.keyPair
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
- for (const stream of this._replicationStreams) {
120
- core.replicate(stream)
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 stream = isStream(opts) ? opts : (opts.stream || Hypercore.createProtocolStream(opts))
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.on('discovery-key', discoveryKey => {
145
- const core = this.get({ discoveryKey })
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(stream), 1)
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
- stream.destroy()
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.discoveryKey && !Buffer.isBuffer(opts.discoveryKey)) throw new Error('discoveryKey option must be a Buffer')
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.5",
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 && tape test/*.js"
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('tape')
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.same(core1a.key, core1b.key)
17
- t.notSame(core1a.key, core2.key)
16
+ t.alike(core1a.key, core1b.key)
17
+ t.unlike(core1a.key, core2.key)
18
18
 
19
- t.true(core1a.writable)
20
- t.true(core1b.writable)
19
+ t.ok(core1a.writable)
20
+ t.ok(core1b.writable)
21
21
 
22
- t.same(store.cores.size, 2)
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.same(core1.key, kp1.publicKey)
37
- t.same(core2.key, kp2.publicKey)
38
- t.true(core1.writable)
39
- t.true(core2.writable)
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.false(core1.key.equals(core2.key))
56
- t.true(core1.key.equals(core3.key))
57
- t.true(core1.writable)
58
- t.true(core2.writable)
59
- t.true(core3.writable)
60
- t.same(store.cores.size, 2)
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.same(await core3.get(0), Buffer.from('hello'))
81
- t.same(await core4.get(0), Buffer.from('world'))
76
+ t.alike(await core3.get(0), Buffer.from('hello'))
77
+ t.alike(await core4.get(0), Buffer.from('world'))
78
+ })
82
79
 
83
- t.end()
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.false(core1.key.equals(core2.key))
96
- t.true(core1.writable)
97
- t.true(core2.writable)
98
- t.same(store.cores.size, 2)
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.same(store.cores.size, 1)
139
+ t.is(store.cores.size, 1)
108
140
  await core1.close()
109
- t.same(store.cores.size, 0)
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.true(core.writable)
152
+ t.ok(core.writable)
122
153
  await core.append('hello')
123
- t.same(core.length, 1)
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.true(core.writable)
161
+ t.ok(core.writable)
131
162
  await core.append('world')
132
- t.same(core.length, 2)
133
- t.same(await core.get(0), Buffer.from('hello'))
134
- t.same(await core.get(1), Buffer.from('world'))
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('tape')
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.same(kp1.publicKey.length, 32)
17
- t.same(kp2.publicKey.length, 32)
18
- t.notSame(kp1.publicKey, kp2.publicKey)
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.notSame(kp1.publicKey, kp2.publicKey)
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.same(kp1.publicKey, kp2.publicKey)
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.notSame(kp1.publicKey, kp2.publicKey)
72
-
73
- t.end()
64
+ t.unlike(kp1.publicKey, kp2.publicKey)
74
65
  })