hypercore 10.38.1 → 11.0.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 +13 -30
- package/index.js +390 -442
- package/lib/audit.js +33 -41
- package/lib/bit-interlude.js +174 -0
- package/lib/bitfield.js +79 -87
- package/lib/block-store.js +12 -50
- package/lib/copy-prologue.js +236 -0
- package/lib/core.js +413 -746
- package/lib/download.js +42 -4
- package/lib/merkle-tree.js +263 -406
- package/lib/multisig.js +9 -6
- package/lib/mutex.js +4 -0
- package/lib/remote-bitfield.js +9 -9
- package/lib/replicator.js +247 -177
- package/lib/session-state.js +949 -0
- package/lib/verifier.js +20 -13
- package/package.json +2 -2
- package/lib/batch.js +0 -431
- package/lib/big-header.js +0 -55
- package/lib/oplog.js +0 -228
package/lib/verifier.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
const
|
|
1
|
+
const crypto = require('hypercore-crypto')
|
|
2
2
|
const b4a = require('b4a')
|
|
3
3
|
const c = require('compact-encoding')
|
|
4
4
|
const flat = require('flat-tree')
|
|
@@ -10,11 +10,10 @@ const multisig = require('./multisig')
|
|
|
10
10
|
const caps = require('./caps')
|
|
11
11
|
|
|
12
12
|
class Signer {
|
|
13
|
-
constructor (
|
|
13
|
+
constructor (manifestHash, version, index, { signature = 'ed25519', publicKey, namespace = caps.DEFAULT_NAMESPACE } = {}) {
|
|
14
14
|
if (!publicKey) throw BAD_ARGUMENT('public key is required for a signer')
|
|
15
15
|
if (signature !== 'ed25519') throw BAD_ARGUMENT('Only Ed25519 signatures are supported')
|
|
16
16
|
|
|
17
|
-
this.crypto = crypto
|
|
18
17
|
this.manifestHash = manifestHash
|
|
19
18
|
this.version = version
|
|
20
19
|
this.signer = index
|
|
@@ -28,31 +27,31 @@ class Signer {
|
|
|
28
27
|
}
|
|
29
28
|
|
|
30
29
|
verify (batch, signature) {
|
|
31
|
-
return
|
|
30
|
+
return crypto.verify(batch.signable(this._ctx()), signature, this.publicKey)
|
|
32
31
|
}
|
|
33
32
|
|
|
34
33
|
sign (batch, keyPair) {
|
|
35
|
-
return
|
|
34
|
+
return crypto.sign(batch.signable(this._ctx()), keyPair.secretKey)
|
|
36
35
|
}
|
|
37
36
|
}
|
|
38
37
|
|
|
39
38
|
class CompatSigner extends Signer {
|
|
40
|
-
constructor (
|
|
41
|
-
super(
|
|
39
|
+
constructor (index, signer, legacy) {
|
|
40
|
+
super(null, 0, index, signer)
|
|
42
41
|
this.legacy = legacy
|
|
43
42
|
}
|
|
44
43
|
|
|
45
44
|
verify (batch, signature) {
|
|
46
|
-
return
|
|
45
|
+
return crypto.verify(batch.signableCompat(this.legacy), signature, this.publicKey)
|
|
47
46
|
}
|
|
48
47
|
|
|
49
48
|
sign (batch, keyPair) {
|
|
50
|
-
return
|
|
49
|
+
return crypto.sign(batch.signableCompat(this.legacy), keyPair.secretKey)
|
|
51
50
|
}
|
|
52
51
|
}
|
|
53
52
|
|
|
54
53
|
module.exports = class Verifier {
|
|
55
|
-
constructor (manifestHash, manifest, { compat = isCompat(manifestHash, manifest),
|
|
54
|
+
constructor (manifestHash, manifest, { compat = isCompat(manifestHash, manifest), legacy = false } = {}) {
|
|
56
55
|
const self = this
|
|
57
56
|
|
|
58
57
|
this.manifestHash = manifestHash
|
|
@@ -67,8 +66,8 @@ module.exports = class Verifier {
|
|
|
67
66
|
|
|
68
67
|
function createSigner (signer, index) {
|
|
69
68
|
return self.compat
|
|
70
|
-
? new CompatSigner(
|
|
71
|
-
: new Signer(
|
|
69
|
+
? new CompatSigner(index, signer, legacy)
|
|
70
|
+
: new Signer(manifestHash, self.version, index, signer)
|
|
72
71
|
}
|
|
73
72
|
}
|
|
74
73
|
|
|
@@ -164,6 +163,14 @@ module.exports = class Verifier {
|
|
|
164
163
|
return manifestHash(manifest)
|
|
165
164
|
}
|
|
166
165
|
|
|
166
|
+
static encodeManifest (manifest) {
|
|
167
|
+
return c.encode(m.manifest, manifest)
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
static decodeManifest (manifest) {
|
|
171
|
+
return c.decode(m.manifest, manifest)
|
|
172
|
+
}
|
|
173
|
+
|
|
167
174
|
static defaultSignerManifest (publicKey) {
|
|
168
175
|
return {
|
|
169
176
|
version: 1,
|
|
@@ -293,7 +300,7 @@ function manifestHash (manifest) {
|
|
|
293
300
|
state.buffer = b4a.allocUnsafe(state.end)
|
|
294
301
|
c.raw.encode(state, caps.MANIFEST)
|
|
295
302
|
m.manifest.encode(state, manifest)
|
|
296
|
-
return
|
|
303
|
+
return crypto.hash(state.buffer)
|
|
297
304
|
}
|
|
298
305
|
|
|
299
306
|
function proofToVersion1 (proof) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "hypercore",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "11.0.0",
|
|
4
4
|
"description": "Hypercore is a secure, distributed append-only log",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"scripts": {
|
|
@@ -52,6 +52,7 @@
|
|
|
52
52
|
"hypercore-crypto": "^3.2.1",
|
|
53
53
|
"hypercore-errors": "^1.2.0",
|
|
54
54
|
"hypercore-id-encoding": "^1.2.0",
|
|
55
|
+
"hypercore-storage": "^1.0.0",
|
|
55
56
|
"is-options": "^1.0.1",
|
|
56
57
|
"protomux": "^3.5.0",
|
|
57
58
|
"quickbit-universal": "^2.2.0",
|
|
@@ -61,7 +62,6 @@
|
|
|
61
62
|
"sodium-universal": "^4.0.0",
|
|
62
63
|
"streamx": "^2.12.4",
|
|
63
64
|
"unslab": "^1.3.0",
|
|
64
|
-
"xache": "^1.1.0",
|
|
65
65
|
"z32": "^1.0.0"
|
|
66
66
|
},
|
|
67
67
|
"devDependencies": {
|
package/lib/batch.js
DELETED
|
@@ -1,431 +0,0 @@
|
|
|
1
|
-
const { BLOCK_NOT_AVAILABLE, SESSION_CLOSED } = require('hypercore-errors')
|
|
2
|
-
const EventEmitter = require('events')
|
|
3
|
-
const c = require('compact-encoding')
|
|
4
|
-
const b4a = require('b4a')
|
|
5
|
-
const safetyCatch = require('safety-catch')
|
|
6
|
-
|
|
7
|
-
module.exports = class HypercoreBatch extends EventEmitter {
|
|
8
|
-
constructor (session, checkoutLength, autoClose, restore, clear) {
|
|
9
|
-
super()
|
|
10
|
-
|
|
11
|
-
this.session = session
|
|
12
|
-
this.opened = false
|
|
13
|
-
this.closed = false
|
|
14
|
-
this.opening = null
|
|
15
|
-
this.closing = null
|
|
16
|
-
this.writable = true // always writable...
|
|
17
|
-
this.autoClose = autoClose
|
|
18
|
-
this.restore = restore
|
|
19
|
-
this.fork = 0
|
|
20
|
-
|
|
21
|
-
this._appends = []
|
|
22
|
-
this._appendsActual = null
|
|
23
|
-
this._checkoutLength = checkoutLength
|
|
24
|
-
this._byteLength = 0
|
|
25
|
-
this._sessionLength = 0
|
|
26
|
-
this._sessionByteLength = 0
|
|
27
|
-
this._sessionBatch = null
|
|
28
|
-
this._cachedBatch = null
|
|
29
|
-
this._flushing = null
|
|
30
|
-
this._clear = clear
|
|
31
|
-
|
|
32
|
-
this.opening = this._open()
|
|
33
|
-
this.opening.catch(safetyCatch)
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
get id () {
|
|
37
|
-
return this.session.id
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
get key () {
|
|
41
|
-
return this.session.key
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
get discoveryKey () {
|
|
45
|
-
return this.session.discoveryKey
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
get indexedLength () {
|
|
49
|
-
return Math.min(this._sessionLength, this.session.core === null ? 0 : this.session.core.tree.length)
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
get flushedLength () {
|
|
53
|
-
return this._sessionLength
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
get indexedByteLength () {
|
|
57
|
-
return this._sessionByteLength
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
get length () {
|
|
61
|
-
return this._sessionLength + this._appends.length
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
get byteLength () {
|
|
65
|
-
return this._sessionByteLength + this._byteLength
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
get core () {
|
|
69
|
-
return this.session.core
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
get manifest () {
|
|
73
|
-
return this.session.manifest
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
ready () {
|
|
77
|
-
return this.opening
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
async _open () {
|
|
81
|
-
await this.session.ready()
|
|
82
|
-
|
|
83
|
-
if (this._clear) this._checkoutLength = this.core.tree.length
|
|
84
|
-
|
|
85
|
-
if (this._checkoutLength !== -1) {
|
|
86
|
-
const batch = await this.session.core.tree.restoreBatch(this._checkoutLength)
|
|
87
|
-
batch.treeLength = this._checkoutLength
|
|
88
|
-
this._sessionLength = batch.length
|
|
89
|
-
this._sessionByteLength = batch.byteLength
|
|
90
|
-
this._sessionBatch = batch
|
|
91
|
-
if (this._clear) await this.core.clearBatch()
|
|
92
|
-
} else {
|
|
93
|
-
const last = this.restore ? this.session.core.bitfield.findFirst(false, this.session.length) : 0
|
|
94
|
-
|
|
95
|
-
if (last > this.session.length) {
|
|
96
|
-
const batch = await this.session.core.tree.restoreBatch(last)
|
|
97
|
-
this._sessionLength = batch.length
|
|
98
|
-
this._sessionByteLength = batch.byteLength - this.session.padding * batch.length
|
|
99
|
-
this._sessionBatch = batch
|
|
100
|
-
} else {
|
|
101
|
-
this._sessionLength = this.session.length
|
|
102
|
-
this._sessionByteLength = this.session.byteLength
|
|
103
|
-
this._sessionBatch = this.session.createTreeBatch()
|
|
104
|
-
}
|
|
105
|
-
}
|
|
106
|
-
|
|
107
|
-
this._appendsActual = this.session.encryption ? [] : this._appends
|
|
108
|
-
this.fork = this.session.fork
|
|
109
|
-
this.opened = true
|
|
110
|
-
this.emit('ready')
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
async has (index) {
|
|
114
|
-
if (this.opened === false) await this.ready()
|
|
115
|
-
if (index >= this._sessionLength) return index < this.length
|
|
116
|
-
return this.session.has(index)
|
|
117
|
-
}
|
|
118
|
-
|
|
119
|
-
async update (opts) {
|
|
120
|
-
if (this.opened === false) await this.ready()
|
|
121
|
-
await this.session.update(opts)
|
|
122
|
-
}
|
|
123
|
-
|
|
124
|
-
treeHash () {
|
|
125
|
-
return this._sessionBatch.hash()
|
|
126
|
-
}
|
|
127
|
-
|
|
128
|
-
setUserData (key, value, opts) {
|
|
129
|
-
return this.session.setUserData(key, value, opts)
|
|
130
|
-
}
|
|
131
|
-
|
|
132
|
-
getUserData (key, opts) {
|
|
133
|
-
return this.session.getUserData(key, opts)
|
|
134
|
-
}
|
|
135
|
-
|
|
136
|
-
async info (opts) {
|
|
137
|
-
const session = this.session
|
|
138
|
-
const info = await session.info(opts)
|
|
139
|
-
|
|
140
|
-
info.length = this._sessionLength
|
|
141
|
-
|
|
142
|
-
if (info.contiguousLength >= info.length) {
|
|
143
|
-
info.contiguousLength = info.length += this._appends.length
|
|
144
|
-
} else {
|
|
145
|
-
info.length += this._appends.length
|
|
146
|
-
}
|
|
147
|
-
|
|
148
|
-
info.byteLength = this._sessionByteLength + this._byteLength
|
|
149
|
-
|
|
150
|
-
return info
|
|
151
|
-
}
|
|
152
|
-
|
|
153
|
-
async seek (bytes, opts = {}) {
|
|
154
|
-
if (this.opened === false) await this.opening
|
|
155
|
-
if (this.closing) throw SESSION_CLOSED()
|
|
156
|
-
|
|
157
|
-
if (bytes < this._sessionByteLength) return await this.session.seek(bytes, { ...opts, tree: this._sessionBatch })
|
|
158
|
-
|
|
159
|
-
bytes -= this._sessionByteLength
|
|
160
|
-
|
|
161
|
-
let i = 0
|
|
162
|
-
|
|
163
|
-
for (const blk of this._appends) {
|
|
164
|
-
if (bytes < blk.byteLength) return [this._sessionLength + i, bytes]
|
|
165
|
-
i++
|
|
166
|
-
bytes -= blk.byteLength
|
|
167
|
-
}
|
|
168
|
-
|
|
169
|
-
if (bytes === 0) return [this._sessionLength + i, 0]
|
|
170
|
-
|
|
171
|
-
throw BLOCK_NOT_AVAILABLE()
|
|
172
|
-
}
|
|
173
|
-
|
|
174
|
-
async get (index, opts = {}) {
|
|
175
|
-
if (this.opened === false) await this.opening
|
|
176
|
-
if (this.closing) throw SESSION_CLOSED()
|
|
177
|
-
|
|
178
|
-
const length = this._sessionLength
|
|
179
|
-
|
|
180
|
-
if (index < length) {
|
|
181
|
-
return this.session.get(index, { ...opts, tree: this._sessionBatch })
|
|
182
|
-
}
|
|
183
|
-
|
|
184
|
-
if (opts && opts.raw) {
|
|
185
|
-
return this._appendsActual[index - length] || null
|
|
186
|
-
}
|
|
187
|
-
|
|
188
|
-
const buffer = this._appends[index - length] || null
|
|
189
|
-
if (!buffer) throw BLOCK_NOT_AVAILABLE()
|
|
190
|
-
|
|
191
|
-
const encoding = (opts && opts.valueEncoding && c.from(opts.valueEncoding)) || this.session.valueEncoding
|
|
192
|
-
if (!encoding) return buffer
|
|
193
|
-
|
|
194
|
-
return c.decode(encoding, buffer)
|
|
195
|
-
}
|
|
196
|
-
|
|
197
|
-
async _waitForFlush () {
|
|
198
|
-
// wait for any pending flush...
|
|
199
|
-
while (this._flushing) {
|
|
200
|
-
await this._flushing
|
|
201
|
-
await Promise.resolve() // yield in case a new flush is queued
|
|
202
|
-
}
|
|
203
|
-
}
|
|
204
|
-
|
|
205
|
-
async restoreBatch (length, blocks) {
|
|
206
|
-
if (this.opened === false) await this.opening
|
|
207
|
-
if (length >= this._sessionLength) return this.createTreeBatch(length, blocks)
|
|
208
|
-
return this.session.core.tree.restoreBatch(length)
|
|
209
|
-
}
|
|
210
|
-
|
|
211
|
-
_catchupBatch (clone) {
|
|
212
|
-
if (this._cachedBatch === null) this._cachedBatch = this._sessionBatch.clone()
|
|
213
|
-
|
|
214
|
-
if (this.length > this._cachedBatch.length) {
|
|
215
|
-
const offset = this._cachedBatch.length - this._sessionBatch.length
|
|
216
|
-
|
|
217
|
-
for (let i = offset; i < this._appendsActual.length; i++) {
|
|
218
|
-
this._cachedBatch.append(this._appendsActual[i])
|
|
219
|
-
}
|
|
220
|
-
}
|
|
221
|
-
|
|
222
|
-
return clone ? this._cachedBatch.clone() : this._cachedBatch
|
|
223
|
-
}
|
|
224
|
-
|
|
225
|
-
createTreeBatch (length, opts = {}) {
|
|
226
|
-
if (Array.isArray(opts)) opts = { blocks: opts }
|
|
227
|
-
|
|
228
|
-
const { blocks = [], clone = true } = opts
|
|
229
|
-
if (!length && length !== 0) length = this.length + blocks.length
|
|
230
|
-
|
|
231
|
-
const maxLength = this.length + blocks.length
|
|
232
|
-
const b = this._catchupBatch(clone || (blocks.length > 0 || length !== this.length))
|
|
233
|
-
const len = Math.min(length, this.length)
|
|
234
|
-
|
|
235
|
-
if (len < this._sessionLength || length > maxLength) return null
|
|
236
|
-
if (len < b.length) b.checkout(len, this._sessionBatch.roots)
|
|
237
|
-
|
|
238
|
-
for (let i = 0; i < length - len; i++) {
|
|
239
|
-
b.append(this._appendsActual === this._appends ? blocks[i] : this._encrypt(b.length, blocks[i]))
|
|
240
|
-
}
|
|
241
|
-
|
|
242
|
-
return b
|
|
243
|
-
}
|
|
244
|
-
|
|
245
|
-
async truncate (newLength = 0, opts = {}) {
|
|
246
|
-
if (this.opened === false) await this.opening
|
|
247
|
-
if (this.closing) throw SESSION_CLOSED()
|
|
248
|
-
|
|
249
|
-
// wait for any pending flush... (prop needs a lock)
|
|
250
|
-
await this._waitForFlush()
|
|
251
|
-
|
|
252
|
-
if (typeof opts === 'number') opts = { fork: opts }
|
|
253
|
-
const { fork = this.fork + 1, force = false } = opts
|
|
254
|
-
|
|
255
|
-
this._cachedBatch = null
|
|
256
|
-
|
|
257
|
-
const length = this._sessionLength
|
|
258
|
-
if (newLength < length) {
|
|
259
|
-
if (!force) throw new Error('Cannot truncate committed blocks')
|
|
260
|
-
this._appends.length = 0
|
|
261
|
-
this._byteLength = 0
|
|
262
|
-
await this.session.truncate(newLength, { fork, force: true, ...opts })
|
|
263
|
-
this._sessionLength = this.session.length
|
|
264
|
-
this._sessionByteLength = this.session.byteLength
|
|
265
|
-
this._sessionBatch = this.session.createTreeBatch()
|
|
266
|
-
} else {
|
|
267
|
-
for (let i = newLength - length; i < this._appends.length; i++) this._byteLength -= this._appends[i].byteLength
|
|
268
|
-
this._appends.length = newLength - length
|
|
269
|
-
}
|
|
270
|
-
|
|
271
|
-
this.fork = fork
|
|
272
|
-
|
|
273
|
-
this.emit('truncate', newLength, this.fork)
|
|
274
|
-
}
|
|
275
|
-
|
|
276
|
-
async append (blocks) {
|
|
277
|
-
const session = this.session
|
|
278
|
-
|
|
279
|
-
if (this.opened === false) await this.opening
|
|
280
|
-
if (this.closing) throw SESSION_CLOSED()
|
|
281
|
-
|
|
282
|
-
// wait for any pending flush... (prop needs a lock)
|
|
283
|
-
await this._waitForFlush()
|
|
284
|
-
|
|
285
|
-
blocks = Array.isArray(blocks) ? blocks : [blocks]
|
|
286
|
-
|
|
287
|
-
const buffers = session.encodeBatch !== null
|
|
288
|
-
? session.encodeBatch(blocks)
|
|
289
|
-
: new Array(blocks.length)
|
|
290
|
-
|
|
291
|
-
if (session.encodeBatch === null) {
|
|
292
|
-
for (let i = 0; i < blocks.length; i++) {
|
|
293
|
-
const buffer = this._encode(session.valueEncoding, blocks[i])
|
|
294
|
-
buffers[i] = buffer
|
|
295
|
-
this._byteLength += buffer.byteLength
|
|
296
|
-
}
|
|
297
|
-
}
|
|
298
|
-
if (this._appends !== this._appendsActual) {
|
|
299
|
-
for (let i = 0; i < buffers.length; i++) {
|
|
300
|
-
this._appendsActual.push(this._encrypt(this._sessionLength + this._appendsActual.length, buffers[i]))
|
|
301
|
-
}
|
|
302
|
-
}
|
|
303
|
-
|
|
304
|
-
for (const b of buffers) this._appends.push(b)
|
|
305
|
-
|
|
306
|
-
const info = { length: this.length, byteLength: this.byteLength }
|
|
307
|
-
this.emit('append')
|
|
308
|
-
|
|
309
|
-
return info
|
|
310
|
-
}
|
|
311
|
-
|
|
312
|
-
_encode (enc, val) {
|
|
313
|
-
const state = { start: 0, end: 0, buffer: null }
|
|
314
|
-
|
|
315
|
-
if (b4a.isBuffer(val)) {
|
|
316
|
-
if (state.start === 0) return val
|
|
317
|
-
state.end += val.byteLength
|
|
318
|
-
} else if (enc) {
|
|
319
|
-
enc.preencode(state, val)
|
|
320
|
-
} else {
|
|
321
|
-
val = b4a.from(val)
|
|
322
|
-
if (state.start === 0) return val
|
|
323
|
-
state.end += val.byteLength
|
|
324
|
-
}
|
|
325
|
-
|
|
326
|
-
state.buffer = b4a.allocUnsafe(state.end)
|
|
327
|
-
|
|
328
|
-
if (enc) enc.encode(state, val)
|
|
329
|
-
else state.buffer.set(val, state.start)
|
|
330
|
-
|
|
331
|
-
return state.buffer
|
|
332
|
-
}
|
|
333
|
-
|
|
334
|
-
_encrypt (index, buffer) {
|
|
335
|
-
const block = b4a.allocUnsafe(buffer.byteLength + 8)
|
|
336
|
-
block.set(buffer, 8)
|
|
337
|
-
this.session.encryption.encrypt(index, block, this.fork)
|
|
338
|
-
return block
|
|
339
|
-
}
|
|
340
|
-
|
|
341
|
-
async flush (opts = {}) {
|
|
342
|
-
if (this.opened === false) await this.opening
|
|
343
|
-
if (this.closing) throw SESSION_CLOSED()
|
|
344
|
-
|
|
345
|
-
const { length = this.length, keyPair = this.session.keyPair, signature = null, pending = !signature && !keyPair } = opts
|
|
346
|
-
|
|
347
|
-
while (this._flushing) await this._flushing
|
|
348
|
-
this._flushing = this._flush(length, keyPair, signature, pending)
|
|
349
|
-
|
|
350
|
-
let flushed = false
|
|
351
|
-
|
|
352
|
-
try {
|
|
353
|
-
flushed = await this._flushing
|
|
354
|
-
} finally {
|
|
355
|
-
this._flushing = null
|
|
356
|
-
}
|
|
357
|
-
|
|
358
|
-
if (this.autoClose) await this.close()
|
|
359
|
-
|
|
360
|
-
return flushed
|
|
361
|
-
}
|
|
362
|
-
|
|
363
|
-
async _flush (length, keyPair, signature, pending) { // TODO: make this safe to interact with a parallel truncate...
|
|
364
|
-
if (this._sessionBatch.fork !== this.session.fork) return false // no truncs supported atm
|
|
365
|
-
|
|
366
|
-
if (this.session.replicator._upgrade) {
|
|
367
|
-
for (const req of this.session.replicator._upgrade.inflight) {
|
|
368
|
-
// yield to the remote inflight upgrade, TODO: if the remote upgrade fails, retry flushing...
|
|
369
|
-
if (req.upgrade && (req.upgrade.start + req.upgrade.length) > length) {
|
|
370
|
-
return false
|
|
371
|
-
}
|
|
372
|
-
}
|
|
373
|
-
}
|
|
374
|
-
|
|
375
|
-
const flushingLength = Math.min(length - this._sessionLength, this._appends.length)
|
|
376
|
-
if (flushingLength <= 0) {
|
|
377
|
-
if (this._sessionLength > this.core.tree.length && length > this.core.tree.length && !pending) {
|
|
378
|
-
const batch = await this.restoreBatch(length)
|
|
379
|
-
const info = await this.core.insertBatch(batch, [], { keyPair, signature, pending, treeLength: length })
|
|
380
|
-
return info !== null
|
|
381
|
-
}
|
|
382
|
-
return true
|
|
383
|
-
}
|
|
384
|
-
|
|
385
|
-
const batch = this.createTreeBatch(this._sessionLength + flushingLength)
|
|
386
|
-
if (batch === null) return false
|
|
387
|
-
|
|
388
|
-
const info = await this.core.insertBatch(batch, this._appendsActual, { keyPair, signature, pending, treeLength: this._sessionLength })
|
|
389
|
-
if (info === null) return false
|
|
390
|
-
|
|
391
|
-
const delta = info.byteLength - this._sessionByteLength
|
|
392
|
-
const newBatch = info.length !== this.session.length ? await this.session.core.tree.restoreBatch(info.length) : this.session.createTreeBatch()
|
|
393
|
-
|
|
394
|
-
this._sessionLength = info.length
|
|
395
|
-
this._sessionByteLength = info.byteLength
|
|
396
|
-
this._sessionBatch = newBatch
|
|
397
|
-
|
|
398
|
-
if (this._cachedBatch !== null) this._cachedBatch.prune(info.length)
|
|
399
|
-
|
|
400
|
-
const same = this._appends === this._appendsActual
|
|
401
|
-
|
|
402
|
-
this._appends = this._appends.slice(flushingLength)
|
|
403
|
-
this._appendsActual = same ? this._appends : this._appendsActual.slice(flushingLength)
|
|
404
|
-
this._byteLength -= delta
|
|
405
|
-
|
|
406
|
-
this.emit('flush')
|
|
407
|
-
|
|
408
|
-
return true
|
|
409
|
-
}
|
|
410
|
-
|
|
411
|
-
close () {
|
|
412
|
-
if (!this.closing) this.closing = this._close()
|
|
413
|
-
return this.closing
|
|
414
|
-
}
|
|
415
|
-
|
|
416
|
-
async _close () {
|
|
417
|
-
this._clearAppends()
|
|
418
|
-
|
|
419
|
-
await this.session.close()
|
|
420
|
-
|
|
421
|
-
this.closed = true
|
|
422
|
-
this.emit('close')
|
|
423
|
-
}
|
|
424
|
-
|
|
425
|
-
_clearAppends () {
|
|
426
|
-
this._appends = []
|
|
427
|
-
this._appendsActual = []
|
|
428
|
-
this._byteLength = 0
|
|
429
|
-
this.fork = 0
|
|
430
|
-
}
|
|
431
|
-
}
|
package/lib/big-header.js
DELETED
|
@@ -1,55 +0,0 @@
|
|
|
1
|
-
const c = require('compact-encoding')
|
|
2
|
-
const { oplog } = require('./messages')
|
|
3
|
-
|
|
4
|
-
module.exports = class BigHeader {
|
|
5
|
-
constructor (storage) {
|
|
6
|
-
this.storage = storage
|
|
7
|
-
}
|
|
8
|
-
|
|
9
|
-
async load (external) {
|
|
10
|
-
const buf = await new Promise((resolve, reject) => {
|
|
11
|
-
this.storage.read(external.start, external.length, (err, buf) => {
|
|
12
|
-
if (err) return reject(err)
|
|
13
|
-
resolve(buf)
|
|
14
|
-
})
|
|
15
|
-
})
|
|
16
|
-
|
|
17
|
-
const header = c.decode(oplog.header, buf)
|
|
18
|
-
header.external = external
|
|
19
|
-
return header
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
async flush (header) {
|
|
23
|
-
const external = header.external || { start: 0, length: 0 }
|
|
24
|
-
header.external = null
|
|
25
|
-
|
|
26
|
-
const buf = c.encode(oplog.header, header)
|
|
27
|
-
|
|
28
|
-
let start = 0
|
|
29
|
-
if (buf.byteLength > external.start) {
|
|
30
|
-
start = external.start + external.length
|
|
31
|
-
const rem = start & 4095
|
|
32
|
-
if (rem > 0) start += (4096 - rem)
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
header.external = { start, length: buf.byteLength }
|
|
36
|
-
|
|
37
|
-
await new Promise((resolve, reject) => {
|
|
38
|
-
this.storage.write(start, buf, (err) => {
|
|
39
|
-
if (err) return reject(err)
|
|
40
|
-
resolve()
|
|
41
|
-
})
|
|
42
|
-
})
|
|
43
|
-
|
|
44
|
-
return header
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
close () {
|
|
48
|
-
return new Promise((resolve, reject) => {
|
|
49
|
-
this.storage.close((err) => {
|
|
50
|
-
if (err) return reject(err)
|
|
51
|
-
resolve()
|
|
52
|
-
})
|
|
53
|
-
})
|
|
54
|
-
}
|
|
55
|
-
}
|