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.
- package/.github/workflows/test-node.yml +3 -4
- package/README.md +131 -404
- package/__snapshots__/test/storage.js.snapshot.cjs +15 -0
- package/examples/announce.js +19 -0
- package/examples/basic.js +10 -0
- package/examples/http.js +123 -0
- package/examples/lookup.js +20 -0
- package/index.js +365 -1600
- package/lib/bitfield.js +113 -285
- package/lib/block-encryption.js +68 -0
- package/lib/block-store.js +58 -0
- package/lib/core.js +468 -0
- package/lib/extensions.js +76 -0
- package/lib/merkle-tree.js +1110 -0
- package/lib/messages.js +571 -0
- package/lib/mutex.js +39 -0
- package/lib/oplog.js +224 -0
- package/lib/protocol.js +525 -0
- package/lib/random-iterator.js +46 -0
- package/lib/remote-bitfield.js +24 -0
- package/lib/replicator.js +857 -0
- package/lib/streams.js +39 -0
- package/package.json +44 -45
- package/test/basic.js +59 -471
- package/test/bitfield.js +48 -133
- package/test/core.js +290 -0
- package/test/encodings.js +18 -0
- package/test/encryption.js +123 -0
- package/test/extension.js +71 -0
- package/test/helpers/index.js +23 -0
- package/test/merkle-tree.js +518 -0
- package/test/mutex.js +137 -0
- package/test/oplog.js +399 -0
- package/test/preload.js +72 -0
- package/test/replicate.js +227 -824
- package/test/sessions.js +173 -0
- package/test/storage.js +31 -0
- package/test/streams.js +39 -146
- package/test/user-data.js +47 -0
- package/bench/all.sh +0 -65
- package/bench/copy-64kb-blocks.js +0 -51
- package/bench/helpers/read-throttled.js +0 -27
- package/bench/helpers/read.js +0 -47
- package/bench/helpers/write.js +0 -29
- package/bench/read-16kb-blocks-proof-throttled.js +0 -1
- package/bench/read-16kb-blocks-proof.js +0 -1
- package/bench/read-16kb-blocks-throttled.js +0 -1
- package/bench/read-16kb-blocks.js +0 -1
- package/bench/read-512b-blocks.js +0 -1
- package/bench/read-64kb-blocks-linear-batch.js +0 -18
- package/bench/read-64kb-blocks-linear.js +0 -18
- package/bench/read-64kb-blocks-proof.js +0 -1
- package/bench/read-64kb-blocks.js +0 -1
- package/bench/replicate-16kb-blocks.js +0 -19
- package/bench/replicate-64kb-blocks.js +0 -19
- package/bench/write-16kb-blocks.js +0 -1
- package/bench/write-512b-blocks.js +0 -1
- package/bench/write-64kb-blocks-static.js +0 -1
- package/bench/write-64kb-blocks.js +0 -1
- package/example.js +0 -23
- package/lib/cache.js +0 -26
- package/lib/crypto.js +0 -5
- package/lib/replicate.js +0 -829
- package/lib/safe-buffer-equals.js +0 -6
- package/lib/storage.js +0 -421
- package/lib/tree-index.js +0 -183
- package/test/ack.js +0 -306
- package/test/audit.js +0 -36
- package/test/cache.js +0 -93
- package/test/compat.js +0 -209
- package/test/copy.js +0 -377
- package/test/default-storage.js +0 -51
- package/test/extensions.js +0 -137
- package/test/get.js +0 -64
- package/test/head.js +0 -65
- package/test/helpers/create-tracking-ram.js +0 -27
- package/test/helpers/create.js +0 -6
- package/test/helpers/replicate.js +0 -4
- package/test/seek.js +0 -234
- package/test/selections.js +0 -95
- package/test/set-uploading-downloading.js +0 -91
- package/test/stats.js +0 -77
- package/test/timeouts.js +0 -22
- package/test/tree-index.js +0 -841
- package/test/update.js +0 -156
- package/test/value-encoding.js +0 -52
package/lib/bitfield.js
CHANGED
|
@@ -1,327 +1,155 @@
|
|
|
1
|
-
|
|
2
|
-
var rle = require('bitfield-rle')
|
|
3
|
-
var pager = require('memory-pager')
|
|
4
|
-
var bitfield = require('sparse-bitfield')
|
|
1
|
+
// TODO: needs massive improvements obvs
|
|
5
2
|
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
var DATA_ITERATE_MASK = [128, 192, 224, 240, 248, 252, 254, 255]
|
|
9
|
-
var DATA_UPDATE_MASK = [127, 191, 223, 239, 247, 251, 253, 254]
|
|
10
|
-
var MAP_PARENT_RIGHT = new Array(256)
|
|
11
|
-
var MAP_PARENT_LEFT = new Array(256)
|
|
12
|
-
var NEXT_DATA_0_BIT = new Array(256)
|
|
13
|
-
var NEXT_INDEX_0_BIT = new Array(256)
|
|
14
|
-
var TOTAL_1_BITS = new Array(256)
|
|
3
|
+
const BigSparseArray = require('big-sparse-array')
|
|
4
|
+
const b4a = require('b4a')
|
|
15
5
|
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
MAP_PARENT_LEFT[i] = MAP_PARENT_RIGHT[i] << 4
|
|
22
|
-
NEXT_DATA_0_BIT[i] = i === 255 ? -1 : (8 - Math.ceil(Math.log(256 - i) / Math.log(2)))
|
|
23
|
-
NEXT_INDEX_0_BIT[i] = i === 255 ? -1 : Math.floor(NEXT_DATA_0_BIT[i] / 2)
|
|
24
|
-
TOTAL_1_BITS[i] = nibble[i >> 4] + nibble[i & 0x0F]
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
module.exports = Bitfield
|
|
28
|
-
|
|
29
|
-
function Bitfield (pageSize, pages) {
|
|
30
|
-
if (!(this instanceof Bitfield)) return new Bitfield(pageSize, pages)
|
|
31
|
-
if (!pageSize) pageSize = 2048 + 1024 + 512
|
|
32
|
-
|
|
33
|
-
var deduplicate = Buffer.allocUnsafe(pageSize)
|
|
34
|
-
deduplicate.fill(255)
|
|
35
|
-
|
|
36
|
-
this.indexSize = pageSize - 2048 - 1024
|
|
37
|
-
this.pages = pager(pageSize, { deduplicate })
|
|
38
|
-
|
|
39
|
-
if (pages) {
|
|
40
|
-
for (var i = 0; i < pages.length; i++) {
|
|
41
|
-
this.pages.set(i, pages[i])
|
|
42
|
-
}
|
|
6
|
+
class FixedBitfield {
|
|
7
|
+
constructor (index, bitfield) {
|
|
8
|
+
this.dirty = false
|
|
9
|
+
this.index = index
|
|
10
|
+
this.bitfield = bitfield
|
|
43
11
|
}
|
|
44
12
|
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
pages: this.pages,
|
|
49
|
-
trackUpdates: true
|
|
50
|
-
})
|
|
51
|
-
|
|
52
|
-
this.tree = bitfield({
|
|
53
|
-
pageSize: 2048,
|
|
54
|
-
pageOffset: 1024,
|
|
55
|
-
pages: this.pages,
|
|
56
|
-
trackUpdates: true
|
|
57
|
-
})
|
|
58
|
-
|
|
59
|
-
this.index = bitfield({
|
|
60
|
-
pageSize: this.indexSize,
|
|
61
|
-
pageOffset: 1024 + 2048,
|
|
62
|
-
pages: this.pages,
|
|
63
|
-
trackUpdates: true
|
|
64
|
-
})
|
|
13
|
+
get (index) {
|
|
14
|
+
const j = index & 31
|
|
15
|
+
const i = (index - j) / 32
|
|
65
16
|
|
|
66
|
-
|
|
67
|
-
this._iterator = flat.iterator(0)
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
Bitfield.prototype.set = function (i, value) {
|
|
71
|
-
var o = i & 7
|
|
72
|
-
i = (i - o) / 8
|
|
73
|
-
var v = value ? this.data.getByte(i) | (128 >> o) : this.data.getByte(i) & DATA_UPDATE_MASK[o]
|
|
74
|
-
|
|
75
|
-
if (!this.data.setByte(i, v)) return false
|
|
76
|
-
this.length = this.data.length
|
|
77
|
-
this._setIndex(i, v)
|
|
78
|
-
return true
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
Bitfield.prototype.get = function (i) {
|
|
82
|
-
return this.data.get(i)
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
Bitfield.prototype.total = function (start, end) {
|
|
86
|
-
if (!start || start < 0) start = 0
|
|
87
|
-
if (!end) end = this.data.length
|
|
88
|
-
if (end < start) return 0
|
|
89
|
-
if (end > this.data.length) {
|
|
90
|
-
this._expand(end)
|
|
91
|
-
}
|
|
92
|
-
var o = start & 7
|
|
93
|
-
var e = end & 7
|
|
94
|
-
var pos = (start - o) / 8
|
|
95
|
-
var last = (end - e) / 8
|
|
96
|
-
var leftMask = (255 - (o ? DATA_ITERATE_MASK[o - 1] : 0))
|
|
97
|
-
var rightMask = (e ? DATA_ITERATE_MASK[e - 1] : 0)
|
|
98
|
-
var byte = this.data.getByte(pos)
|
|
99
|
-
if (pos === last) {
|
|
100
|
-
return TOTAL_1_BITS[byte & leftMask & rightMask]
|
|
101
|
-
}
|
|
102
|
-
var total = TOTAL_1_BITS[byte & leftMask]
|
|
103
|
-
for (var i = pos + 1; i < last; i++) {
|
|
104
|
-
total += TOTAL_1_BITS[this.data.getByte(i)]
|
|
17
|
+
return i < this.bitfield.length && (this.bitfield[i] & (1 << j)) !== 0
|
|
105
18
|
}
|
|
106
|
-
total += TOTAL_1_BITS[this.data.getByte(last) & rightMask]
|
|
107
|
-
return total
|
|
108
|
-
}
|
|
109
19
|
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
20
|
+
set (index, val) {
|
|
21
|
+
const j = index & 31
|
|
22
|
+
const i = (index - j) / 32
|
|
23
|
+
const v = this.bitfield[i]
|
|
113
24
|
|
|
114
|
-
|
|
115
|
-
var buf = Buffer.alloc(Math.ceil(length / 8))
|
|
25
|
+
if (val === ((v & (1 << j)) !== 0)) return false
|
|
116
26
|
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
27
|
+
const u = val
|
|
28
|
+
? v | (1 << j)
|
|
29
|
+
: v ^ (1 << j)
|
|
120
30
|
|
|
121
|
-
|
|
122
|
-
var page = this.data.pages.get(p, true)
|
|
123
|
-
if (!page || !page.buffer) continue
|
|
124
|
-
page.buffer.copy(buf, p * this.data.pageSize - offset, this.data.pageOffset, this.data.pageOffset + this.data.pageSize)
|
|
125
|
-
}
|
|
31
|
+
if (u === v) return false
|
|
126
32
|
|
|
127
|
-
|
|
33
|
+
this.bitfield[i] = u
|
|
34
|
+
return true
|
|
35
|
+
}
|
|
128
36
|
}
|
|
129
37
|
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
i = (i - o) / 4
|
|
137
|
-
|
|
138
|
-
var bitfield = this.index
|
|
139
|
-
var ite = this._iterator
|
|
140
|
-
var start = 2 * i
|
|
141
|
-
var byte = (bitfield.getByte(start) & INDEX_UPDATE_MASK[o]) | (getIndexValue(value) >> (2 * o))
|
|
142
|
-
var len = bitfield.length
|
|
143
|
-
var maxLength = this.pages.length * this.indexSize
|
|
38
|
+
module.exports = class Bitfield {
|
|
39
|
+
constructor (storage, buf) {
|
|
40
|
+
this.pageSize = 32768
|
|
41
|
+
this.pages = new BigSparseArray()
|
|
42
|
+
this.unflushed = []
|
|
43
|
+
this.storage = storage
|
|
144
44
|
|
|
145
|
-
|
|
45
|
+
const all = (buf && buf.byteLength >= 4)
|
|
46
|
+
? new Uint32Array(buf.buffer, buf.byteOffset, Math.floor(buf.byteLength / 4))
|
|
47
|
+
: new Uint32Array(1024)
|
|
146
48
|
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
byte = MAP_PARENT_RIGHT[byte] | MAP_PARENT_LEFT[bitfield.getByte(ite.sibling())]
|
|
49
|
+
for (let i = 0; i < all.length; i += 1024) {
|
|
50
|
+
const bitfield = ensureSize(all.subarray(i, i + 1024), 1024)
|
|
51
|
+
const page = new FixedBitfield(i / 1024, bitfield)
|
|
52
|
+
this.pages.set(page.index, page)
|
|
152
53
|
}
|
|
153
|
-
ite.parent()
|
|
154
54
|
}
|
|
155
55
|
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
Bitfield.prototype._expand = function (len) {
|
|
162
|
-
var roots = flat.fullRoots(2 * len)
|
|
163
|
-
var bitfield = this.index
|
|
164
|
-
var ite = this._iterator
|
|
165
|
-
var byte = 0
|
|
166
|
-
|
|
167
|
-
for (var i = 0; i < roots.length; i++) {
|
|
168
|
-
ite.seek(roots[i])
|
|
169
|
-
byte = bitfield.getByte(ite.index)
|
|
56
|
+
get (index) {
|
|
57
|
+
const j = index & 32767
|
|
58
|
+
const i = (index - j) / 32768
|
|
59
|
+
const p = this.pages.get(i)
|
|
170
60
|
|
|
171
|
-
|
|
172
|
-
if (ite.isLeft()) {
|
|
173
|
-
byte = MAP_PARENT_LEFT[byte] | MAP_PARENT_RIGHT[bitfield.getByte(ite.sibling())]
|
|
174
|
-
} else {
|
|
175
|
-
byte = MAP_PARENT_RIGHT[byte] | MAP_PARENT_LEFT[bitfield.getByte(ite.sibling())]
|
|
176
|
-
}
|
|
177
|
-
} while (setByteNoAlloc(bitfield, ite.parent(), byte))
|
|
61
|
+
return p ? p.get(j) : false
|
|
178
62
|
}
|
|
179
|
-
}
|
|
180
|
-
|
|
181
|
-
function setByteNoAlloc (bitfield, i, b) {
|
|
182
|
-
if (8 * i >= bitfield.length) return false
|
|
183
|
-
return bitfield.setByte(i, b)
|
|
184
|
-
}
|
|
185
|
-
|
|
186
|
-
Bitfield.prototype.iterator = function (start, end) {
|
|
187
|
-
var ite = new Iterator(this)
|
|
188
|
-
|
|
189
|
-
ite.range(start || 0, end || this.length)
|
|
190
|
-
ite.seek(0)
|
|
191
|
-
|
|
192
|
-
return ite
|
|
193
|
-
}
|
|
194
|
-
|
|
195
|
-
function Iterator (bitfield) {
|
|
196
|
-
this.start = 0
|
|
197
|
-
this.end = 0
|
|
198
63
|
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
this._bitfield = bitfield
|
|
203
|
-
}
|
|
204
|
-
|
|
205
|
-
Iterator.prototype.range = function (start, end) {
|
|
206
|
-
this.start = start
|
|
207
|
-
this.end = end
|
|
208
|
-
this._indexEnd = 2 * Math.ceil(end / 32)
|
|
64
|
+
set (index, val) {
|
|
65
|
+
const j = index & 32767
|
|
66
|
+
const i = (index - j) / 32768
|
|
209
67
|
|
|
210
|
-
|
|
211
|
-
this._bitfield._expand(this.end)
|
|
212
|
-
}
|
|
68
|
+
let p = this.pages.get(i)
|
|
213
69
|
|
|
214
|
-
|
|
215
|
-
|
|
70
|
+
if (!p) {
|
|
71
|
+
if (!val) return
|
|
72
|
+
p = this.pages.set(i, new FixedBitfield(i, new Uint32Array(1024)))
|
|
73
|
+
}
|
|
216
74
|
|
|
217
|
-
|
|
218
|
-
offset += this.start
|
|
219
|
-
if (offset < this.start) offset = this.start
|
|
75
|
+
if (!p.set(j, val) || p.dirty) return
|
|
220
76
|
|
|
221
|
-
|
|
222
|
-
this.
|
|
223
|
-
return this
|
|
77
|
+
p.dirty = true
|
|
78
|
+
this.unflushed.push(p)
|
|
224
79
|
}
|
|
225
80
|
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
this._byte = this._bitfield.data.getByte(this._pos) | (o ? DATA_ITERATE_MASK[o - 1] : 0)
|
|
230
|
-
|
|
231
|
-
return this
|
|
232
|
-
}
|
|
233
|
-
|
|
234
|
-
Iterator.prototype.random = function () {
|
|
235
|
-
var i = this.seek(Math.floor(Math.random() * (this.end - this.start))).next()
|
|
236
|
-
return i === -1 ? this.seek(0).next() : i
|
|
237
|
-
}
|
|
238
|
-
|
|
239
|
-
Iterator.prototype.next = function () {
|
|
240
|
-
if (this._pos === -1) return -1
|
|
241
|
-
|
|
242
|
-
var dataBitfield = this._bitfield.data
|
|
243
|
-
var free = NEXT_DATA_0_BIT[this._byte]
|
|
244
|
-
|
|
245
|
-
while (free === -1) {
|
|
246
|
-
this._byte = dataBitfield.getByte(++this._pos)
|
|
247
|
-
free = NEXT_DATA_0_BIT[this._byte]
|
|
248
|
-
|
|
249
|
-
if (free === -1) {
|
|
250
|
-
this._pos = this._skipAhead(this._pos)
|
|
251
|
-
if (this._pos === -1) return -1
|
|
252
|
-
|
|
253
|
-
this._byte = dataBitfield.getByte(this._pos)
|
|
254
|
-
free = NEXT_DATA_0_BIT[this._byte]
|
|
81
|
+
setRange (start, length, val) {
|
|
82
|
+
for (let i = 0; i < length; i++) {
|
|
83
|
+
this.set(start + i, val)
|
|
255
84
|
}
|
|
256
85
|
}
|
|
257
86
|
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
}
|
|
263
|
-
|
|
264
|
-
Iterator.prototype.peek = function () {
|
|
265
|
-
if (this._pos === -1) return -1
|
|
266
|
-
|
|
267
|
-
var free = NEXT_DATA_0_BIT[this._byte]
|
|
268
|
-
var n = 8 * this._pos + free
|
|
269
|
-
return n < this.end ? n : -1
|
|
270
|
-
}
|
|
271
|
-
|
|
272
|
-
Iterator.prototype._skipAhead = function (start) {
|
|
273
|
-
var indexBitfield = this._bitfield.index
|
|
274
|
-
var treeEnd = this._indexEnd
|
|
275
|
-
var ite = this._bitfield._iterator
|
|
276
|
-
var o = start & 3
|
|
277
|
-
|
|
278
|
-
ite.seek(2 * ((start - o) / 4))
|
|
279
|
-
|
|
280
|
-
var treeByte = indexBitfield.getByte(ite.index) | INDEX_ITERATE_MASK[o]
|
|
281
|
-
|
|
282
|
-
while (NEXT_INDEX_0_BIT[treeByte] === -1) {
|
|
283
|
-
if (ite.isLeft()) {
|
|
284
|
-
ite.next()
|
|
285
|
-
} else {
|
|
286
|
-
ite.next()
|
|
287
|
-
ite.parent()
|
|
288
|
-
}
|
|
289
|
-
|
|
290
|
-
if (rightSpan(ite) >= treeEnd) {
|
|
291
|
-
while (rightSpan(ite) >= treeEnd && isParent(ite)) ite.leftChild()
|
|
292
|
-
if (rightSpan(ite) >= treeEnd) return -1
|
|
293
|
-
}
|
|
294
|
-
|
|
295
|
-
treeByte = indexBitfield.getByte(ite.index)
|
|
87
|
+
// Should prob be removed, when/if we re-add compression
|
|
88
|
+
page (i) {
|
|
89
|
+
const p = this.pages.get(i)
|
|
90
|
+
return p ? p.bitfield : new Uint32Array(1024)
|
|
296
91
|
}
|
|
297
92
|
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
93
|
+
clear () {
|
|
94
|
+
return new Promise((resolve, reject) => {
|
|
95
|
+
this.storage.del(0, Infinity, (err) => {
|
|
96
|
+
if (err) reject(err)
|
|
97
|
+
else resolve()
|
|
98
|
+
})
|
|
99
|
+
})
|
|
100
|
+
}
|
|
301
101
|
|
|
302
|
-
|
|
102
|
+
close () {
|
|
103
|
+
return new Promise((resolve, reject) => {
|
|
104
|
+
this.storage.close((err) => {
|
|
105
|
+
if (err) reject(err)
|
|
106
|
+
else resolve()
|
|
107
|
+
})
|
|
108
|
+
})
|
|
303
109
|
}
|
|
304
110
|
|
|
305
|
-
|
|
306
|
-
|
|
111
|
+
flush () {
|
|
112
|
+
return new Promise((resolve, reject) => {
|
|
113
|
+
if (!this.unflushed.length) return resolve()
|
|
307
114
|
|
|
308
|
-
|
|
115
|
+
const self = this
|
|
116
|
+
let missing = this.unflushed.length
|
|
117
|
+
let error = null
|
|
309
118
|
|
|
310
|
-
|
|
311
|
-
|
|
119
|
+
for (const page of this.unflushed) {
|
|
120
|
+
const buf = b4a.from(page.bitfield.buffer, page.bitfield.byteOffset, page.bitfield.byteLength)
|
|
121
|
+
page.dirty = false
|
|
122
|
+
this.storage.write(page.index * 4096, buf, done)
|
|
123
|
+
}
|
|
312
124
|
|
|
313
|
-
function
|
|
314
|
-
|
|
315
|
-
|
|
125
|
+
function done (err) {
|
|
126
|
+
if (err) error = err
|
|
127
|
+
if (--missing) return
|
|
128
|
+
if (error) return reject(error)
|
|
129
|
+
self.unflushed = []
|
|
130
|
+
resolve()
|
|
131
|
+
}
|
|
132
|
+
})
|
|
133
|
+
}
|
|
316
134
|
|
|
317
|
-
|
|
318
|
-
|
|
135
|
+
static open (storage) {
|
|
136
|
+
return new Promise((resolve, reject) => {
|
|
137
|
+
storage.stat((err, st) => {
|
|
138
|
+
if (err) return resolve(new Bitfield(storage, null))
|
|
139
|
+
const size = st.size - (st.size & 3)
|
|
140
|
+
if (!size) return resolve(new Bitfield(storage, null))
|
|
141
|
+
storage.read(0, size, (err, data) => {
|
|
142
|
+
if (err) return reject(err)
|
|
143
|
+
resolve(new Bitfield(storage, data))
|
|
144
|
+
})
|
|
145
|
+
})
|
|
146
|
+
})
|
|
147
|
+
}
|
|
319
148
|
}
|
|
320
149
|
|
|
321
|
-
function
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
}
|
|
150
|
+
function ensureSize (uint32, size) {
|
|
151
|
+
if (uint32.length === size) return uint32
|
|
152
|
+
const a = new Uint32Array(1024)
|
|
153
|
+
a.set(uint32, 0)
|
|
154
|
+
return a
|
|
327
155
|
}
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
const sodium = require('sodium-universal')
|
|
2
|
+
const c = require('compact-encoding')
|
|
3
|
+
const b4a = require('b4a')
|
|
4
|
+
|
|
5
|
+
const nonce = b4a.alloc(sodium.crypto_stream_NONCEBYTES)
|
|
6
|
+
|
|
7
|
+
module.exports = class BlockEncryption {
|
|
8
|
+
constructor (encryptionKey, hypercoreKey) {
|
|
9
|
+
const subKeys = b4a.alloc(2 * sodium.crypto_stream_KEYBYTES)
|
|
10
|
+
|
|
11
|
+
this.key = encryptionKey
|
|
12
|
+
this.blockKey = subKeys.subarray(0, sodium.crypto_stream_KEYBYTES)
|
|
13
|
+
this.blindingKey = subKeys.subarray(sodium.crypto_stream_KEYBYTES)
|
|
14
|
+
this.padding = 8
|
|
15
|
+
|
|
16
|
+
sodium.crypto_generichash(this.blockKey, encryptionKey, hypercoreKey)
|
|
17
|
+
sodium.crypto_generichash(this.blindingKey, this.blockKey)
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
encrypt (index, block, fork) {
|
|
21
|
+
const padding = block.subarray(0, this.padding)
|
|
22
|
+
block = block.subarray(this.padding)
|
|
23
|
+
|
|
24
|
+
c.uint64.encode({ start: 0, end: 8, buffer: padding }, fork)
|
|
25
|
+
c.uint64.encode({ start: 0, end: 8, buffer: nonce }, index)
|
|
26
|
+
|
|
27
|
+
// Zero out any previous padding.
|
|
28
|
+
nonce.fill(0, 8, 8 + padding.byteLength)
|
|
29
|
+
|
|
30
|
+
// Blind the fork ID, possibly risking reusing the nonce on a reorg of the
|
|
31
|
+
// Hypercore. This is fine as the blinding is best-effort and the latest
|
|
32
|
+
// fork ID shared on replication anyway.
|
|
33
|
+
sodium.crypto_stream_xor(
|
|
34
|
+
padding,
|
|
35
|
+
padding,
|
|
36
|
+
nonce,
|
|
37
|
+
this.blindingKey
|
|
38
|
+
)
|
|
39
|
+
|
|
40
|
+
nonce.set(padding, 8)
|
|
41
|
+
|
|
42
|
+
// The combination of a (blinded) fork ID and a block index is unique for a
|
|
43
|
+
// given Hypercore and is therefore a valid nonce for encrypting the block.
|
|
44
|
+
sodium.crypto_stream_xor(
|
|
45
|
+
block,
|
|
46
|
+
block,
|
|
47
|
+
nonce,
|
|
48
|
+
this.blockKey
|
|
49
|
+
)
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
decrypt (index, block) {
|
|
53
|
+
const padding = block.subarray(0, this.padding)
|
|
54
|
+
block = block.subarray(this.padding)
|
|
55
|
+
|
|
56
|
+
c.uint64.encode({ start: 0, end: 8, buffer: nonce }, index)
|
|
57
|
+
|
|
58
|
+
nonce.set(padding, 8)
|
|
59
|
+
|
|
60
|
+
// Decrypt the block using the blinded fork ID.
|
|
61
|
+
sodium.crypto_stream_xor(
|
|
62
|
+
block,
|
|
63
|
+
block,
|
|
64
|
+
nonce,
|
|
65
|
+
this.blockKey
|
|
66
|
+
)
|
|
67
|
+
}
|
|
68
|
+
}
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
const b4a = require('b4a')
|
|
2
|
+
|
|
3
|
+
module.exports = class BlockStore {
|
|
4
|
+
constructor (storage, tree) {
|
|
5
|
+
this.storage = storage
|
|
6
|
+
this.tree = tree
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
async get (i) {
|
|
10
|
+
const [offset, size] = await this.tree.byteRange(2 * i)
|
|
11
|
+
return this._read(offset, size)
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
async put (i, data, offset) {
|
|
15
|
+
return this._write(offset, data)
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
putBatch (i, batch, offset) {
|
|
19
|
+
if (batch.length === 0) return Promise.resolve()
|
|
20
|
+
return this.put(i, batch.length === 1 ? batch[0] : b4a.concat(batch), offset)
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
clear () {
|
|
24
|
+
return new Promise((resolve, reject) => {
|
|
25
|
+
this.storage.del(0, Infinity, (err) => {
|
|
26
|
+
if (err) reject(err)
|
|
27
|
+
else resolve()
|
|
28
|
+
})
|
|
29
|
+
})
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
close () {
|
|
33
|
+
return new Promise((resolve, reject) => {
|
|
34
|
+
this.storage.close((err) => {
|
|
35
|
+
if (err) reject(err)
|
|
36
|
+
else resolve()
|
|
37
|
+
})
|
|
38
|
+
})
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
_read (offset, size) {
|
|
42
|
+
return new Promise((resolve, reject) => {
|
|
43
|
+
this.storage.read(offset, size, (err, data) => {
|
|
44
|
+
if (err) reject(err)
|
|
45
|
+
else resolve(data)
|
|
46
|
+
})
|
|
47
|
+
})
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
_write (offset, data) {
|
|
51
|
+
return new Promise((resolve, reject) => {
|
|
52
|
+
this.storage.write(offset, data, (err) => {
|
|
53
|
+
if (err) reject(err)
|
|
54
|
+
else resolve()
|
|
55
|
+
})
|
|
56
|
+
})
|
|
57
|
+
}
|
|
58
|
+
}
|