hypercore 9.12.0 → 10.0.0-alpha.11

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.
Files changed (86) hide show
  1. package/.github/workflows/test-node.yml +3 -4
  2. package/README.md +131 -404
  3. package/__snapshots__/test/storage.js.snapshot.cjs +15 -0
  4. package/examples/announce.js +19 -0
  5. package/examples/basic.js +10 -0
  6. package/examples/http.js +123 -0
  7. package/examples/lookup.js +20 -0
  8. package/index.js +365 -1600
  9. package/lib/bitfield.js +113 -285
  10. package/lib/block-encryption.js +68 -0
  11. package/lib/block-store.js +58 -0
  12. package/lib/core.js +468 -0
  13. package/lib/extensions.js +76 -0
  14. package/lib/merkle-tree.js +1110 -0
  15. package/lib/messages.js +571 -0
  16. package/lib/mutex.js +39 -0
  17. package/lib/oplog.js +224 -0
  18. package/lib/protocol.js +525 -0
  19. package/lib/random-iterator.js +46 -0
  20. package/lib/remote-bitfield.js +24 -0
  21. package/lib/replicator.js +857 -0
  22. package/lib/streams.js +39 -0
  23. package/package.json +44 -45
  24. package/test/basic.js +59 -471
  25. package/test/bitfield.js +48 -133
  26. package/test/core.js +290 -0
  27. package/test/encodings.js +18 -0
  28. package/test/encryption.js +123 -0
  29. package/test/extension.js +71 -0
  30. package/test/helpers/index.js +23 -0
  31. package/test/merkle-tree.js +518 -0
  32. package/test/mutex.js +137 -0
  33. package/test/oplog.js +399 -0
  34. package/test/preload.js +72 -0
  35. package/test/replicate.js +227 -824
  36. package/test/sessions.js +173 -0
  37. package/test/storage.js +31 -0
  38. package/test/streams.js +39 -146
  39. package/test/user-data.js +47 -0
  40. package/bench/all.sh +0 -65
  41. package/bench/copy-64kb-blocks.js +0 -51
  42. package/bench/helpers/read-throttled.js +0 -27
  43. package/bench/helpers/read.js +0 -47
  44. package/bench/helpers/write.js +0 -29
  45. package/bench/read-16kb-blocks-proof-throttled.js +0 -1
  46. package/bench/read-16kb-blocks-proof.js +0 -1
  47. package/bench/read-16kb-blocks-throttled.js +0 -1
  48. package/bench/read-16kb-blocks.js +0 -1
  49. package/bench/read-512b-blocks.js +0 -1
  50. package/bench/read-64kb-blocks-linear-batch.js +0 -18
  51. package/bench/read-64kb-blocks-linear.js +0 -18
  52. package/bench/read-64kb-blocks-proof.js +0 -1
  53. package/bench/read-64kb-blocks.js +0 -1
  54. package/bench/replicate-16kb-blocks.js +0 -19
  55. package/bench/replicate-64kb-blocks.js +0 -19
  56. package/bench/write-16kb-blocks.js +0 -1
  57. package/bench/write-512b-blocks.js +0 -1
  58. package/bench/write-64kb-blocks-static.js +0 -1
  59. package/bench/write-64kb-blocks.js +0 -1
  60. package/example.js +0 -23
  61. package/lib/cache.js +0 -26
  62. package/lib/crypto.js +0 -5
  63. package/lib/replicate.js +0 -829
  64. package/lib/safe-buffer-equals.js +0 -6
  65. package/lib/storage.js +0 -421
  66. package/lib/tree-index.js +0 -183
  67. package/test/ack.js +0 -306
  68. package/test/audit.js +0 -36
  69. package/test/cache.js +0 -93
  70. package/test/compat.js +0 -209
  71. package/test/copy.js +0 -377
  72. package/test/default-storage.js +0 -51
  73. package/test/extensions.js +0 -137
  74. package/test/get.js +0 -64
  75. package/test/head.js +0 -65
  76. package/test/helpers/create-tracking-ram.js +0 -27
  77. package/test/helpers/create.js +0 -6
  78. package/test/helpers/replicate.js +0 -4
  79. package/test/seek.js +0 -234
  80. package/test/selections.js +0 -95
  81. package/test/set-uploading-downloading.js +0 -91
  82. package/test/stats.js +0 -77
  83. package/test/timeouts.js +0 -22
  84. package/test/tree-index.js +0 -841
  85. package/test/update.js +0 -156
  86. package/test/value-encoding.js +0 -52
