hypercore 10.0.0-alpha.9 → 10.2.0
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/README.md +154 -36
- package/index.js +579 -213
- package/lib/bitfield.js +110 -42
- package/lib/block-encryption.js +3 -2
- package/lib/block-store.js +10 -5
- package/lib/caps.js +32 -0
- package/lib/core.js +189 -47
- package/lib/download.js +22 -0
- package/lib/errors.js +50 -0
- package/lib/info.js +24 -0
- package/lib/merkle-tree.js +182 -106
- package/lib/messages.js +249 -168
- package/lib/oplog.js +6 -5
- package/lib/remote-bitfield.js +28 -7
- package/lib/replicator.js +1415 -624
- package/lib/streams.js +56 -0
- package/package.json +23 -16
- package/.github/workflows/test-node.yml +0 -23
- package/CHANGELOG.md +0 -37
- package/UPGRADE.md +0 -9
- package/examples/announce.js +0 -19
- package/examples/basic.js +0 -10
- package/examples/http.js +0 -123
- package/examples/lookup.js +0 -20
- package/lib/extensions.js +0 -76
- package/lib/protocol.js +0 -524
- package/lib/random-iterator.js +0 -46
- package/test/basic.js +0 -90
- package/test/bitfield.js +0 -71
- package/test/core.js +0 -290
- package/test/encodings.js +0 -18
- package/test/encryption.js +0 -121
- package/test/extension.js +0 -71
- package/test/helpers/index.js +0 -23
- package/test/merkle-tree.js +0 -518
- package/test/mutex.js +0 -137
- package/test/oplog.js +0 -399
- package/test/preload.js +0 -72
- package/test/replicate.js +0 -372
- package/test/sessions.js +0 -173
- package/test/user-data.js +0 -47
package/lib/bitfield.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
// TODO: needs massive improvements obvs
|
|
2
|
-
|
|
3
1
|
const BigSparseArray = require('big-sparse-array')
|
|
2
|
+
const b4a = require('b4a')
|
|
3
|
+
const bits = require('bits-to-bytes')
|
|
4
4
|
|
|
5
5
|
class FixedBitfield {
|
|
6
6
|
constructor (index, bitfield) {
|
|
@@ -10,27 +10,26 @@ class FixedBitfield {
|
|
|
10
10
|
}
|
|
11
11
|
|
|
12
12
|
get (index) {
|
|
13
|
-
|
|
14
|
-
const i = (index - j) / 32
|
|
15
|
-
|
|
16
|
-
return i < this.bitfield.length && (this.bitfield[i] & (1 << j)) !== 0
|
|
13
|
+
return bits.get(this.bitfield, index)
|
|
17
14
|
}
|
|
18
15
|
|
|
19
16
|
set (index, val) {
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
const v = this.bitfield[i]
|
|
23
|
-
|
|
24
|
-
if (val === ((v & (1 << j)) !== 0)) return false
|
|
17
|
+
return bits.set(this.bitfield, index, val)
|
|
18
|
+
}
|
|
25
19
|
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
20
|
+
setRange (start, length, val) {
|
|
21
|
+
// Using fill instead of setRange is ~2 orders of magnitude faster, but does
|
|
22
|
+
// have the downside of not being able to tell if any bits actually changed.
|
|
23
|
+
bits.fill(this.bitfield, val, start, start + length)
|
|
24
|
+
return true
|
|
25
|
+
}
|
|
29
26
|
|
|
30
|
-
|
|
27
|
+
firstSet (position) {
|
|
28
|
+
return bits.indexOf(this.bitfield, true, position)
|
|
29
|
+
}
|
|
31
30
|
|
|
32
|
-
|
|
33
|
-
return true
|
|
31
|
+
lastSet (position) {
|
|
32
|
+
return bits.lastIndexOf(this.bitfield, true, position)
|
|
34
33
|
}
|
|
35
34
|
}
|
|
36
35
|
|
|
@@ -40,9 +39,14 @@ module.exports = class Bitfield {
|
|
|
40
39
|
this.pages = new BigSparseArray()
|
|
41
40
|
this.unflushed = []
|
|
42
41
|
this.storage = storage
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
42
|
+
this.resumed = !!(buf && buf.byteLength >= 4)
|
|
43
|
+
|
|
44
|
+
const all = this.resumed
|
|
45
|
+
? new Uint32Array(
|
|
46
|
+
buf.buffer,
|
|
47
|
+
buf.byteOffset,
|
|
48
|
+
Math.floor(buf.byteLength / 4)
|
|
49
|
+
)
|
|
46
50
|
: new Uint32Array(1024)
|
|
47
51
|
|
|
48
52
|
for (let i = 0; i < all.length; i += 1024) {
|
|
@@ -53,47 +57,106 @@ module.exports = class Bitfield {
|
|
|
53
57
|
}
|
|
54
58
|
|
|
55
59
|
get (index) {
|
|
56
|
-
const j = index &
|
|
57
|
-
const i = (index - j) /
|
|
60
|
+
const j = index & (this.pageSize - 1)
|
|
61
|
+
const i = (index - j) / this.pageSize
|
|
62
|
+
|
|
58
63
|
const p = this.pages.get(i)
|
|
59
64
|
|
|
60
65
|
return p ? p.get(j) : false
|
|
61
66
|
}
|
|
62
67
|
|
|
63
68
|
set (index, val) {
|
|
64
|
-
const j = index &
|
|
65
|
-
const i = (index - j) /
|
|
69
|
+
const j = index & (this.pageSize - 1)
|
|
70
|
+
const i = (index - j) / this.pageSize
|
|
66
71
|
|
|
67
72
|
let p = this.pages.get(i)
|
|
68
73
|
|
|
69
|
-
if (!p) {
|
|
70
|
-
if (!val) return
|
|
74
|
+
if (!p && val) {
|
|
71
75
|
p = this.pages.set(i, new FixedBitfield(i, new Uint32Array(1024)))
|
|
72
76
|
}
|
|
73
77
|
|
|
74
|
-
if (
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
+
if (p && p.set(j, val) && !p.dirty) {
|
|
79
|
+
p.dirty = true
|
|
80
|
+
this.unflushed.push(p)
|
|
81
|
+
}
|
|
78
82
|
}
|
|
79
83
|
|
|
80
84
|
setRange (start, length, val) {
|
|
81
|
-
|
|
82
|
-
|
|
85
|
+
let j = start & (this.pageSize - 1)
|
|
86
|
+
let i = (start - j) / this.pageSize
|
|
87
|
+
|
|
88
|
+
while (length > 0) {
|
|
89
|
+
let p = this.pages.get(i)
|
|
90
|
+
|
|
91
|
+
if (!p && val) {
|
|
92
|
+
p = this.pages.set(i, new FixedBitfield(i, new Uint32Array(1024)))
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
const end = Math.min(j + length, this.pageSize)
|
|
96
|
+
const range = end - j
|
|
97
|
+
|
|
98
|
+
if (p && p.setRange(j, range, val) && !p.dirty) {
|
|
99
|
+
p.dirty = true
|
|
100
|
+
this.unflushed.push(p)
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
j = 0
|
|
104
|
+
i++
|
|
105
|
+
length -= range
|
|
83
106
|
}
|
|
84
107
|
}
|
|
85
108
|
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
109
|
+
firstSet (position) {
|
|
110
|
+
let j = position & (this.pageSize - 1)
|
|
111
|
+
let i = (position - j) / this.pageSize
|
|
112
|
+
|
|
113
|
+
while (i < this.pages.factor) {
|
|
114
|
+
const p = this.pages.get(i)
|
|
115
|
+
|
|
116
|
+
if (p) {
|
|
117
|
+
const index = p.firstSet(j)
|
|
118
|
+
|
|
119
|
+
if (index !== -1) {
|
|
120
|
+
return i * this.pageSize + index
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
j = 0
|
|
125
|
+
i++
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
return -1
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
lastSet (position) {
|
|
132
|
+
let j = position & (this.pageSize - 1)
|
|
133
|
+
let i = (position - j) / this.pageSize
|
|
134
|
+
|
|
135
|
+
while (i >= 0) {
|
|
136
|
+
const p = this.pages.get(i)
|
|
137
|
+
|
|
138
|
+
if (p) {
|
|
139
|
+
const index = p.lastSet(j)
|
|
140
|
+
|
|
141
|
+
if (index !== -1) {
|
|
142
|
+
return i * this.pageSize + index
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
j = this.pageSize - 1
|
|
147
|
+
i--
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
return -1
|
|
90
151
|
}
|
|
91
152
|
|
|
92
153
|
clear () {
|
|
93
154
|
return new Promise((resolve, reject) => {
|
|
94
|
-
this.storage.
|
|
95
|
-
if (err) reject(err)
|
|
96
|
-
|
|
155
|
+
this.storage.truncate(0, (err) => {
|
|
156
|
+
if (err) return reject(err)
|
|
157
|
+
this.pages = new BigSparseArray()
|
|
158
|
+
this.unflushed = []
|
|
159
|
+
resolve()
|
|
97
160
|
})
|
|
98
161
|
})
|
|
99
162
|
}
|
|
@@ -116,9 +179,14 @@ module.exports = class Bitfield {
|
|
|
116
179
|
let error = null
|
|
117
180
|
|
|
118
181
|
for (const page of this.unflushed) {
|
|
119
|
-
const
|
|
182
|
+
const buf = b4a.from(
|
|
183
|
+
page.bitfield.buffer,
|
|
184
|
+
page.bitfield.byteOffset,
|
|
185
|
+
page.bitfield.byteLength
|
|
186
|
+
)
|
|
187
|
+
|
|
120
188
|
page.dirty = false
|
|
121
|
-
this.storage.write(page.index * 4096,
|
|
189
|
+
this.storage.write(page.index * 4096, buf, done)
|
|
122
190
|
}
|
|
123
191
|
|
|
124
192
|
function done (err) {
|
|
@@ -147,7 +215,7 @@ module.exports = class Bitfield {
|
|
|
147
215
|
}
|
|
148
216
|
|
|
149
217
|
function ensureSize (uint32, size) {
|
|
150
|
-
if (uint32.
|
|
218
|
+
if (uint32.byteLength === size) return uint32
|
|
151
219
|
const a = new Uint32Array(1024)
|
|
152
220
|
a.set(uint32, 0)
|
|
153
221
|
return a
|
package/lib/block-encryption.js
CHANGED
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
const sodium = require('sodium-universal')
|
|
2
2
|
const c = require('compact-encoding')
|
|
3
|
+
const b4a = require('b4a')
|
|
3
4
|
|
|
4
|
-
const nonce =
|
|
5
|
+
const nonce = b4a.alloc(sodium.crypto_stream_NONCEBYTES)
|
|
5
6
|
|
|
6
7
|
module.exports = class BlockEncryption {
|
|
7
8
|
constructor (encryptionKey, hypercoreKey) {
|
|
8
|
-
const subKeys =
|
|
9
|
+
const subKeys = b4a.alloc(2 * sodium.crypto_stream_KEYBYTES)
|
|
9
10
|
|
|
10
11
|
this.key = encryptionKey
|
|
11
12
|
this.blockKey = subKeys.subarray(0, sodium.crypto_stream_KEYBYTES)
|
package/lib/block-store.js
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
const b4a = require('b4a')
|
|
2
|
+
|
|
1
3
|
module.exports = class BlockStore {
|
|
2
4
|
constructor (storage, tree) {
|
|
3
5
|
this.storage = storage
|
|
@@ -15,15 +17,18 @@ module.exports = class BlockStore {
|
|
|
15
17
|
|
|
16
18
|
putBatch (i, batch, offset) {
|
|
17
19
|
if (batch.length === 0) return Promise.resolve()
|
|
18
|
-
return this.put(i, batch.length === 1 ? batch[0] :
|
|
20
|
+
return this.put(i, batch.length === 1 ? batch[0] : b4a.concat(batch), offset)
|
|
19
21
|
}
|
|
20
22
|
|
|
21
|
-
clear () {
|
|
23
|
+
clear (offset = 0, length = -1) {
|
|
22
24
|
return new Promise((resolve, reject) => {
|
|
23
|
-
this.storage.
|
|
25
|
+
if (length === -1) this.storage.truncate(offset, done)
|
|
26
|
+
else this.storage.del(offset, length, done)
|
|
27
|
+
|
|
28
|
+
function done (err) {
|
|
24
29
|
if (err) reject(err)
|
|
25
30
|
else resolve()
|
|
26
|
-
}
|
|
31
|
+
}
|
|
27
32
|
})
|
|
28
33
|
}
|
|
29
34
|
|
|
@@ -49,7 +54,7 @@ module.exports = class BlockStore {
|
|
|
49
54
|
return new Promise((resolve, reject) => {
|
|
50
55
|
this.storage.write(offset, data, (err) => {
|
|
51
56
|
if (err) reject(err)
|
|
52
|
-
else resolve()
|
|
57
|
+
else resolve(offset + data.byteLength)
|
|
53
58
|
})
|
|
54
59
|
})
|
|
55
60
|
}
|
package/lib/caps.js
ADDED
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
const crypto = require('hypercore-crypto')
|
|
2
|
+
const sodium = require('sodium-universal')
|
|
3
|
+
const b4a = require('b4a')
|
|
4
|
+
const c = require('compact-encoding')
|
|
5
|
+
|
|
6
|
+
// TODO: rename this to "crypto" and move everything hashing related etc in here
|
|
7
|
+
// Also lets move the tree stuff from hypercore-crypto here
|
|
8
|
+
|
|
9
|
+
const [TREE, REPLICATE_INITIATOR, REPLICATE_RESPONDER] = crypto.namespace('hypercore', 3)
|
|
10
|
+
|
|
11
|
+
exports.replicate = function (isInitiator, key, handshakeHash) {
|
|
12
|
+
const out = b4a.allocUnsafe(32)
|
|
13
|
+
sodium.crypto_generichash_batch(out, [isInitiator ? REPLICATE_INITIATOR : REPLICATE_RESPONDER, key], handshakeHash)
|
|
14
|
+
return out
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
exports.treeSignable = function (hash, length, fork) {
|
|
18
|
+
const state = { start: 0, end: 80, buffer: b4a.allocUnsafe(80) }
|
|
19
|
+
c.raw.encode(state, TREE)
|
|
20
|
+
c.raw.encode(state, hash)
|
|
21
|
+
c.uint64.encode(state, length)
|
|
22
|
+
c.uint64.encode(state, fork)
|
|
23
|
+
return state.buffer
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
exports.treeSignableLegacy = function (hash, length, fork) {
|
|
27
|
+
const state = { start: 0, end: 48, buffer: b4a.allocUnsafe(48) }
|
|
28
|
+
c.raw.encode(state, hash)
|
|
29
|
+
c.uint64.encode(state, length)
|
|
30
|
+
c.uint64.encode(state, fork)
|
|
31
|
+
return state.buffer
|
|
32
|
+
}
|