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.
@@ -0,0 +1,949 @@
1
+ const crypto = require('hypercore-crypto')
2
+ const b4a = require('b4a')
3
+ const assert = require('nanoassert')
4
+ const flat = require('flat-tree')
5
+ const quickbit = require('quickbit-universal')
6
+
7
+ const { STORAGE_CONFLICT, INVALID_OPERATION, INVALID_SIGNATURE } = require('hypercore-errors')
8
+
9
+ const Mutex = require('./mutex')
10
+ const Bitfield = require('./bitfield')
11
+ const { MerkleTree, MerkleTreeBatch } = require('./merkle-tree')
12
+
13
+ module.exports = class SessionState {
14
+ constructor (core, parent, storage, blocks, tree, treeInfo, name) {
15
+ this.core = core
16
+ this.index = this.core.sessionStates.push(this) - 1
17
+
18
+ this.storage = storage
19
+ this.name = name
20
+ this.sessions = []
21
+
22
+ this.parent = parent
23
+ this.mutex = new Mutex()
24
+
25
+ this.blocks = blocks
26
+ this.tree = tree
27
+
28
+ // merkle state
29
+ this.roots = []
30
+ this.length = 0
31
+ this.fork = treeInfo.fork || 0
32
+ this.prologue = treeInfo.prologue || null
33
+ this.signature = treeInfo.signature || null
34
+
35
+ const deps = this.storage.dependencies
36
+ this.dependencyLength = deps.length ? deps[deps.length - 1].length : Infinity
37
+
38
+ if (treeInfo.roots.length) this.setRoots(treeInfo.roots)
39
+
40
+ this.snapshotCompatLength = this.isSnapshot() ? this.length : -1
41
+
42
+ this.active = 0
43
+
44
+ this._onflush = null
45
+ this._flushing = null
46
+ this._activeTx = null
47
+ this._pendingBitfield = null
48
+
49
+ this.ref()
50
+ }
51
+
52
+ isSnapshot () {
53
+ return this.storage.snapshotted
54
+ }
55
+
56
+ isDefault () {
57
+ return this.core.state === this
58
+ }
59
+
60
+ createTreeBatch () {
61
+ return new MerkleTreeBatch(this.tree, this)
62
+ }
63
+
64
+ addSession (s) {
65
+ if (s._stateIndex !== -1) return
66
+ s._stateIndex = this.sessions.push(s) - 1
67
+ if (s.weak === false) this.core.activeSessions++
68
+ }
69
+
70
+ removeSession (s) {
71
+ if (s._stateIndex === -1) return
72
+ const head = this.sessions.pop()
73
+ if (head !== s) this.sessions[(head._stateIndex = s._stateIndex)] = head
74
+ s._stateIndex = -1
75
+ if (s.weak === false) this.core.activeSessions--
76
+ this.core.checkIfIdle()
77
+ }
78
+
79
+ flushedLength () {
80
+ if (this.isDefault() || this.isSnapshot()) return this.length
81
+ return this.dependencyLength
82
+ }
83
+
84
+ unref () {
85
+ if (--this.active > 0) return
86
+ this.close().catch(noop) // technically async, but only for the last db session
87
+ }
88
+
89
+ ref () {
90
+ this.active++
91
+ return this
92
+ }
93
+
94
+ hash () {
95
+ return MerkleTree.hash(this)
96
+ }
97
+
98
+ setRoots (roots) {
99
+ this.roots = roots
100
+ this.length = MerkleTree.span(roots) / 2
101
+ }
102
+
103
+ get byteLength () {
104
+ return MerkleTree.size(this.roots)
105
+ }
106
+
107
+ treeInfo () {
108
+ return {
109
+ fork: this.fork,
110
+ roots: this.roots.slice(),
111
+ length: this.length,
112
+ prologue: this.prologue
113
+ }
114
+ }
115
+
116
+ async close () {
117
+ if (this.index === -1) return
118
+
119
+ this.active = 0
120
+ this.mutex.destroy(new Error('Closed')).catch(noop)
121
+
122
+ const closing = this.storage.close()
123
+
124
+ const head = this.core.sessionStates.pop()
125
+ if (head !== this) this.core.sessionStates[(head.index = this.index)] = head
126
+
127
+ this.index = -1
128
+ this.core.checkIfIdle()
129
+
130
+ return closing
131
+ }
132
+
133
+ snapshot () {
134
+ const s = new SessionState(
135
+ this.core,
136
+ null,
137
+ this.storage.snapshot(),
138
+ this.blocks,
139
+ this.tree.clone(),
140
+ this.treeInfo(),
141
+ this.name
142
+ )
143
+
144
+ return s
145
+ }
146
+
147
+ updateDependency (storage, length) {
148
+ const dependency = updateDependency(this, length)
149
+ if (dependency) {
150
+ this.dependencyLength = dependency.length
151
+ storage.setDependency(dependency)
152
+ }
153
+
154
+ return dependency
155
+ }
156
+
157
+ _clearActiveBatch (err) {
158
+ if (!this._activeTx) return
159
+ this._activeTx = null
160
+
161
+ if (this._onflush) this._onflush(err)
162
+
163
+ this._onflush = null
164
+ this._flushing = null
165
+
166
+ this._activeTx = null
167
+ }
168
+
169
+ createWriteBatch () {
170
+ assert(!this._activeTx && !this.storage.snapshotted)
171
+
172
+ this._activeTx = this.storage.write()
173
+ return this._activeTx
174
+ }
175
+
176
+ _unlock (lock) {
177
+ this._clearActiveBatch()
178
+ this.mutex.unlock()
179
+ this.core.checkIfIdle()
180
+ }
181
+
182
+ async flush () {
183
+ const tx = this._activeTx
184
+ this._activeTx = null
185
+
186
+ const flushing = tx.flush()
187
+
188
+ try {
189
+ if (!this._flushing) this._flushing = flushing
190
+
191
+ return flushing
192
+ } finally {
193
+ this._clearActiveBatch()
194
+ }
195
+ }
196
+
197
+ _commit () {
198
+ const bitfield = this._pendingBitfield
199
+ this._pendingBitfield = null
200
+
201
+ return this.parent._oncommit(this, bitfield)
202
+ }
203
+
204
+ async _oncommit (src, bitfield) {
205
+ this.fork = src.fork
206
+ this.length = src.length
207
+ this.roots = src.roots.slice()
208
+ this.signature = src.signature
209
+
210
+ const tree = {
211
+ fork: this.fork,
212
+ length: this.length,
213
+ rootHash: this.hash(),
214
+ signature: this.signature
215
+ }
216
+
217
+ // handle migration
218
+ if (src.core !== this.core) {
219
+ this.prologue = src.prologue
220
+ this.storage = await src.core.state.storage.resumeSession(this.name)
221
+ this.tree = new MerkleTree(this.storage)
222
+
223
+ for (let i = this.core.sessionStates.length - 1; i >= 0; i--) {
224
+ const state = this.core.sessionStates[i]
225
+ if (state === this) continue
226
+ if (state.name === this.name) state._moveToCore(src.core)
227
+ }
228
+
229
+ this._moveToCore(src.core)
230
+ }
231
+
232
+ if (!bitfield || !bitfield.drop) {
233
+ this.onappend(tree, bitfield, true)
234
+ } else {
235
+ this.ontruncate(tree, bitfield.start, bitfield.start + bitfield.length, true)
236
+ }
237
+ }
238
+
239
+ flushed () {
240
+ if (!this._activeTx) return
241
+
242
+ if (this._flushing) return this._flushing
243
+
244
+ this._flushing = new Promise(resolve => {
245
+ this._onflush = resolve
246
+ })
247
+
248
+ return this._flushing
249
+ }
250
+
251
+ async setUserData (key, value) {
252
+ await this.mutex.lock()
253
+
254
+ try {
255
+ const tx = this.createWriteBatch()
256
+ tx.putUserData(key, value)
257
+
258
+ return await this.flush()
259
+ } finally {
260
+ this._unlock()
261
+ }
262
+ }
263
+
264
+ async _verifyBlock (batch, bitfield, value, manifest, from) {
265
+ await this.mutex.lock()
266
+
267
+ try {
268
+ const tx = this.createWriteBatch()
269
+ this.updating = true
270
+
271
+ if (bitfield) this.blocks.put(tx, bitfield.start, value)
272
+
273
+ if (bitfield && this.isDefault()) {
274
+ await storeBitfieldRange(this.storage, tx, bitfield.start, bitfield.start + 1, true)
275
+ }
276
+
277
+ if (manifest) this.core._setManifest(tx, manifest, null)
278
+
279
+ if (batch.commitable()) {
280
+ batch.commit(tx)
281
+ }
282
+
283
+ const head = {
284
+ fork: batch.fork,
285
+ length: batch.length,
286
+ rootHash: batch.hash(),
287
+ signature: batch.signature
288
+ }
289
+
290
+ if (batch.upgraded) tx.setHead(head)
291
+
292
+ const flushed = await this.flush()
293
+
294
+ if (batch.upgraded) {
295
+ this.roots = batch.roots
296
+ this.length = batch.length
297
+ this.fork = batch.fork
298
+ this.signature = batch.signature
299
+
300
+ this.onappend(head, bitfield, flushed)
301
+ }
302
+ } finally {
303
+ this._clearActiveBatch()
304
+ this.updating = false
305
+ this.mutex.unlock()
306
+ }
307
+ }
308
+
309
+ async truncate (length, fork, { signature, keyPair } = {}) {
310
+ if (this.prologue && length < this.prologue.length) {
311
+ throw INVALID_OPERATION('Truncation breaks prologue')
312
+ }
313
+
314
+ if (!keyPair && this.isDefault()) keyPair = this.core.header.keyPair
315
+
316
+ await this.mutex.lock()
317
+
318
+ try {
319
+ const batch = this.createTreeBatch()
320
+ await this.tree.truncate(length, batch, fork)
321
+
322
+ if (!signature && keyPair && length > 0) signature = this.core.verifier.sign(batch, keyPair)
323
+ if (signature) batch.signature = signature
324
+
325
+ const tx = this.createWriteBatch()
326
+
327
+ // upsert compat manifest
328
+ if (this.core.verifier === null && keyPair) this.core._setManifest(tx, null, keyPair)
329
+
330
+ const { dependency, tree, roots } = await this._truncate(tx, batch)
331
+
332
+ const flushed = await this.flush()
333
+
334
+ this.fork = tree.fork
335
+ this.length = tree.length
336
+ this.roots = roots
337
+ this.signature = tree.signature
338
+
339
+ if (dependency) this.storage.updateDependencyLength(this.dependencyLength)
340
+
341
+ this.ontruncate(tree, tree.length, batch.treeLength, flushed)
342
+ } finally {
343
+ this._unlock()
344
+ }
345
+ }
346
+
347
+ async reorg (batch) {
348
+ await this.mutex.lock()
349
+
350
+ const storage = this.createWriteBatch()
351
+
352
+ try {
353
+ if (!batch.commitable()) return false
354
+
355
+ const { dependency, tree } = await this._truncate(storage, batch)
356
+
357
+ const flushed = await this.flush()
358
+
359
+ this.fork = batch.fork
360
+ this.length = batch.length
361
+ this.roots = batch.roots
362
+ this.signature = batch.signature
363
+
364
+ if (dependency) this.storage.updateDependencyLength(this.dependencyLength)
365
+
366
+ this.ontruncate(tree, batch.ancestors, batch.treeLength, flushed)
367
+ } finally {
368
+ this._unlock()
369
+ }
370
+ }
371
+
372
+ async _truncate (storage, batch) {
373
+ storage.deleteBlockRange(batch.ancestors, batch.treeLength)
374
+
375
+ if (batch.commitable()) batch.commit(storage)
376
+
377
+ const tree = {
378
+ fork: batch.fork,
379
+ length: batch.length,
380
+ rootHash: batch.hash(),
381
+ signature: batch.signature
382
+ }
383
+
384
+ if (tree) storage.setHead(tree)
385
+
386
+ const truncated = batch.length < this.flushedLength()
387
+ const dependency = truncated ? updateDependency(this, batch.length) : null
388
+
389
+ if (dependency) this.dependencyLength = dependency.length
390
+
391
+ if (this.isDefault()) {
392
+ await storeBitfieldRange(this.storage, storage, batch.ancestors, batch.treeLength, false)
393
+ }
394
+
395
+ return { dependency, tree, roots: batch.roots }
396
+ }
397
+
398
+ async clear (start, end, cleared) {
399
+ await this.mutex.lock()
400
+
401
+ try {
402
+ const tx = this.createWriteBatch()
403
+
404
+ if (this.isDefault()) await storeBitfieldRange(this.storage, tx, start, end, false)
405
+
406
+ this.blocks.clear(tx, start, end)
407
+
408
+ const dependency = start < this.flushedLength() ? updateDependency(this, start) : null
409
+
410
+ if (dependency) this.dependencyLength = dependency.length
411
+
412
+ const flushed = await this.flush()
413
+
414
+ if (dependency) this.storage.updateDependencyLength(this.dependencyLength)
415
+
416
+ // todo: atomic event handle
417
+ if (this.isDefault() && flushed) {
418
+ const length = end - start
419
+ this.core.updateContiguousLength({ start, length, drop: true })
420
+ this.core._setBitfieldRanges(start, end, false)
421
+ this.core.replicator.onhave(start, length, true)
422
+ }
423
+ } finally {
424
+ this._unlock()
425
+ }
426
+ }
427
+
428
+ async append (values, { signature, keyPair, preappend } = {}) {
429
+ if (!keyPair && this.isDefault()) keyPair = this.core.header.keyPair
430
+
431
+ await this.mutex.lock()
432
+
433
+ try {
434
+ const tx = this.createWriteBatch()
435
+
436
+ // upsert compat manifest
437
+ if (this.core.verifier === null && keyPair) this.core._setManifest(tx, null, keyPair)
438
+
439
+ if (preappend) await preappend(values)
440
+
441
+ if (!values.length) {
442
+ await this.flush()
443
+ return { length: this.length, byteLength: this.byteLength }
444
+ }
445
+
446
+ const batch = this.createTreeBatch()
447
+ for (const val of values) batch.append(val)
448
+
449
+ // only multisig can have prologue so signature is always present
450
+ if (this.prologue && batch.length < this.prologue.length) {
451
+ throw INVALID_OPERATION('Append is not consistent with prologue')
452
+ }
453
+
454
+ if (!signature && keyPair) signature = this.core.verifier.sign(batch, keyPair)
455
+ if (signature) batch.signature = signature
456
+
457
+ batch.commit(tx)
458
+
459
+ const tree = {
460
+ fork: batch.fork,
461
+ length: batch.length,
462
+ rootHash: batch.hash(),
463
+ signature: batch.signature
464
+ }
465
+
466
+ tx.setHead(tree)
467
+
468
+ if (this.isDefault()) await storeBitfieldRange(this.storage, tx, batch.ancestors, batch.length, true)
469
+
470
+ this.blocks.putBatch(tx, this.length, values)
471
+
472
+ const bitfield = {
473
+ drop: false,
474
+ start: batch.ancestors,
475
+ length: values.length
476
+ }
477
+
478
+ const flushed = await this.flush()
479
+
480
+ this.fork = batch.fork
481
+ this.roots = batch.roots
482
+ this.length = batch.length
483
+ this.signature = batch.signature
484
+
485
+ this.onappend(tree, bitfield, flushed)
486
+
487
+ return { length: this.length, byteLength: this.byteLength }
488
+ } finally {
489
+ this._unlock()
490
+ }
491
+ }
492
+
493
+ onappend (tree, bitfield, flushed) {
494
+ if (!flushed) this._updateBitfield(bitfield)
495
+ else if (this.isDefault()) this.core.onappend(tree, bitfield)
496
+
497
+ for (let i = this.sessions.length - 1; i >= 0; i--) {
498
+ this.sessions[i].emit('append')
499
+ }
500
+ }
501
+
502
+ ontruncate (tree, to, from, flushed) {
503
+ const bitfield = { start: to, length: from - to, drop: true }
504
+
505
+ if (!flushed) this._updateBitfield(bitfield)
506
+ else if (this.isDefault()) this.core.ontruncate(tree, bitfield)
507
+
508
+ for (let i = this.sessions.length - 1; i >= 0; i--) {
509
+ this.sessions[i].emit('truncate', to, tree.fork)
510
+ }
511
+ }
512
+
513
+ _updateBitfield (bitfield, flushed) {
514
+ const p = this._pendingBitfield
515
+
516
+ if (p === null) {
517
+ if (bitfield) this._pendingBitfield = bitfield
518
+ return
519
+ }
520
+
521
+ if (p.drop) {
522
+ const from = bitfield.start + bitfield.length
523
+ if (!bitfield.drop || p.start !== from) throw INVALID_OPERATION('Atomic truncations must be contiguous')
524
+
525
+ p.length += bitfield.length
526
+ p.start = bitfield.start
527
+ } else {
528
+ p.length = bitfield.start + bitfield.length - p.start
529
+ }
530
+ }
531
+
532
+ async _overwrite (source, fork, length, treeLength, signature, isDependent, shallow) {
533
+ const blockPromises = []
534
+ const treePromises = []
535
+ const rootPromises = []
536
+
537
+ const rx = source.storage.read()
538
+
539
+ for (const root of flat.fullRoots(length * 2)) {
540
+ rootPromises.push(rx.getTreeNode(root))
541
+ }
542
+
543
+ if (shallow !== true) {
544
+ for (const index of flat.patch(treeLength * 2, length * 2)) {
545
+ treePromises.push(rx.getTreeNode(index))
546
+ }
547
+
548
+ for (let i = treeLength; i < length; i++) {
549
+ treePromises.push(rx.getTreeNode(i * 2))
550
+ treePromises.push(rx.getTreeNode(i * 2 + 1))
551
+ blockPromises.push(rx.getBlock(i))
552
+ }
553
+ }
554
+
555
+ rx.tryFlush()
556
+
557
+ const blocks = await Promise.all(blockPromises)
558
+ const nodes = await Promise.all(treePromises)
559
+ const roots = await Promise.all(rootPromises)
560
+
561
+ if (this.core.destroyed) throw new Error('Core destroyed')
562
+
563
+ if (signature) {
564
+ const batch = this.createTreeBatch()
565
+ batch.roots = roots
566
+ batch.length = length
567
+
568
+ if (!this.core.verifier.verify(batch, signature)) {
569
+ throw INVALID_SIGNATURE('Signature is not valid over committed tree')
570
+ }
571
+ }
572
+
573
+ const tx = this.createWriteBatch()
574
+
575
+ // truncate existing tree
576
+ if (treeLength < this.length) {
577
+ tx.deleteBlockRange(treeLength, this.length)
578
+ }
579
+
580
+ for (const node of nodes) {
581
+ if (node !== null) tx.putTreeNode(node)
582
+ }
583
+
584
+ for (let i = 0; i < blocks.length; i++) {
585
+ tx.putBlock(i + treeLength, blocks[i])
586
+ }
587
+
588
+ const totalLength = Math.max(length, this.length)
589
+
590
+ const firstPage = getBitfieldPage(treeLength)
591
+ const lastPage = getBitfieldPage(totalLength)
592
+
593
+ let index = treeLength
594
+
595
+ for (let i = firstPage; i <= lastPage; i++) {
596
+ const page = b4a.alloc(Bitfield.BYTES_PER_PAGE)
597
+ tx.putBitfieldPage(i, page)
598
+
599
+ if (index < length) {
600
+ index = fillBitfieldPage(page, index, length, i, true)
601
+ if (index < length) continue
602
+ }
603
+
604
+ if (index < this.length) {
605
+ index = fillBitfieldPage(page, index, this.length, i, false)
606
+ }
607
+ }
608
+
609
+ const tree = {
610
+ fork,
611
+ length,
612
+ rootHash: crypto.tree(roots),
613
+ signature
614
+ }
615
+
616
+ const upgraded = treeLength < this.length || this.length < length || tree.fork !== this.tree.fork
617
+
618
+ if (upgraded) tx.setHead(tree)
619
+
620
+ const dependency = isDependent ? updateDependency(this, length) : null
621
+
622
+ if (dependency) this.dependencyLength = dependency.length
623
+
624
+ const flushed = await this.flush()
625
+
626
+ this.fork = tree.fork
627
+ this.roots = roots
628
+ this.length = length
629
+ this.signature = signature
630
+
631
+ if (dependency) this.storage.updateDependencyLength(this.dependencyLength)
632
+
633
+ return { tree, flushed }
634
+ }
635
+
636
+ async overwrite (state, { length = state.tree.length, treeLength = state.flushedLength(), shallow = false } = {}) {
637
+ assert(!this.isDefault(), 'Cannot overwrite signed state') // TODO: make this check better
638
+
639
+ await this.mutex.lock()
640
+
641
+ try {
642
+ const origLength = this.length
643
+ const fork = treeLength < origLength ? this.fork + 1 : this.fork
644
+
645
+ const { tree, flushed } = await this._overwrite(state, fork, length, treeLength, null, state === this.core.state, shallow)
646
+
647
+ const bitfield = { start: treeLength, length: tree.length - treeLength, drop: false }
648
+
649
+ if (treeLength < origLength) this.ontruncate(tree, treeLength, origLength, flushed)
650
+ if (treeLength < tree.length) this.onappend(tree, bitfield, flushed)
651
+
652
+ return {
653
+ length: this.length,
654
+ byteLength: this.byteLength
655
+ }
656
+ } finally {
657
+ this._clearActiveBatch()
658
+ this.updating = false
659
+ this.mutex.unlock()
660
+ }
661
+ }
662
+
663
+ async commit (state, { signature, keyPair, length = state.length, treeLength = state.flushedLength(), overwrite = false } = {}) {
664
+ assert(this.isDefault() || (this.parent && this.parent.isDefault()), 'Can only commit into default state')
665
+
666
+ let srcLocked = false
667
+ await this.mutex.lock()
668
+
669
+ try {
670
+ await state.mutex.lock()
671
+ srcLocked = true
672
+
673
+ await this.core._validateCommit(state, treeLength)
674
+
675
+ if (this.length < length && !signature) {
676
+ if (!keyPair) keyPair = this.core.header.keyPair
677
+ const batch = state.createTreeBatch()
678
+ if (length !== batch.length) await batch.restore(length)
679
+ signature = this.core.verifier.sign(batch, keyPair)
680
+ }
681
+
682
+ const { tree, flushed } = await this._overwrite(state, this.fork, length, treeLength, signature, false, false)
683
+
684
+ // gc blocks from source
685
+ if (treeLength < length) {
686
+ const tx = state.createWriteBatch()
687
+
688
+ state.blocks.clear(tx, treeLength, length)
689
+ const dependency = state.updateDependency(tx, length)
690
+
691
+ await state.flush(tx)
692
+
693
+ if (dependency) {
694
+ state.storage.updateDependencyLength(dependency.length)
695
+ state.dependencyLength = dependency.length
696
+ }
697
+ }
698
+
699
+ const bitfield = { start: treeLength, length: length - treeLength, drop: false }
700
+ this.onappend(tree, bitfield, flushed)
701
+
702
+ return {
703
+ length: this.length,
704
+ byteLength: this.byteLength
705
+ }
706
+ } finally {
707
+ this.updating = false
708
+ this.mutex.unlock()
709
+
710
+ if (srcLocked) {
711
+ state.mutex.unlock()
712
+ state._clearActiveBatch()
713
+ }
714
+
715
+ this.core.checkIfIdle()
716
+ }
717
+ }
718
+
719
+ async _getTreeHeadAt (length) {
720
+ if (length === null) return this.treeInfo()
721
+
722
+ const head = getDefaultTree()
723
+
724
+ head.length = length
725
+
726
+ const roots = await this.tree.getRoots(length)
727
+ const rootHash = crypto.tree(roots)
728
+
729
+ head.fork = this.fork
730
+ head.rootHash = rootHash
731
+
732
+ return head
733
+ }
734
+
735
+ _moveToCore (core) {
736
+ const head = this.core.sessionStates.pop()
737
+ if (head !== this) this.core.sessionStates[(head.index = this.index)] = head
738
+
739
+ this.core = core
740
+ this.index = this.core.sessionStates.push(this) - 1
741
+
742
+ for (let i = this.sessions.length - 1; i >= 0; i--) this.sessions[i].transferSession(this.core)
743
+ }
744
+
745
+ async moveTo (core, length) {
746
+ const state = core.state
747
+
748
+ await this.mutex.lock()
749
+
750
+ try {
751
+ if (state.storage && (await state.storage.resumeSession(this.name)) !== null) {
752
+ throw STORAGE_CONFLICT('Batch has already been created')
753
+ }
754
+
755
+ const treeLength = this.length
756
+
757
+ if (!this.isSnapshot()) {
758
+ const truncation = length < this.length ? await truncateAndFlush(this, length) : null
759
+
760
+ const treeInfo = truncation ? truncation.tree : await state._getTreeHeadAt(this.length)
761
+
762
+ treeInfo.fork = truncation ? this.fork + 1 : this.fork
763
+
764
+ // todo: validate treeInfo
765
+
766
+ if (!this.storage.atom) {
767
+ this.storage = await state.storage.createSession(this.name, treeInfo)
768
+ } else {
769
+ const s = state.storage.atomize(this.storage.atom)
770
+ this.storage = await s.createSession(this.name, treeInfo)
771
+ await s.close()
772
+ }
773
+
774
+ this.tree = new MerkleTree(this.storage)
775
+
776
+ this.prologue = state.prologue
777
+ this.fork = treeInfo.fork
778
+ this.length = length
779
+ this.roots = await this.tree.getRoots(length)
780
+
781
+ if (truncation) {
782
+ const { dependency, tree, flushed } = truncation
783
+
784
+ if (dependency) this.storage.updateDependencyLength(this.dependencyLength)
785
+ this.ontruncate(tree, tree.length, treeLength, flushed)
786
+ }
787
+ }
788
+
789
+ if (!this.storage.atom) {
790
+ for (let i = this.core.sessionStates.length - 1; i >= 0; i--) {
791
+ const state = this.core.sessionStates[i]
792
+ if (state === this) continue
793
+ if (state.name === this.name) state._moveToCore(core)
794
+ }
795
+ }
796
+
797
+ this._moveToCore(core)
798
+ } finally {
799
+ this.mutex.unlock()
800
+ }
801
+ }
802
+
803
+ async createSession (name, length, overwrite, atom) {
804
+ let storage = null
805
+ let treeInfo = null
806
+
807
+ if (!atom && !overwrite && this.storage) {
808
+ storage = await this.storage.resumeSession(name)
809
+
810
+ if (storage !== null) {
811
+ treeInfo = (await getCoreHead(storage)) || getDefaultTree()
812
+ if (length !== -1 && treeInfo.length !== length) throw STORAGE_CONFLICT('Different batch stored here')
813
+ }
814
+ }
815
+
816
+ if (length === -1) length = treeInfo ? treeInfo.length : this.length
817
+
818
+ if (storage === null) {
819
+ treeInfo = await this._getTreeHeadAt(length)
820
+
821
+ if (atom) {
822
+ storage = await this.storage.createAtomicSession(atom, treeInfo)
823
+ } else {
824
+ storage = await this.storage.createSession(name, treeInfo)
825
+ }
826
+ }
827
+
828
+ const tree = new MerkleTree(storage)
829
+
830
+ const head = {
831
+ fork: this.fork,
832
+ roots: length === this.length ? this.roots.slice() : await tree.getRoots(length),
833
+ length,
834
+ prologue: this.prologue
835
+ }
836
+
837
+ const state = new SessionState(
838
+ this.core,
839
+ atom ? this : null,
840
+ storage,
841
+ this.core.blocks,
842
+ tree,
843
+ head,
844
+ atom ? this.name : name
845
+ )
846
+
847
+ if (atom) atom.onflush(state._commit.bind(state))
848
+
849
+ return state
850
+ }
851
+ }
852
+
853
+ function noop () {}
854
+
855
+ function getBitfieldPage (index) {
856
+ return Math.floor(index / Bitfield.BITS_PER_PAGE)
857
+ }
858
+
859
+ function getBitfieldOffset (index) {
860
+ return index & (Bitfield.BITS_PER_PAGE - 1)
861
+ }
862
+
863
+ function fillBitfieldPage (page, start, end, pageIndex, value) {
864
+ const last = ((pageIndex + 1) * Bitfield.BITS_PER_PAGE) - 1
865
+ const from = getBitfieldOffset(start)
866
+
867
+ const index = last < end ? last : end
868
+ const to = getBitfieldOffset(index)
869
+
870
+ quickbit.fill(page, value, from, to)
871
+
872
+ return index
873
+ }
874
+
875
+ async function storeBitfieldRange (storage, tx, from, to, value) {
876
+ const firstPage = getBitfieldPage(from)
877
+ const lastPage = getBitfieldPage(to)
878
+
879
+ let index = from
880
+
881
+ const rx = storage.read()
882
+
883
+ const promises = []
884
+ for (let i = firstPage; i <= lastPage; i++) {
885
+ promises.push(rx.getBitfieldPage(i))
886
+ }
887
+
888
+ rx.tryFlush()
889
+ const pages = await Promise.all(promises)
890
+
891
+ for (let i = 0; i <= lastPage - firstPage; i++) {
892
+ const pageIndex = i + firstPage
893
+ if (!pages[i]) pages[i] = b4a.alloc(Bitfield.BYTES_PER_PAGE)
894
+
895
+ index = fillBitfieldPage(pages[i], index, to, pageIndex, true)
896
+ tx.putBitfieldPage(pageIndex, pages[i])
897
+ }
898
+ }
899
+
900
+ async function truncateAndFlush (s, length) {
901
+ const batch = s.createTreeBatch()
902
+ await s.tree.truncate(length, batch, s.tree.fork)
903
+ const tx = s.createWriteBatch()
904
+
905
+ const info = await s._truncate(tx, batch)
906
+ const flushed = await s.flush()
907
+
908
+ return {
909
+ tree: info.tree,
910
+ roots: info.roots,
911
+ dependency: info.dependency,
912
+ flushed
913
+ }
914
+ }
915
+
916
+ function updateDependency (state, length) {
917
+ const dependency = findDependency(state.storage, length)
918
+ if (dependency === null) return null // skip default state and overlays of default
919
+
920
+ return {
921
+ dataPointer: dependency.dataPointer,
922
+ length
923
+ }
924
+ }
925
+
926
+ function findDependency (storage, length) {
927
+ for (let i = storage.dependencies.length - 1; i >= 0; i--) {
928
+ const dep = storage.dependencies[i]
929
+ if (dep.length < length) return dep
930
+ }
931
+
932
+ return null
933
+ }
934
+
935
+ function getDefaultTree () {
936
+ return {
937
+ fork: 0,
938
+ length: 0,
939
+ rootHash: null,
940
+ signature: null
941
+ }
942
+ }
943
+
944
+ function getCoreHead (storage) {
945
+ const b = storage.read()
946
+ const p = b.getHead()
947
+ b.tryFlush()
948
+ return p
949
+ }