package/test/bitfield.js CHANGED
@@ -1,156 +1,71 @@
1
- var tape = require('tape')
2
- var bitfield = require('../lib/bitfield')
1
+ const test = require('brittle')
2
+ const ram = require('random-access-memory')
3
+ const Bitfield = require('../lib/bitfield')
3
4
 
4
- tape('set and get', function (t) {
5
- var b = bitfield()
5
+ test('bitfield - set and get', async function (t) {
6
+ const b = await Bitfield.open(ram())
6
7
 
7
- t.same(b.get(0), false)
8
- t.same(b.set(0, true), true)
9
- t.same(b.set(0, true), false)
10
- t.same(b.get(0), true)
8
+ t.absent(b.get(42))
9
+ b.set(42, true)
10
+ t.ok(b.get(42))
11
11
 
12
- t.same(b.get(1424244), false)
13
- t.same(b.set(1424244, true), true)
14
- t.same(b.set(1424244, true), false)
15
- t.same(b.get(1424244), true)
12
+ // bigger offsets
13
+ t.absent(b.get(42000000))
14
+ b.set(42000000, true)
15
+ t.ok(b.get(42000000))
16
16
 
17
- t.end()
18
- })
19
-
20
- tape('set and get (tree)', function (t) {
21
- var b = bitfield()
22
- var tree = b.tree
23
-
24
- t.same(tree.get(0), false)
25
- t.same(tree.set(0, true), true)
26
- t.same(tree.set(0, true), false)
27
- t.same(tree.get(0), true)
28
-
29
- t.same(tree.get(1424244), false)
30
- t.same(tree.set(1424244, true), true)
31
- t.same(tree.set(1424244, true), false)
32
- t.same(tree.get(1424244), true)
33
-
34
- t.same(b.get(0), false)
35
- t.same(b.get(1424244), false)
36
-
37
- t.end()
38
- })
39
-
40
- tape('set and index', function (t) {
41
- var b = bitfield()
42
- var ite = b.iterator(0, 100000000)
43
- var i = 0
44
-
45
- t.same(ite.next(), 0)
46
-
47
- b.set(0, true)
48
- t.same(ite.seek(0).next(), 1)
49
-
50
- b.set(479, true)
51
- t.same(ite.seek(478).next(), 478)
52
- t.same(ite.next(), 480)
53
-
54
- b.set(1, true)
55
- t.same(ite.seek(0).next(), 2)
56
-
57
- b.set(2, true)
58
- t.same(ite.seek(0).next(), 3)
59
-
60
- b.set(3, true)
61
- t.same(ite.seek(0).next(), 4)
62
-
63
- for (i = 0; i < b.length; i++) {
64
- b.set(i, true)
65
- }
17
+ b.set(42000000, false)
18
+ t.absent(b.get(42000000))
66
19
 
67
- t.same(ite.seek(0).next(), b.length)
68
-
69
- for (i = 0; i < b.length; i++) {
70
- b.set(i, false)
71
- }
72
-
73
- t.same(ite.seek(0).next(), 0)
74
- t.end()
20
+ await b.flush()
75
21
  })
76
22
 
77
- tape('set and index (random)', function (t) {
78
- var b = bitfield()
23
+ test('bitfield - random set and gets', async function (t) {
24
+ const b = await Bitfield.open(ram())
25
+ const set = new Set()
79
26
 
80
- for (var i = 0; i < 100; i++) {
81
- t.ok(check(), 'index validates')
82
- set(Math.round(Math.random() * 2000), Math.round(Math.random() * 8))
27
+ for (let i = 0; i < 200; i++) {
28
+ const idx = Math.floor(Math.random() * Number.MAX_SAFE_INTEGER)
29
+ b.set(idx, true)
30
+ set.add(idx)
83
31
  }
84
32
 
85
- t.ok(check(), 'index validates')
86
- t.end()
87
-
88
- function check () {
89
- var all = []
90
- var ite = b.iterator()
91
- var i = 0
92
-
93
- for (i = 0; i < b.length; i++) {
94
- all[i] = true
95
- }
96
-
97
- i = ite.next()
98
-
99
- while (i > -1) {
100
- all[i] = false
101
- i = ite.next()
33
+ for (let i = 0; i < 500; i++) {
34
+ const idx = Math.floor(Math.random() * Number.MAX_SAFE_INTEGER)
35
+ const expected = set.has(idx)
36
+ const val = b.get(idx)
37
+ if (val !== expected) {
38
+ t.fail('expected ' + expected + ' but got ' + val + ' at ' + idx)
39
+ return
102
40
  }
103
-
104
- for (i = 0; i < b.length; i++) {
105
- if (b.get(i) !== all[i]) {
106
- return false
107
- }
108
- }
109
-
110
- return true
111
41
  }
112
42
 
113
- function set (i, n) {
114
- while (n--) {
115
- b.set(i++, true)
43
+ for (const idx of set) {
44
+ const val = b.get(idx)
45
+ if (val !== true) {
46
+ t.fail('expected true but got ' + val + ' at ' + idx)
47
+ return
116
48
  }
117
49
  }
118
- })
119
50
 
120
- tape('get total positive bits', function (t) {
121
- var b = bitfield()
122
-
123
- t.same(b.set(1, true), true)
124
- t.same(b.set(2, true), true)
125
- t.same(b.set(4, true), true)
126
- t.same(b.set(5, true), true)
127
- t.same(b.set(39, true), true)
128
-
129
- t.same(b.total(0, 4), 2)
130
- t.same(b.total(3, 4), 0)
131
- t.same(b.total(3, 5), 1)
132
- t.same(b.total(3, 40), 3)
133
- t.same(b.total(), 5)
134
- t.same(b.total(7), 1)
135
-
136
- t.end()
51
+ t.pass('all random set and gets pass')
137
52
  })
138
53
 
139
- tape('bitfield dedup', function (t) {
140
- var b = bitfield()
54
+ test('bitfield - reload', async function (t) {
55
+ const s = ram()
141
56
 
142
- for (var i = 0; i < 32 * 1024; i++) {
143
- b.set(i, true)
57
+ {
58
+ const b = await Bitfield.open(s)
59
+ b.set(142, true)
60
+ b.set(40000, true)
61
+ b.set(1424242424, true)
62
+ await b.flush()
144
63
  }
145
64
 
146
- for (var j = 0; j < 64 * 1024; j++) {
147
- b.tree.set(j, true)
65
+ {
66
+ const b = await Bitfield.open(s)
67
+ t.ok(b.get(142))
68
+ t.ok(b.get(40000))
69
+ t.ok(b.get(1424242424))
148
70
  }
149
-
150
- t.ok(b.get(8 * 1024))
151
- t.ok(b.get(16 * 1024))
152
- b.set(8 * 1024, false)
153
- t.notOk(b.get(8 * 1024))
154
- t.ok(b.get(16 * 1024))
155
- t.end()
156
71
  })
package/test/core.js ADDED
@@ -0,0 +1,290 @@
1
+ const test = require('brittle')
2
+ const RAM = require('random-access-memory')
3
+ const Core = require('../lib/core')
4
+
5
+ test('core - append', async function (t) {
6
+ const { core } = await create()
7
+
8
+ {
9
+ const seq = await core.append([
10
+ Buffer.from('hello'),
11
+ Buffer.from('world')
12
+ ])
13
+
14
+ t.is(seq, 0)
15
+ t.is(core.tree.length, 2)
16
+ t.is(core.tree.byteLength, 10)
17
+ t.alike([
18
+ await core.blocks.get(0),
19
+ await core.blocks.get(1)
20
+ ], [
21
+ Buffer.from('hello'),
22
+ Buffer.from('world')
23
+ ])
24
+ }
25
+
26
+ {
27
+ const seq = await core.append([
28
+ Buffer.from('hej')
29
+ ])
30
+
31
+ t.is(seq, 2)
32
+ t.is(core.tree.length, 3)
33
+ t.is(core.tree.byteLength, 13)
34
+ t.alike([
35
+ await core.blocks.get(0),
36
+ await core.blocks.get(1),
37
+ await core.blocks.get(2)
38
+ ], [
39
+ Buffer.from('hello'),
40
+ Buffer.from('world'),
41
+ Buffer.from('hej')
42
+ ])
43
+ }
44
+ })
45
+
46
+ test('core - append and truncate', async function (t) {
47
+ const { core, reopen } = await create()
48
+
49
+ await core.append([
50
+ Buffer.from('hello'),
51
+ Buffer.from('world'),
52
+ Buffer.from('fo'),
53
+ Buffer.from('ooo')
54
+ ])
55
+
56
+ await core.truncate(3, 1)
57
+
58
+ t.is(core.tree.length, 3)
59
+ t.is(core.tree.byteLength, 12)
60
+ t.is(core.tree.fork, 1)
61
+ t.alike(core.header.hints.reorgs, [{ from: 0, to: 1, ancestors: 3 }])
62
+
63
+ await core.append([
64
+ Buffer.from('a'),
65
+ Buffer.from('b'),
66
+ Buffer.from('c'),
67
+ Buffer.from('d')
68
+ ])
69
+
70
+ await core.truncate(3, 2)
71
+
72
+ t.is(core.tree.length, 3)
73
+ t.is(core.tree.byteLength, 12)
74
+ t.is(core.tree.fork, 2)
75
+ t.alike(core.header.hints.reorgs, [{ from: 0, to: 1, ancestors: 3 }, { from: 1, to: 2, ancestors: 3 }])
76
+
77
+ await core.truncate(2, 3)
78
+
79
+ t.alike(core.header.hints.reorgs, [{ from: 2, to: 3, ancestors: 2 }])
80
+
81
+ await core.append([Buffer.from('a')])
82
+ await core.truncate(2, 4)
83
+
84
+ await core.append([Buffer.from('a')])
85
+ await core.truncate(2, 5)
86
+
87
+ await core.append([Buffer.from('a')])
88
+ await core.truncate(2, 6)
89
+
90
+ await core.append([Buffer.from('a')])
91
+ await core.truncate(2, 7)
92
+
93
+ t.is(core.header.hints.reorgs.length, 4)
94
+
95
+ // check that it was persisted
96
+ const coreReopen = await reopen()
97
+
98
+ t.is(coreReopen.tree.length, 2)
99
+ t.is(coreReopen.tree.byteLength, 10)
100
+ t.is(coreReopen.tree.fork, 7)
101
+ t.is(coreReopen.header.hints.reorgs.length, 4)
102
+ })
103
+
104
+ test('core - user data', async function (t) {
105
+ const { core, reopen } = await create()
106
+
107
+ await core.userData('hello', Buffer.from('world'))
108
+ t.alike(core.header.userData, [{ key: 'hello', value: Buffer.from('world') }])
109
+
110
+ await core.userData('hej', Buffer.from('verden'))
111
+ t.alike(core.header.userData, [
112
+ { key: 'hello', value: Buffer.from('world') },
113
+ { key: 'hej', value: Buffer.from('verden') }
114
+ ])
115
+
116
+ await core.userData('hello', null)
117
+ t.alike(core.header.userData, [{ key: 'hej', value: Buffer.from('verden') }])
118
+
119
+ await core.userData('hej', Buffer.from('world'))
120
+ t.alike(core.header.userData, [{ key: 'hej', value: Buffer.from('world') }])
121
+
122
+ // check that it was persisted
123
+ const coreReopen = await reopen()
124
+
125
+ t.alike(coreReopen.header.userData, [{ key: 'hej', value: Buffer.from('world') }])
126
+ })
127
+
128
+ test('core - verify', async function (t) {
129
+ const { core } = await create()
130
+ const { core: clone } = await create({ keyPair: { publicKey: core.header.signer.publicKey } })
131
+
132
+ t.is(clone.header.signer.publicKey, core.header.signer.publicKey)
133
+
134
+ await core.append([Buffer.from('a'), Buffer.from('b')])
135
+
136
+ {
137
+ const p = await core.tree.proof({ upgrade: { start: 0, length: 2 } })
138
+ await clone.verify(p)
139
+ }
140
+
141
+ t.is(clone.header.tree.length, 2)
142
+ t.is(clone.header.tree.signature, core.header.tree.signature)
143
+
144
+ {
145
+ const p = await core.tree.proof({ block: { index: 1, nodes: await clone.tree.nodes(2), value: true } })
146
+ p.block.value = await core.blocks.get(1)
147
+ await clone.verify(p)
148
+ }
149
+ })
150
+
151
+ test('core - verify parallel upgrades', async function (t) {
152
+ const { core } = await create()
153
+ const { core: clone } = await create({ keyPair: { publicKey: core.header.signer.publicKey } })
154
+
155
+ t.is(clone.header.signer.publicKey, core.header.signer.publicKey)
156
+
157
+ await core.append([Buffer.from('a'), Buffer.from('b'), Buffer.from('c'), Buffer.from('d')])
158
+
159
+ {
160
+ const p1 = await core.tree.proof({ upgrade: { start: 0, length: 2 } })
161
+ const p2 = await core.tree.proof({ upgrade: { start: 0, length: 3 } })
162
+
163
+ const v1 = clone.verify(p1)
164
+ const v2 = clone.verify(p2)
165
+
166
+ await v1
167
+ await v2
168
+ }
169
+
170
+ t.is(clone.header.tree.length, core.header.tree.length)
171
+ t.is(clone.header.tree.signature, core.header.tree.signature)
172
+ })
173
+
174
+ test('core - update hook is triggered', async function (t) {
175
+ const { core } = await create()
176
+ const { core: clone } = await create({ keyPair: { publicKey: core.header.signer.publicKey } })
177
+
178
+ let ran = 0
179
+
180
+ core.onupdate = (status, bitfield, value, from) => {
181
+ t.is(status, 0b01, 'was appended')
182
+ t.is(from, null, 'was local')
183
+ t.alike(bitfield, { drop: false, start: 0, length: 4 })
184
+ ran |= 1
185
+ }
186
+
187
+ await core.append([Buffer.from('a'), Buffer.from('b'), Buffer.from('c'), Buffer.from('d')])
188
+
189
+ const peer = {}
190
+
191
+ clone.onupdate = (status, bitfield, value, from) => {
192
+ t.is(status, 0b01, 'was appended')
193
+ t.is(from, peer, 'was remote')
194
+ t.alike(bitfield, { drop: false, start: 1, length: 1 })
195
+ t.alike(value, Buffer.from('b'))
196
+ ran |= 2
197
+ }
198
+
199
+ {
200
+ const p = await core.tree.proof({ block: { index: 1, nodes: 0, value: true }, upgrade: { start: 0, length: 2 } })
201
+ p.block.value = await core.blocks.get(1)
202
+ await clone.verify(p, peer)
203
+ }
204
+
205
+ clone.onupdate = (status, bitfield, value, from) => {
206
+ t.is(status, 0b00, 'no append or truncate')
207
+ t.is(from, peer, 'was remote')
208
+ t.alike(bitfield, { drop: false, start: 3, length: 1 })
209
+ t.alike(value, Buffer.from('d'))
210
+ ran |= 4
211
+ }
212
+
213
+ {
214
+ const p = await core.tree.proof({ block: { index: 3, nodes: await clone.tree.nodes(6), value: true } })
215
+ p.block.value = await core.blocks.get(3)
216
+ await clone.verify(p, peer)
217
+ }
218
+
219
+ core.onupdate = (status, bitfield, value, from) => {
220
+ t.is(status, 0b10, 'was truncated')
221
+ t.is(from, null, 'was local')
222
+ t.alike(bitfield, { drop: true, start: 1, length: 3 })
223
+ ran |= 8
224
+ }
225
+
226
+ await core.truncate(1, 1)
227
+
228
+ core.onupdate = (status, bitfield, value, from) => {
229
+ t.is(status, 0b01, 'was appended')
230
+ t.is(from, null, 'was local')
231
+ t.alike(bitfield, { drop: false, start: 1, length: 1 })
232
+ ran |= 16
233
+ }
234
+
235
+ await core.append([Buffer.from('e')])
236
+
237
+ clone.onupdate = (status, bitfield, value, from) => {
238
+ t.is(status, 0b11, 'was appended and truncated')
239
+ t.is(from, peer, 'was remote')
240
+ t.alike(bitfield, { drop: true, start: 1, length: 3 })
241
+ ran |= 32
242
+ }
243
+
244
+ {
245
+ const p = await core.tree.proof({ block: { index: 0, nodes: 0, value: false }, upgrade: { start: 0, length: 2 } })
246
+ const r = await clone.tree.reorg(p)
247
+
248
+ await clone.reorg(r, peer)
249
+ }
250
+
251
+ core.onupdate = (status, bitfield, value, from) => {
252
+ t.is(status, 0b10, 'was truncated')
253
+ t.is(from, null, 'was local')
254
+ t.alike(bitfield, { drop: true, start: 1, length: 1 })
255
+ ran |= 64
256
+ }
257
+
258
+ await core.truncate(1, 2)
259
+
260
+ clone.onupdate = (status, bitfield, value, from) => {
261
+ t.is(status, 0b10, 'was truncated')
262
+ t.is(from, peer, 'was remote')
263
+ t.alike(bitfield, { drop: true, start: 1, length: 1 })
264
+ ran |= 128
265
+ }
266
+
267
+ {
268
+ const p = await core.tree.proof({ block: { index: 0, nodes: 0, value: false }, upgrade: { start: 0, length: 1 } })
269
+ const r = await clone.tree.reorg(p)
270
+
271
+ await clone.reorg(r, peer)
272
+ }
273
+
274
+ t.is(ran, 255, 'ran all')
275
+ })
276
+
277
+ async function create (opts) {
278
+ const storage = new Map()
279
+
280
+ const createFile = (name) => {
281
+ if (storage.has(name)) return storage.get(name)
282
+ const s = new RAM()
283
+ storage.set(name, s)
284
+ return s
285
+ }
286
+
287
+ const reopen = () => Core.open(createFile, opts)
288
+ const core = await reopen()
289
+ return { core, reopen }
290
+ }
@@ -0,0 +1,18 @@
1
+ const test = require('brittle')
2
+ const { create } = require('./helpers')
3
+
4
+ test('encodings - supports built ins', async function (t) {
5
+ const a = await create(null, { valueEncoding: 'json' })
6
+
7
+ await a.append({ hello: 'world' })
8
+ t.alike(await a.get(0), { hello: 'world' })
9
+ t.alike(await a.get(0, { valueEncoding: 'utf-8' }), '{"hello":"world"}')
10
+ })
11
+
12
+ test('encodings - supports custom encoding', async function (t) {
13
+ const a = await create(null, { valueEncoding: { encode () { return Buffer.from('foo') }, decode () { return 'bar' } } })
14
+
15
+ await a.append({ hello: 'world' })
16
+ t.is(await a.get(0), 'bar')
17
+ t.alike(await a.get(0, { valueEncoding: 'utf-8' }), 'foo')
18
+ })
@@ -0,0 +1,123 @@
1
+ const test = require('brittle')
2
+ const RAM = require('random-access-memory')
3
+ const Hypercore = require('..')
4
+ const { create, replicate } = require('./helpers')
5
+
6
+ const encryptionKey = Buffer.alloc(32, 'hello world')
7
+
8
+ test('encrypted append and get', async function (t) {
9
+ const a = await create({ encryptionKey })
10
+
11
+ t.alike(a.encryptionKey, encryptionKey)
12
+
13
+ await a.append(['hello'])
14
+
15
+ t.is(a.byteLength, 5)
16
+ t.is(a.core.tree.byteLength, 5 + a.padding)
17
+
18
+ const unencrypted = await a.get(0)
19
+ t.alike(unencrypted, Buffer.from('hello'))
20
+
21
+ const encrypted = await a.core.blocks.get(0)
22
+ t.absent(encrypted.includes('hello'))
23
+ })
24
+
25
+ test('encrypted seek', async function (t) {
26
+ const a = await create({ encryptionKey })
27
+
28
+ await a.append(['hello', 'world', '!'])
29
+
30
+ t.alike(await a.seek(0), [0, 0])
31
+ t.alike(await a.seek(4), [0, 4])
32
+ t.alike(await a.seek(5), [1, 0])
33
+ t.alike(await a.seek(6), [1, 1])
34
+ t.alike(await a.seek(6), [1, 1])
35
+ t.alike(await a.seek(9), [1, 4])
36
+ t.alike(await a.seek(10), [2, 0])
37
+ t.alike(await a.seek(11), [3, 0])
38
+ })
39
+
40
+ test('encrypted replication', async function (t) {
41
+ const a = await create({ encryptionKey })
42
+
43
+ await a.append(['a', 'b', 'c', 'd', 'e'])
44
+
45
+ t.test('with encryption key', async function (t) {
46
+ const b = await create(a.key, { encryptionKey })
47
+
48
+ replicate(a, b, t)
49
+
50
+ await t.test('through direct download', async function (t) {
51
+ const r = b.download({ start: 0, end: a.length })
52
+ await r.downloaded()
53
+
54
+ for (let i = 0; i < 5; i++) {
55
+ t.alike(await b.get(i), await a.get(i))
56
+ }
57
+ })
58
+
59
+ await t.test('through indirect download', async function (t) {
60
+ await a.append(['f', 'g', 'h', 'i', 'j'])
61
+
62
+ for (let i = 5; i < 10; i++) {
63
+ t.alike(await b.get(i), await a.get(i))
64
+ }
65
+ })
66
+ })
67
+
68
+ t.test('without encryption key', async function (t) {
69
+ const b = await create(a.key)
70
+
71
+ replicate(a, b, t)
72
+
73
+ await t.test('through direct download', async function (t) {
74
+ const r = b.download({ start: 0, end: a.length })
75
+ await r.downloaded()
76
+
77
+ for (let i = 0; i < 5; i++) {
78
+ t.alike(await b.get(i), await a.core.blocks.get(i))
79
+ }
80
+ })
81
+
82
+ await t.test('through indirect download', async function (t) {
83
+ await a.append(['f', 'g', 'h', 'i', 'j'])
84
+
85
+ for (let i = 5; i < 10; i++) {
86
+ t.alike(await b.get(i), await a.core.blocks.get(i))
87
+ }
88
+ })
89
+ })
90
+ })
91
+
92
+ test('encrypted session', async function (t) {
93
+ const a = await create({ encryptionKey })
94
+
95
+ await a.append(['hello'])
96
+
97
+ const s = a.session()
98
+
99
+ t.alike(a.encryptionKey, s.encryptionKey)
100
+ t.alike(await s.get(0), Buffer.from('hello'))
101
+
102
+ await s.append(['world'])
103
+
104
+ const unencrypted = await s.get(1)
105
+ t.alike(unencrypted, Buffer.from('world'))
106
+ t.alike(await a.get(1), unencrypted)
107
+
108
+ const encrypted = await s.core.blocks.get(1)
109
+ t.absent(encrypted.includes('world'))
110
+ t.alike(await a.core.blocks.get(1), encrypted)
111
+ })
112
+
113
+ test('encrypted session before ready core', async function (t) {
114
+ const a = new Hypercore(RAM, { encryptionKey })
115
+ const s = a.session()
116
+
117
+ await a.ready()
118
+
119
+ t.alike(a.encryptionKey, s.encryptionKey)
120
+
121
+ await a.append(['hello'])
122
+ t.alike(await s.get(0), Buffer.from('hello'))
123
+ })
@@ -0,0 +1,71 @@
1
+ const test = require('brittle')
2
+ const { create, replicate, eventFlush } = require('./helpers')
3
+
4
+ test('basic extension', async function (t) {
5
+ const messages = ['world', 'hello']
6
+
7
+ const a = await create()
8
+ a.registerExtension('test-extension', {
9
+ encoding: 'utf-8',
10
+ onmessage: (message, peer) => {
11
+ t.ok(peer === a.peers[0])
12
+ t.is(message, messages.pop())
13
+ }
14
+ })
15
+
16
+ const b = await create(a.key)
17
+ const bExt = b.registerExtension('test-extension', {
18
+ encoding: 'utf-8'
19
+ })
20
+
21
+ replicate(a, b, t)
22
+
23
+ await eventFlush()
24
+ t.is(b.peers.length, 1)
25
+
26
+ bExt.send('hello', b.peers[0])
27
+ bExt.send('world', b.peers[0])
28
+
29
+ await eventFlush()
30
+ t.absent(messages.length)
31
+
32
+ t.end()
33
+ })
34
+
35
+ test('two extensions', async function (t) {
36
+ const messages = ['world', 'hello']
37
+
38
+ const a = await create()
39
+ const b = await create(a.key)
40
+
41
+ replicate(a, b, t)
42
+
43
+ b.registerExtension('test-extension-1', {
44
+ encoding: 'utf-8'
45
+ })
46
+ const bExt2 = b.registerExtension('test-extension-2', {
47
+ encoding: 'utf-8'
48
+ })
49
+
50
+ await eventFlush()
51
+ t.is(b.peers.length, 1)
52
+
53
+ bExt2.send('world', b.peers[0])
54
+
55
+ await eventFlush()
56
+
57
+ a.registerExtension('test-extension-2', {
58
+ encoding: 'utf-8',
59
+ onmessage: (message, peer) => {
60
+ t.ok(peer === a.peers[0])
61
+ t.is(message, messages.pop())
62
+ }
63
+ })
64
+
65
+ bExt2.send('hello', b.peers[0])
66
+
67
+ await eventFlush()
68
+ t.is(messages.length, 1) // First message gets ignored
69
+
70
+ t.end()
71
+ })