hypercore-storage 0.0.41 → 1.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/LICENSE +201 -0
- package/README.md +17 -0
- package/index.js +467 -783
- package/lib/block-dependency-stream.js +107 -0
- package/lib/keys.js +242 -0
- package/lib/streams.js +112 -0
- package/lib/tx.js +286 -0
- package/lib/view.js +326 -0
- package/migrations/0/index.js +628 -0
- package/migrations/0/messages.js +1069 -0
- package/package.json +33 -18
- package/spec/hyperschema/index.js +510 -0
- package/lib/dependency-stream.js +0 -111
- package/lib/memory-overlay.js +0 -438
- package/lib/messages.js +0 -190
- package/lib/tip-list.js +0 -93
package/index.js
CHANGED
|
@@ -1,1013 +1,697 @@
|
|
|
1
1
|
const RocksDB = require('rocksdb-native')
|
|
2
|
-
const c = require('compact-encoding')
|
|
3
|
-
const { UINT } = require('index-encoder')
|
|
4
|
-
const RW = require('read-write-mutexify')
|
|
5
|
-
const b4a = require('b4a')
|
|
6
|
-
const flat = require('flat-tree')
|
|
7
2
|
const rrp = require('resolve-reject-promise')
|
|
8
|
-
const
|
|
9
|
-
const
|
|
10
|
-
|
|
11
|
-
const
|
|
12
|
-
const
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
// <data><CORE_TREE><index> = { index, size, hash }
|
|
32
|
-
// <data><CORE_BITFIELD><index> = <4kb buffer>
|
|
33
|
-
// <data><CORE_BLOCKS><index> = <buffer>
|
|
34
|
-
// <data><CORE_USER_DATA><key> = <value>
|
|
35
|
-
|
|
36
|
-
// top level prefixes
|
|
37
|
-
const TL = {
|
|
38
|
-
STORAGE_INFO: 0,
|
|
39
|
-
LOCAL_SEED: 1,
|
|
40
|
-
DKEYS: 2,
|
|
41
|
-
CORE: 3,
|
|
42
|
-
DATA: 4,
|
|
43
|
-
DEFAULT_KEY: 5
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
// core prefixes
|
|
47
|
-
const CORE = {
|
|
48
|
-
MANIFEST: 0,
|
|
49
|
-
LOCAL_SEED: 1,
|
|
50
|
-
ENCRYPTION_KEY: 2,
|
|
51
|
-
HEAD: 3,
|
|
52
|
-
BATCHES: 4
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
// data prefixes
|
|
56
|
-
const DATA = {
|
|
57
|
-
INFO: 0,
|
|
58
|
-
UPDATES: 1,
|
|
59
|
-
DEPENDENCY: 2,
|
|
60
|
-
HINTS: 3,
|
|
61
|
-
TREE: 4,
|
|
62
|
-
BITFIELD: 5,
|
|
63
|
-
BLOCK: 6,
|
|
64
|
-
USER_DATA: 7
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
const SLAB = {
|
|
68
|
-
start: 0,
|
|
69
|
-
end: 65536,
|
|
70
|
-
buffer: b4a.allocUnsafe(65536)
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
// PREFIX + BATCH + TYPE + INDEX
|
|
74
|
-
|
|
75
|
-
class WriteBatch {
|
|
76
|
-
constructor (storage, write, atom) {
|
|
77
|
-
this.storage = storage
|
|
78
|
-
this.write = write
|
|
79
|
-
this.atom = atom
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
setCoreHead (head) {
|
|
83
|
-
this.write.tryPut(encodeCoreIndex(this.storage.corePointer, CORE.HEAD, this.storage.dataPointer), c.encode(m.CoreHead, head))
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
setCoreAuth ({ key, manifest }) {
|
|
87
|
-
this.write.tryPut(encodeCoreIndex(this.storage.corePointer, CORE.MANIFEST), c.encode(m.CoreAuth, { key, manifest }))
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
setBatchPointer (name, pointer) {
|
|
91
|
-
this.write.tryPut(encodeBatch(this.storage.corePointer, CORE.BATCHES, name), encode(m.DataPointer, pointer))
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
setDataDependency ({ data, length }) {
|
|
95
|
-
this.write.tryPut(encodeDataIndex(this.storage.dataPointer, DATA.DEPENDENCY), encode(m.DataDependency, { data, length }))
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
setLocalKeyPair (keyPair) {
|
|
99
|
-
this.write.tryPut(encodeCoreIndex(this.storage.corePointer, CORE.LOCAL_SEED), encode(m.KeyPair, keyPair))
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
setEncryptionKey (encryptionKey) {
|
|
103
|
-
this.write.tryPut(encodeCoreIndex(this.storage.corePointer, CORE.ENCRYPTION_KEY), encryptionKey)
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
setDataInfo (info) {
|
|
107
|
-
if (info.version !== 0) throw new Error('Version > 0 is not supported')
|
|
108
|
-
this.write.tryPut(encodeDataIndex(this.storage.dataPointer, DATA.INFO), encode(m.DataInfo, info))
|
|
109
|
-
}
|
|
110
|
-
|
|
111
|
-
setUserData (key, value) {
|
|
112
|
-
this.write.tryPut(encodeUserDataIndex(this.storage.dataPointer, DATA.USER_DATA, key), value)
|
|
113
|
-
}
|
|
114
|
-
|
|
115
|
-
putBlock (index, data) {
|
|
116
|
-
this.write.tryPut(encodeDataIndex(this.storage.dataPointer, DATA.BLOCK, index), data)
|
|
117
|
-
}
|
|
118
|
-
|
|
119
|
-
deleteBlock (index) {
|
|
120
|
-
this.write.tryDelete(encodeDataIndex(this.storage.dataPointer, DATA.BLOCK, index))
|
|
121
|
-
}
|
|
122
|
-
|
|
123
|
-
deleteBlockRange (start, end) {
|
|
124
|
-
return this._deleteRange(DATA.BLOCK, start, end)
|
|
125
|
-
}
|
|
3
|
+
const ScopeLock = require('scope-lock')
|
|
4
|
+
const View = require('./lib/view.js')
|
|
5
|
+
|
|
6
|
+
const VERSION = 1
|
|
7
|
+
const COLUMN_FAMILY = 'corestore'
|
|
8
|
+
|
|
9
|
+
const {
|
|
10
|
+
CorestoreRX,
|
|
11
|
+
CorestoreTX,
|
|
12
|
+
CoreTX,
|
|
13
|
+
CoreRX
|
|
14
|
+
} = require('./lib/tx.js')
|
|
15
|
+
|
|
16
|
+
const {
|
|
17
|
+
createCoreStream,
|
|
18
|
+
createAliasStream,
|
|
19
|
+
createBlockStream,
|
|
20
|
+
createBitfieldStream,
|
|
21
|
+
createUserDataStream,
|
|
22
|
+
createTreeNodeStream
|
|
23
|
+
} = require('./lib/streams.js')
|
|
24
|
+
|
|
25
|
+
const EMPTY = new View()
|
|
126
26
|
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
this.write.tryDelete(encodeDataIndex(this.storage.dataPointer, DATA.TREE, index))
|
|
133
|
-
}
|
|
134
|
-
|
|
135
|
-
deleteTreeNodeRange (start, end) {
|
|
136
|
-
return this._deleteRange(DATA.TREE, start, end)
|
|
137
|
-
}
|
|
138
|
-
|
|
139
|
-
putBitfieldPage (index, page) {
|
|
140
|
-
this.write.tryPut(encodeDataIndex(this.storage.dataPointer, DATA.BITFIELD, index), page)
|
|
141
|
-
}
|
|
142
|
-
|
|
143
|
-
deleteBitfieldPage (index) {
|
|
144
|
-
this.write.tryDelete(encodeDataIndex(this.storage.dataPointer, DATA.BITFIELD, index))
|
|
27
|
+
class Atom {
|
|
28
|
+
constructor (db) {
|
|
29
|
+
this.db = db
|
|
30
|
+
this.view = new View()
|
|
31
|
+
this.flushes = []
|
|
145
32
|
}
|
|
146
33
|
|
|
147
|
-
|
|
148
|
-
|
|
34
|
+
onflush (fn) {
|
|
35
|
+
this.flushes.push(fn)
|
|
149
36
|
}
|
|
150
37
|
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
return this.write.tryDeleteRange(s, e)
|
|
156
|
-
}
|
|
38
|
+
async flush () {
|
|
39
|
+
await View.flush(this.view.changes, this.db)
|
|
40
|
+
this.view.reset()
|
|
157
41
|
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
else this.write.destroy()
|
|
161
|
-
}
|
|
42
|
+
const promises = []
|
|
43
|
+
while (this.flushes.length) promises.push(this.flushes.pop()())
|
|
162
44
|
|
|
163
|
-
|
|
164
|
-
if (this.atom) return this.atom.flush()
|
|
165
|
-
return flushAndDestroy(this.write)
|
|
45
|
+
await Promise.all(promises)
|
|
166
46
|
}
|
|
167
47
|
}
|
|
168
48
|
|
|
169
|
-
class
|
|
170
|
-
constructor (
|
|
171
|
-
this.
|
|
172
|
-
this.
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
return this._get(encodeCoreIndex(this.storage.corePointer, CORE.HEAD, this.storage.dataPointer), m.CoreHead)
|
|
177
|
-
}
|
|
178
|
-
|
|
179
|
-
async getCoreAuth () {
|
|
180
|
-
return this._get(encodeCoreIndex(this.storage.corePointer, CORE.MANIFEST), m.CoreAuth)
|
|
181
|
-
}
|
|
182
|
-
|
|
183
|
-
async getLocalKeyPair () {
|
|
184
|
-
return this._get(encodeCoreIndex(this.storage.corePointer, CORE.LOCAL_SEED), m.KeyPair)
|
|
185
|
-
}
|
|
186
|
-
|
|
187
|
-
async getEncryptionKey () {
|
|
188
|
-
return this._get(encodeCoreIndex(this.storage.corePointer, CORE.ENCRYPTION_KEY), null)
|
|
189
|
-
}
|
|
190
|
-
|
|
191
|
-
getDataDependency () {
|
|
192
|
-
return this._get(encodeDataIndex(this.storage.dataPointer, DATA.DEPENDENCY), m.DataDependency)
|
|
193
|
-
}
|
|
49
|
+
class HypercoreStorage {
|
|
50
|
+
constructor (store, db, core, view, atomic) {
|
|
51
|
+
this.store = store
|
|
52
|
+
this.db = db
|
|
53
|
+
this.core = core
|
|
54
|
+
this.view = view
|
|
55
|
+
this.atomic = atomic
|
|
194
56
|
|
|
195
|
-
|
|
196
|
-
return this._get(encodeDataIndex(this.storage.dataPointer, DATA.INFO), m.DataInfo)
|
|
57
|
+
this.view.readStart()
|
|
197
58
|
}
|
|
198
59
|
|
|
199
|
-
|
|
200
|
-
return this.
|
|
60
|
+
get dependencies () {
|
|
61
|
+
return this.core.dependencies
|
|
201
62
|
}
|
|
202
63
|
|
|
203
|
-
|
|
204
|
-
return this.
|
|
64
|
+
getDependencyLength () {
|
|
65
|
+
return this.core.dependencies.length
|
|
66
|
+
? this.core.dependencies[this.core.dependencies.length - 1].length
|
|
67
|
+
: -1
|
|
205
68
|
}
|
|
206
69
|
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
const key = encodeDataIndex(dataPointer, DATA.BLOCK, index)
|
|
212
|
-
const block = await this._get(key, null)
|
|
213
|
-
|
|
214
|
-
if (block === null && error === true) {
|
|
215
|
-
throw new Error('Node not found: ' + index)
|
|
70
|
+
getDependency (length) {
|
|
71
|
+
for (let i = this.core.dependencies.length - 1; i >= 0; i--) {
|
|
72
|
+
const dep = this.core.dependencies[i]
|
|
73
|
+
if (dep.length < length) return dep
|
|
216
74
|
}
|
|
217
75
|
|
|
218
|
-
return
|
|
219
|
-
}
|
|
220
|
-
|
|
221
|
-
async hasTreeNode (index) {
|
|
222
|
-
return this._has(encodeDataIndex(this.storage.dataPointer, DATA.TREE, index))
|
|
76
|
+
return null
|
|
223
77
|
}
|
|
224
78
|
|
|
225
|
-
async
|
|
226
|
-
|
|
227
|
-
const
|
|
228
|
-
|
|
229
|
-
const key = encodeDataIndex(dataPointer, DATA.TREE, index)
|
|
230
|
-
const node = await this._get(key, m.TreeNode)
|
|
79
|
+
// TODO: this might have to be async if the dependents have changed, but prop ok for now
|
|
80
|
+
updateDependencyLength (length) {
|
|
81
|
+
const deps = this.core.dependencies
|
|
231
82
|
|
|
232
|
-
|
|
233
|
-
|
|
83
|
+
for (let i = deps.length - 1; i >= 0; i--) {
|
|
84
|
+
if (deps[i].length >= length) continue
|
|
85
|
+
deps[i].length = length
|
|
86
|
+
this.core.dependencies = deps.slice(0, i + 1)
|
|
87
|
+
return
|
|
234
88
|
}
|
|
235
89
|
|
|
236
|
-
|
|
90
|
+
throw new Error('Dependency not found')
|
|
237
91
|
}
|
|
238
92
|
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
return this._get(key, null)
|
|
93
|
+
get snapshotted () {
|
|
94
|
+
return this.db._snapshot !== null
|
|
242
95
|
}
|
|
243
96
|
|
|
244
|
-
|
|
245
|
-
return (
|
|
97
|
+
snapshot () {
|
|
98
|
+
return new HypercoreStorage(this.store, this.db.snapshot(), this.core, this.view.snapshot(), this.atomic)
|
|
246
99
|
}
|
|
247
100
|
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
if (buffer === null) return null
|
|
251
|
-
|
|
252
|
-
if (enc) return c.decode(enc, buffer)
|
|
253
|
-
|
|
254
|
-
return buffer
|
|
101
|
+
atomize (atom) {
|
|
102
|
+
return new HypercoreStorage(this.store, this.db.session(), this.core, atom.view, true)
|
|
255
103
|
}
|
|
256
104
|
|
|
257
|
-
|
|
258
|
-
this.
|
|
105
|
+
atom () {
|
|
106
|
+
return this.store.atom()
|
|
259
107
|
}
|
|
260
108
|
|
|
261
|
-
|
|
262
|
-
return
|
|
109
|
+
createBlockStream (opts) {
|
|
110
|
+
return createBlockStream(this.core, this.db, this.view, opts)
|
|
263
111
|
}
|
|
264
112
|
|
|
265
|
-
|
|
266
|
-
|
|
113
|
+
createTreeNodeStream (opts) {
|
|
114
|
+
return createTreeNodeStream(this.core, this.db, this.view, opts)
|
|
267
115
|
}
|
|
268
|
-
}
|
|
269
116
|
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
this.mutex = mutex
|
|
273
|
-
this.refs = 0
|
|
274
|
-
this.promise = null
|
|
117
|
+
createBitfieldStream (opts) {
|
|
118
|
+
return createBitfieldStream(this.core, this.db, this.view, opts)
|
|
275
119
|
}
|
|
276
120
|
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
return this
|
|
121
|
+
createUserDataStream (opts) {
|
|
122
|
+
return createUserDataStream(this.core, this.db, this.view, opts)
|
|
280
123
|
}
|
|
281
124
|
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
}
|
|
125
|
+
async resumeSession (name) {
|
|
126
|
+
const rx = this.read()
|
|
127
|
+
const existingSessionsPromise = rx.getSessions()
|
|
286
128
|
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
this.db = db
|
|
290
|
-
this.batch = null
|
|
291
|
-
this.refs = 0
|
|
292
|
-
this.destroyed = false
|
|
293
|
-
this.flushing = null
|
|
294
|
-
this.resolve = null
|
|
295
|
-
this.reject = null
|
|
296
|
-
|
|
297
|
-
this._executing = null
|
|
298
|
-
this._waiting = []
|
|
299
|
-
this._queue = []
|
|
300
|
-
this._enqueue = (lock, resolve, reject) => this._queue.push({ lock, resolve, reject })
|
|
301
|
-
}
|
|
129
|
+
rx.tryFlush()
|
|
130
|
+
const existingSessions = await existingSessionsPromise
|
|
302
131
|
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
}
|
|
132
|
+
const sessions = existingSessions || []
|
|
133
|
+
const session = getBatch(sessions, name, false)
|
|
306
134
|
|
|
307
|
-
|
|
308
|
-
if (--this.refs === 0) this._commit()
|
|
309
|
-
}
|
|
135
|
+
if (session === null) return null
|
|
310
136
|
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
_lock (mutex) {
|
|
317
|
-
for (const lock of this._waiting) {
|
|
318
|
-
if (lock.mutex === mutex) return lock.acquire()
|
|
137
|
+
const core = {
|
|
138
|
+
version: this.core.version,
|
|
139
|
+
corePointer: this.core.corePointer,
|
|
140
|
+
dataPointer: session.dataPointer,
|
|
141
|
+
dependencies: []
|
|
319
142
|
}
|
|
320
143
|
|
|
321
|
-
const
|
|
322
|
-
this._waiting.push(lock)
|
|
144
|
+
const coreRx = new CoreRX(core, this.db, this.view)
|
|
323
145
|
|
|
324
|
-
|
|
146
|
+
const dependencyPromise = coreRx.getDependency()
|
|
147
|
+
coreRx.tryFlush()
|
|
325
148
|
|
|
326
|
-
|
|
327
|
-
|
|
149
|
+
const dependency = await dependencyPromise
|
|
150
|
+
if (dependency) core.dependencies = this._addDependency(dependency)
|
|
328
151
|
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
for (const { promise } of this._waiting) await promise
|
|
152
|
+
return new HypercoreStorage(this.store, this.db.session(), core, this.atomic ? this.view : new View(), this.atomic)
|
|
153
|
+
}
|
|
332
154
|
|
|
333
|
-
|
|
155
|
+
async createSession (name, head, atom) {
|
|
156
|
+
const rx = this.read(atom)
|
|
334
157
|
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
this._executing = null
|
|
158
|
+
const existingSessionsPromise = rx.getSessions()
|
|
159
|
+
const existingHeadPromise = rx.getHead()
|
|
338
160
|
|
|
339
|
-
|
|
340
|
-
if (this.destroyed) reject(new Error('Atomizer destroyed'))
|
|
341
|
-
else resolve(lock)
|
|
342
|
-
}
|
|
161
|
+
rx.tryFlush()
|
|
343
162
|
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
}
|
|
347
|
-
|
|
348
|
-
createBatch () {
|
|
349
|
-
if (this.refs === 0) this._ensureTick()
|
|
350
|
-
this.enter()
|
|
351
|
-
if (this.batch === null) this.batch = this.db.write()
|
|
352
|
-
return this.batch
|
|
353
|
-
}
|
|
163
|
+
const [existingSessions, existingHead] = await Promise.all([existingSessionsPromise, existingHeadPromise])
|
|
164
|
+
if (head === null) head = existingHead
|
|
354
165
|
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
}
|
|
166
|
+
if (existingHead !== null && head.length > existingHead.length) {
|
|
167
|
+
throw new Error('Invalid head passed, ahead of core')
|
|
168
|
+
}
|
|
359
169
|
|
|
360
|
-
|
|
361
|
-
|
|
170
|
+
const sessions = existingSessions || []
|
|
171
|
+
const session = getBatch(sessions, name, true)
|
|
362
172
|
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
173
|
+
if (session.dataPointer === -1) {
|
|
174
|
+
session.dataPointer = await this.store._allocData()
|
|
175
|
+
}
|
|
366
176
|
|
|
367
|
-
|
|
368
|
-
this.flushing = null
|
|
369
|
-
this.resolve = this.reject = null
|
|
177
|
+
const tx = this.write(atom)
|
|
370
178
|
|
|
371
|
-
|
|
372
|
-
this.destroyed = false
|
|
373
|
-
batch.destroy()
|
|
374
|
-
if (reject !== null) reject(new Error('Atomic batch destroyed'))
|
|
375
|
-
return
|
|
376
|
-
}
|
|
179
|
+
tx.setSessions(sessions)
|
|
377
180
|
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
181
|
+
const length = head === null ? 0 : head.length
|
|
182
|
+
const core = {
|
|
183
|
+
version: this.core.version,
|
|
184
|
+
corePointer: this.core.corePointer,
|
|
185
|
+
dataPointer: session.dataPointer,
|
|
186
|
+
dependencies: this._addDependency({ dataPointer: this.core.dataPointer, length })
|
|
384
187
|
}
|
|
385
188
|
|
|
386
|
-
|
|
387
|
-
resolve()
|
|
388
|
-
}
|
|
389
|
-
|
|
390
|
-
_createFlushing () {
|
|
391
|
-
if (this.flushing !== null) return this.flushing
|
|
189
|
+
const coreTx = new CoreTX(core, this.db, tx.view, tx.changes)
|
|
392
190
|
|
|
393
|
-
|
|
191
|
+
if (length > 0) coreTx.setHead(head)
|
|
192
|
+
coreTx.setDependency(core.dependencies[core.dependencies.length - 1])
|
|
394
193
|
|
|
395
|
-
|
|
396
|
-
this.resolve = resolve
|
|
397
|
-
this.reject = reject
|
|
194
|
+
await tx.flush()
|
|
398
195
|
|
|
399
|
-
return this.
|
|
196
|
+
return new HypercoreStorage(this.store, this.db.session(), core, atom ? atom.view : this.atomic ? this.view : new View(), !!atom || this.atomic)
|
|
400
197
|
}
|
|
401
198
|
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
199
|
+
async createAtomicSession (atom, head) {
|
|
200
|
+
const length = head === null ? 0 : head.length
|
|
201
|
+
const core = {
|
|
202
|
+
version: this.core.version,
|
|
203
|
+
corePointer: this.core.corePointer,
|
|
204
|
+
dataPointer: this.core.dataPointer,
|
|
205
|
+
dependencies: this._addDependency({ dataPointer: this.core.dataPointer, length })
|
|
206
|
+
}
|
|
406
207
|
|
|
407
|
-
|
|
408
|
-
const flushing = this._createFlushing()
|
|
409
|
-
this.exit()
|
|
410
|
-
return flushing
|
|
411
|
-
}
|
|
412
|
-
}
|
|
208
|
+
const coreTx = new CoreTX(core, this.db, atom.view, [])
|
|
413
209
|
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
this.db = typeof dir === 'object' ? dir : new RocksDB(dir)
|
|
417
|
-
this.mutex = new RW()
|
|
418
|
-
}
|
|
210
|
+
if (length > 0) coreTx.setHead(head)
|
|
211
|
+
coreTx.setDependency(core.dependencies[core.dependencies.length - 1])
|
|
419
212
|
|
|
420
|
-
|
|
421
|
-
return !!s && typeof s.setLocalSeed === 'function'
|
|
422
|
-
}
|
|
213
|
+
await coreTx.flush()
|
|
423
214
|
|
|
424
|
-
|
|
425
|
-
static async clear (dir) {
|
|
426
|
-
const s = new this(dir)
|
|
427
|
-
await s.clear()
|
|
428
|
-
return s
|
|
215
|
+
return this.atomize(atom)
|
|
429
216
|
}
|
|
430
217
|
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
const existing = await getLocalSeed(this.db)
|
|
434
|
-
if (existing) return b4a.equals(existing, seed)
|
|
435
|
-
}
|
|
218
|
+
_addDependency (dep) {
|
|
219
|
+
const deps = []
|
|
436
220
|
|
|
437
|
-
|
|
221
|
+
for (let i = 0; i < this.core.dependencies.length; i++) {
|
|
222
|
+
const d = this.core.dependencies[i]
|
|
438
223
|
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
224
|
+
if (d.length > dep.length) {
|
|
225
|
+
deps.push({ dataPointer: d.dataPointer, length: dep.length })
|
|
226
|
+
return deps
|
|
227
|
+
}
|
|
443
228
|
|
|
444
|
-
|
|
445
|
-
} finally {
|
|
446
|
-
this.mutex.write.unlock()
|
|
229
|
+
deps.push(d)
|
|
447
230
|
}
|
|
448
|
-
}
|
|
449
231
|
|
|
450
|
-
|
|
451
|
-
return
|
|
232
|
+
deps.push(dep)
|
|
233
|
+
return deps
|
|
452
234
|
}
|
|
453
235
|
|
|
454
|
-
|
|
455
|
-
return
|
|
236
|
+
read (atom) {
|
|
237
|
+
return new CoreRX(this.core, this.db, atom ? atom.view : this.view)
|
|
456
238
|
}
|
|
457
239
|
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
gt: b4a.from([TL.DKEYS]),
|
|
461
|
-
lt: b4a.from([TL.DKEYS + 1])
|
|
462
|
-
})
|
|
463
|
-
|
|
464
|
-
s._readableState.map = mapOnlyDiscoveryKey
|
|
465
|
-
return s
|
|
240
|
+
write (atom) {
|
|
241
|
+
return new CoreTX(this.core, this.db, atom ? atom.view : this.atomic ? this.view : null, [])
|
|
466
242
|
}
|
|
467
243
|
|
|
468
|
-
|
|
469
|
-
if (this.
|
|
244
|
+
close () {
|
|
245
|
+
if (this.view !== null) {
|
|
246
|
+
this.view.readStop()
|
|
247
|
+
this.view = null
|
|
248
|
+
}
|
|
470
249
|
|
|
471
|
-
|
|
472
|
-
await new Promise(setImmediate)
|
|
473
|
-
await this.db.idle()
|
|
474
|
-
await new Promise(setImmediate)
|
|
475
|
-
} while (!this.isIdle())
|
|
250
|
+
return this.db.close()
|
|
476
251
|
}
|
|
252
|
+
}
|
|
477
253
|
|
|
478
|
-
|
|
479
|
-
|
|
254
|
+
class CorestoreStorage {
|
|
255
|
+
constructor (db) {
|
|
256
|
+
this.path = typeof db === 'string' ? db : db.path
|
|
257
|
+
this.rocks = typeof db === 'string' ? new RocksDB(db) : db
|
|
258
|
+
this.db = createColumnFamily(this.rocks)
|
|
259
|
+
this.view = null
|
|
260
|
+
this.enters = 0
|
|
261
|
+
this.lock = new ScopeLock()
|
|
262
|
+
this.flushing = null
|
|
263
|
+
this.version = 0
|
|
264
|
+
this.migrating = null
|
|
480
265
|
}
|
|
481
266
|
|
|
482
|
-
|
|
483
|
-
return this.db.
|
|
267
|
+
get opened () {
|
|
268
|
+
return this.db.opened
|
|
484
269
|
}
|
|
485
270
|
|
|
486
|
-
|
|
487
|
-
return this.db.
|
|
271
|
+
get closed () {
|
|
272
|
+
return this.db.closed
|
|
488
273
|
}
|
|
489
274
|
|
|
490
|
-
|
|
491
|
-
|
|
275
|
+
async ready () {
|
|
276
|
+
if (this.version === 0) await this._migrateStore()
|
|
277
|
+
return this.db.ready()
|
|
492
278
|
}
|
|
493
279
|
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
b.tryDeleteRange(b4a.from([TL.STORAGE_INFO]), INF)
|
|
497
|
-
await flushAndDestroy(b)
|
|
280
|
+
static isCoreStorage (db) {
|
|
281
|
+
return isCorestoreStorage(db)
|
|
498
282
|
}
|
|
499
283
|
|
|
500
|
-
|
|
501
|
-
|
|
284
|
+
static from (db) {
|
|
285
|
+
if (isCorestoreStorage(db)) return db
|
|
286
|
+
return new this(db)
|
|
502
287
|
}
|
|
503
288
|
|
|
504
|
-
async
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
289
|
+
async _flush () {
|
|
290
|
+
while (this.enters > 0) {
|
|
291
|
+
await this.lock.lock()
|
|
292
|
+
await this.lock.unlock()
|
|
508
293
|
}
|
|
509
|
-
|
|
510
|
-
const val = await this.db.get(encodeDiscoveryKey(discoveryKey))
|
|
511
|
-
if (val === null) return null
|
|
512
|
-
|
|
513
|
-
const { core, data } = c.decode(m.CorePointer, val)
|
|
514
|
-
|
|
515
|
-
return new HypercoreStorage(this, discoveryKey, core, data, null)
|
|
516
294
|
}
|
|
517
295
|
|
|
518
|
-
|
|
519
|
-
|
|
296
|
+
// runs pre any other mutation and read
|
|
297
|
+
async _migrateStore () {
|
|
298
|
+
const view = await this._enter()
|
|
520
299
|
|
|
521
300
|
try {
|
|
522
|
-
|
|
301
|
+
if (this.version === VERSION) return
|
|
523
302
|
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
return existing
|
|
527
|
-
}
|
|
303
|
+
const rx = new CorestoreRX(this.db, view)
|
|
304
|
+
const headPromise = rx.getHead()
|
|
528
305
|
|
|
529
|
-
|
|
306
|
+
rx.tryFlush()
|
|
307
|
+
const head = await headPromise
|
|
530
308
|
|
|
531
|
-
|
|
309
|
+
const version = head === null ? 0 : head.version
|
|
310
|
+
if (version === VERSION) return
|
|
532
311
|
|
|
533
|
-
const
|
|
312
|
+
const target = { version: VERSION, dryRun: false }
|
|
534
313
|
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
314
|
+
switch (version) {
|
|
315
|
+
case 0: {
|
|
316
|
+
await require('./migrations/0').store(this, target)
|
|
317
|
+
break
|
|
318
|
+
}
|
|
319
|
+
default: {
|
|
320
|
+
throw new Error('Unsupported version: ' + version + ' - you should probably upgrade your dependencies')
|
|
321
|
+
}
|
|
538
322
|
}
|
|
539
323
|
|
|
540
|
-
|
|
541
|
-
const data = info.free++
|
|
542
|
-
|
|
543
|
-
write.tryPut(encodeDiscoveryKey(discoveryKey), encode(m.CorePointer, { core, data }))
|
|
544
|
-
write.tryPut(b4a.from([TL.STORAGE_INFO]), encode(m.StorageInfo, info))
|
|
545
|
-
|
|
546
|
-
const storage = new HypercoreStorage(this, discoveryKey, core, data, null)
|
|
547
|
-
const batch = new WriteBatch(storage, write, null)
|
|
548
|
-
|
|
549
|
-
initialiseCoreInfo(batch, { key, manifest, keyPair, encryptionKey })
|
|
550
|
-
initialiseCoreData(batch, { userData })
|
|
551
|
-
|
|
552
|
-
await batch.flush()
|
|
553
|
-
return storage
|
|
324
|
+
this.version = VERSION
|
|
554
325
|
} finally {
|
|
555
|
-
this.
|
|
326
|
+
await this._exit()
|
|
556
327
|
}
|
|
557
328
|
}
|
|
558
|
-
}
|
|
559
|
-
|
|
560
|
-
class HypercoreStorage {
|
|
561
|
-
constructor (root, discoveryKey, core, data, snapshot) {
|
|
562
|
-
this.root = root
|
|
563
|
-
this.db = root.db
|
|
564
|
-
this.dbSnapshot = snapshot
|
|
565
|
-
this.dbRead = snapshot || this.db
|
|
566
|
-
this.mutex = root.mutex
|
|
567
|
-
|
|
568
|
-
this.discoveryKey = discoveryKey
|
|
569
|
-
|
|
570
|
-
this.dependencies = []
|
|
571
|
-
|
|
572
|
-
// pointers
|
|
573
|
-
this.corePointer = core
|
|
574
|
-
this.dataPointer = data
|
|
575
|
-
|
|
576
|
-
this.destroyed = false
|
|
577
|
-
}
|
|
578
|
-
|
|
579
|
-
get snapshotted () {
|
|
580
|
-
return this.dbSnapshot !== null
|
|
581
|
-
}
|
|
582
329
|
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
330
|
+
// runs pre the core is returned to the user
|
|
331
|
+
async _migrateCore (core, discoveryKey, locked) {
|
|
332
|
+
const view = locked ? this.view : await this._enter()
|
|
586
333
|
|
|
587
|
-
|
|
588
|
-
return this.dependencies.length
|
|
589
|
-
? this.dependencies[this.dependencies.length - 1].length
|
|
590
|
-
: -1
|
|
591
|
-
}
|
|
592
|
-
|
|
593
|
-
async openBatch (name) {
|
|
594
|
-
const existing = await this.db.get(encodeBatch(this.corePointer, CORE.BATCHES, name))
|
|
595
|
-
if (!existing) return null
|
|
596
|
-
|
|
597
|
-
const storage = new HypercoreStorage(this.root, this.discoveryKey, this.corePointer, this.dataPointer, this.dbSnapshot)
|
|
598
|
-
const dataPointer = c.decode(m.DataPointer, existing)
|
|
599
|
-
|
|
600
|
-
storage.dataPointer = dataPointer
|
|
601
|
-
storage.dependencies = await addDependencies(this.db, storage.dataPointer, -1)
|
|
602
|
-
|
|
603
|
-
return storage
|
|
604
|
-
}
|
|
605
|
-
|
|
606
|
-
async registerBatch (name, head, atom) {
|
|
607
|
-
await this.mutex.write.lock()
|
|
608
|
-
|
|
609
|
-
const storage = new HypercoreStorage(this.root, this.discoveryKey, this.corePointer, this.dataPointer, null)
|
|
334
|
+
const version = core.core.version
|
|
610
335
|
|
|
611
336
|
try {
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
337
|
+
if (version === VERSION) return
|
|
338
|
+
|
|
339
|
+
const target = { version: VERSION, dryRun: false }
|
|
340
|
+
|
|
341
|
+
switch (version) {
|
|
342
|
+
case 0: {
|
|
343
|
+
await require('./migrations/0').core(core, target)
|
|
344
|
+
break
|
|
345
|
+
}
|
|
346
|
+
default: {
|
|
347
|
+
throw new Error('Unsupported version: ' + version + ' - you should probably upgrade your dependencies')
|
|
348
|
+
}
|
|
349
|
+
}
|
|
616
350
|
|
|
617
|
-
|
|
351
|
+
core.core.version = VERSION
|
|
618
352
|
|
|
619
|
-
|
|
353
|
+
if (locked === false) return
|
|
620
354
|
|
|
621
|
-
|
|
355
|
+
// if its locked, then move the core state into the memview
|
|
356
|
+
// in case the core is reopened from the memview, pre flush
|
|
622
357
|
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
if (head.rootHash) batch.setCoreHead(head) // if no root hash its the empty core - no head yet
|
|
358
|
+
const rx = new CorestoreRX(this.db, EMPTY)
|
|
359
|
+
const tx = new CorestoreTX(view)
|
|
626
360
|
|
|
627
|
-
|
|
628
|
-
|
|
361
|
+
const corePromise = rx.getCore(discoveryKey)
|
|
362
|
+
rx.tryFlush()
|
|
629
363
|
|
|
630
|
-
|
|
631
|
-
|
|
364
|
+
tx.putCore(discoveryKey, await corePromise)
|
|
365
|
+
tx.apply()
|
|
632
366
|
} finally {
|
|
633
|
-
this.
|
|
367
|
+
if (!locked) await this._exit()
|
|
634
368
|
}
|
|
635
369
|
}
|
|
636
370
|
|
|
637
|
-
async
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
if (head.rootHash) batch.setCoreHead(head) // if no root hash its the empty core - no head yet
|
|
643
|
-
|
|
644
|
-
await batch.flush()
|
|
645
|
-
|
|
646
|
-
return storage
|
|
371
|
+
async _enter () {
|
|
372
|
+
this.enters++
|
|
373
|
+
await this.lock.lock()
|
|
374
|
+
if (this.view === null) this.view = new View()
|
|
375
|
+
return this.view
|
|
647
376
|
}
|
|
648
377
|
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
}
|
|
378
|
+
async _exit () {
|
|
379
|
+
this.enters--
|
|
652
380
|
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
const s = new HypercoreStorage(this.root, this.discoveryKey, this.corePointer, this.dataPointer, this.dbRead.snapshot())
|
|
381
|
+
if (this.flushing === null) this.flushing = rrp()
|
|
382
|
+
const flushed = this.flushing.promise
|
|
656
383
|
|
|
657
|
-
|
|
384
|
+
if (this.enters === 0 || this.view.size() > 128) {
|
|
385
|
+
try {
|
|
386
|
+
await View.flush(this.view.changes, this.db)
|
|
387
|
+
this.flushing.resolve()
|
|
388
|
+
} catch (err) {
|
|
389
|
+
this.flushing.reject(err)
|
|
390
|
+
} finally {
|
|
391
|
+
this.flushing = null
|
|
392
|
+
this.view = null
|
|
393
|
+
}
|
|
394
|
+
}
|
|
658
395
|
|
|
659
|
-
|
|
396
|
+
this.lock.unlock()
|
|
397
|
+
return flushed
|
|
660
398
|
}
|
|
661
399
|
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
}
|
|
400
|
+
// when used with core catches this isnt transactional for simplicity, HOWEVER, its just a number
|
|
401
|
+
// so worth the tradeoff
|
|
402
|
+
async _allocData () {
|
|
403
|
+
let dataPointer = 0
|
|
667
404
|
|
|
668
|
-
|
|
669
|
-
|
|
405
|
+
const view = await this._enter()
|
|
406
|
+
const tx = new CorestoreTX(view)
|
|
670
407
|
|
|
671
|
-
|
|
672
|
-
|
|
408
|
+
try {
|
|
409
|
+
const head = await this._getHead(view)
|
|
673
410
|
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
411
|
+
dataPointer = head.allocated.datas++
|
|
412
|
+
|
|
413
|
+
tx.setHead(head)
|
|
414
|
+
tx.apply()
|
|
415
|
+
} finally {
|
|
416
|
+
await this._exit()
|
|
680
417
|
}
|
|
681
418
|
|
|
682
|
-
|
|
419
|
+
return dataPointer
|
|
683
420
|
}
|
|
684
421
|
|
|
685
|
-
|
|
686
|
-
|
|
422
|
+
// exposes here so migrations can easily access the head in an init state
|
|
423
|
+
async _getHead (view) {
|
|
424
|
+
const rx = new CorestoreRX(this.db, view)
|
|
425
|
+
const headPromise = rx.getHead()
|
|
426
|
+
rx.tryFlush()
|
|
687
427
|
|
|
688
|
-
|
|
428
|
+
const head = await headPromise
|
|
429
|
+
return head === null ? initStoreHead() : head
|
|
689
430
|
}
|
|
690
431
|
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
if (atom) return new WriteBatch(this, atom.createBatch(), atom)
|
|
695
|
-
|
|
696
|
-
return new WriteBatch(this, this.db.write(), null)
|
|
432
|
+
atom () {
|
|
433
|
+
return new Atom(this.db)
|
|
697
434
|
}
|
|
698
435
|
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
436
|
+
async close () {
|
|
437
|
+
if (this.db.closed) return
|
|
438
|
+
await this._flush()
|
|
439
|
+
await this.db.close()
|
|
440
|
+
await this.rocks.close()
|
|
441
|
+
}
|
|
703
442
|
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
} catch (err) {
|
|
707
|
-
if (atomizer) atomizer.exit()
|
|
708
|
-
throw err
|
|
709
|
-
}
|
|
443
|
+
async clear () {
|
|
444
|
+
if (this.version === 0) await this._migrateStore()
|
|
710
445
|
|
|
711
|
-
const
|
|
712
|
-
|
|
446
|
+
const view = await this._enter()
|
|
447
|
+
const tx = new CorestoreTX(view)
|
|
713
448
|
|
|
714
|
-
|
|
715
|
-
|
|
449
|
+
tx.clear()
|
|
450
|
+
tx.apply()
|
|
716
451
|
|
|
717
|
-
|
|
718
|
-
assert(this.destroyed === false)
|
|
719
|
-
return createStream(this, createBlockStream, opts)
|
|
452
|
+
await this._exit()
|
|
720
453
|
}
|
|
721
454
|
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
const r = encodeUserDataRange(this.dataPointer, DATA.USER_DATA, opts)
|
|
726
|
-
const s = this.dbRead.iterator(r)
|
|
727
|
-
s._readableState.map = mapStreamUserData
|
|
728
|
-
return s
|
|
455
|
+
createCoreStream () {
|
|
456
|
+
// TODO: be nice to run the mgiration here also, but too much plumbing atm
|
|
457
|
+
return createCoreStream(this.db, EMPTY)
|
|
729
458
|
}
|
|
730
459
|
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
const r = encodeIndexRange(this.dataPointer, DATA.TREE, opts)
|
|
735
|
-
const s = this.dbRead.iterator(r)
|
|
736
|
-
s._readableState.map = mapStreamTreeNode
|
|
737
|
-
return s
|
|
460
|
+
createAliasStream (namespace) {
|
|
461
|
+
// TODO: be nice to run the mgiration here also, but too much plumbing atm
|
|
462
|
+
return createAliasStream(this.db, EMPTY, namespace)
|
|
738
463
|
}
|
|
739
464
|
|
|
740
|
-
|
|
741
|
-
|
|
465
|
+
async getAlias (alias) {
|
|
466
|
+
if (this.version === 0) await this._migrateStore()
|
|
742
467
|
|
|
743
|
-
const
|
|
744
|
-
const
|
|
745
|
-
|
|
746
|
-
return
|
|
468
|
+
const rx = new CorestoreRX(this.db, EMPTY)
|
|
469
|
+
const discoveryKeyPromise = rx.getCoreByAlias(alias)
|
|
470
|
+
rx.tryFlush()
|
|
471
|
+
return discoveryKeyPromise
|
|
747
472
|
}
|
|
748
473
|
|
|
749
|
-
async
|
|
750
|
-
|
|
474
|
+
async getSeed () {
|
|
475
|
+
if (this.version === 0) await this._migrateStore()
|
|
751
476
|
|
|
752
|
-
const
|
|
753
|
-
|
|
754
|
-
return c.decode(m.TreeNode, last.value)
|
|
755
|
-
}
|
|
477
|
+
const rx = new CorestoreRX(this.db, EMPTY)
|
|
478
|
+
const headPromise = rx.getHead()
|
|
756
479
|
|
|
757
|
-
|
|
758
|
-
assert(this.destroyed === false)
|
|
480
|
+
rx.tryFlush()
|
|
759
481
|
|
|
760
|
-
const
|
|
761
|
-
|
|
762
|
-
return mapStreamBitfieldPage(last)
|
|
482
|
+
const head = await headPromise
|
|
483
|
+
return head === null ? null : head.seed
|
|
763
484
|
}
|
|
764
485
|
|
|
765
|
-
|
|
766
|
-
if (this.
|
|
767
|
-
this.destroyed = true
|
|
486
|
+
async setSeed (seed, { overwrite = true } = {}) {
|
|
487
|
+
if (this.version === 0) await this._migrateStore()
|
|
768
488
|
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
}
|
|
772
|
-
}
|
|
489
|
+
const view = await this._enter()
|
|
490
|
+
const tx = new CorestoreTX(view)
|
|
773
491
|
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
: new DependencyStream(storage, createStreamType, opts)
|
|
778
|
-
}
|
|
492
|
+
try {
|
|
493
|
+
const rx = new CorestoreRX(this.db, view)
|
|
494
|
+
const headPromise = rx.getHead()
|
|
779
495
|
|
|
780
|
-
|
|
781
|
-
const r = encodeIndexRange(data, DATA.BLOCK, opts)
|
|
782
|
-
const s = db.iterator(r)
|
|
783
|
-
s._readableState.map = mapStreamBlock
|
|
784
|
-
return s
|
|
785
|
-
}
|
|
496
|
+
rx.tryFlush()
|
|
786
497
|
|
|
787
|
-
|
|
788
|
-
const state = { start: 0, end: data.key.byteLength, buffer: data.key }
|
|
498
|
+
const head = (await headPromise) || initStoreHead()
|
|
789
499
|
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
|
|
500
|
+
if (head.seed === null || overwrite) head.seed = seed
|
|
501
|
+
tx.setHead(head)
|
|
502
|
+
tx.apply()
|
|
793
503
|
|
|
794
|
-
|
|
504
|
+
return head.seed
|
|
505
|
+
} finally {
|
|
506
|
+
await this._exit()
|
|
507
|
+
}
|
|
508
|
+
}
|
|
795
509
|
|
|
796
|
-
|
|
510
|
+
async getDefaultDiscoveryKey () {
|
|
511
|
+
if (this.version === 0) await this._migrateStore()
|
|
797
512
|
|
|
798
|
-
|
|
799
|
-
|
|
513
|
+
const rx = new CorestoreRX(this.db, EMPTY)
|
|
514
|
+
const headPromise = rx.getHead()
|
|
800
515
|
|
|
801
|
-
|
|
802
|
-
return c.decode(m.TreeNode, data.value)
|
|
803
|
-
}
|
|
516
|
+
rx.tryFlush()
|
|
804
517
|
|
|
805
|
-
|
|
806
|
-
|
|
518
|
+
const head = await headPromise
|
|
519
|
+
return head === null ? null : head.defaultDiscoveryKey
|
|
520
|
+
}
|
|
807
521
|
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
UINT.decode(state) // DATA.BITFIELD
|
|
522
|
+
async setDefaultDiscoveryKey (discoveryKey, { overwrite = true } = {}) {
|
|
523
|
+
if (this.version === 0) await this._migrateStore()
|
|
811
524
|
|
|
812
|
-
|
|
525
|
+
const view = await this._enter()
|
|
526
|
+
const tx = new CorestoreTX(view)
|
|
813
527
|
|
|
814
|
-
|
|
815
|
-
|
|
528
|
+
try {
|
|
529
|
+
const rx = new CorestoreRX(this.db, view)
|
|
530
|
+
const headPromise = rx.getHead()
|
|
816
531
|
|
|
817
|
-
|
|
818
|
-
const state = { start: 0, end: data.key.byteLength, buffer: data.key }
|
|
532
|
+
rx.tryFlush()
|
|
819
533
|
|
|
820
|
-
|
|
821
|
-
UINT.decode(state) // pointer
|
|
822
|
-
UINT.decode(state) // DATA.BITFIELD
|
|
534
|
+
const head = (await headPromise) || initStoreHead()
|
|
823
535
|
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
|
|
536
|
+
if (head.defaultDiscoveryKey === null || overwrite) head.defaultDiscoveryKey = discoveryKey
|
|
537
|
+
tx.setHead(head)
|
|
538
|
+
tx.apply()
|
|
827
539
|
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
|
|
540
|
+
return head.defaultDiscoveryKey
|
|
541
|
+
} finally {
|
|
542
|
+
await this._exit()
|
|
543
|
+
}
|
|
544
|
+
}
|
|
831
545
|
|
|
832
|
-
async
|
|
833
|
-
|
|
834
|
-
}
|
|
546
|
+
async has (discoveryKey) {
|
|
547
|
+
if (this.version === 0) await this._migrateStore()
|
|
835
548
|
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
}
|
|
549
|
+
const rx = new CorestoreRX(this.db, EMPTY)
|
|
550
|
+
const promise = rx.getCore(discoveryKey)
|
|
839
551
|
|
|
840
|
-
|
|
841
|
-
const value = await db.get(b4a.from([TL.STORAGE_INFO]))
|
|
842
|
-
if (value === null) return null
|
|
843
|
-
return c.decode(m.StorageInfo, value)
|
|
844
|
-
}
|
|
552
|
+
rx.tryFlush()
|
|
845
553
|
|
|
846
|
-
|
|
847
|
-
if (SLAB.buffer.byteLength - SLAB.start < size) {
|
|
848
|
-
SLAB.buffer = b4a.allocUnsafe(SLAB.end)
|
|
849
|
-
SLAB.start = 0
|
|
554
|
+
return (await promise) !== null
|
|
850
555
|
}
|
|
851
556
|
|
|
852
|
-
|
|
853
|
-
|
|
557
|
+
async resume (discoveryKey) {
|
|
558
|
+
if (this.version === 0) await this._migrateStore()
|
|
854
559
|
|
|
855
|
-
|
|
856
|
-
|
|
560
|
+
if (!discoveryKey) {
|
|
561
|
+
discoveryKey = await this.getDefaultDiscoveryKey()
|
|
562
|
+
if (!discoveryKey) return null
|
|
563
|
+
}
|
|
857
564
|
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
else bounded.gte = encodeDataIndex(pointer, type, 0)
|
|
565
|
+
const rx = new CorestoreRX(this.db, EMPTY)
|
|
566
|
+
const corePromise = rx.getCore(discoveryKey)
|
|
861
567
|
|
|
862
|
-
|
|
863
|
-
|
|
864
|
-
else bounded.lte = encodeDataIndex(pointer, type, Infinity) // infinity
|
|
568
|
+
rx.tryFlush()
|
|
569
|
+
const core = await corePromise
|
|
865
570
|
|
|
866
|
-
|
|
867
|
-
|
|
571
|
+
if (core === null) return null
|
|
572
|
+
return this._resumeFromPointers(EMPTY, discoveryKey, false, core)
|
|
573
|
+
}
|
|
868
574
|
|
|
869
|
-
|
|
870
|
-
|
|
575
|
+
async _resumeFromPointers (view, discoveryKey, create, { version, corePointer, dataPointer }) {
|
|
576
|
+
const core = { version, corePointer, dataPointer, dependencies: [] }
|
|
871
577
|
|
|
872
|
-
|
|
873
|
-
|
|
874
|
-
|
|
578
|
+
while (true) {
|
|
579
|
+
const rx = new CoreRX({ version, dataPointer, corePointer: 0, dependencies: [] }, this.db, view)
|
|
580
|
+
const dependencyPromise = rx.getDependency()
|
|
581
|
+
rx.tryFlush()
|
|
582
|
+
const dependency = await dependencyPromise
|
|
583
|
+
if (!dependency) break
|
|
584
|
+
core.dependencies.push(dependency)
|
|
585
|
+
dataPointer = dependency.dataPointer
|
|
586
|
+
}
|
|
875
587
|
|
|
876
|
-
|
|
877
|
-
else if (opts.lte) bounded.lte = encodeUserDataIndex(pointer, type, opts.lte)
|
|
878
|
-
else bounded.lte = encodeDataIndex(pointer, type, Infinity)
|
|
588
|
+
const result = new HypercoreStorage(this, this.db.session(), core, EMPTY, false)
|
|
879
589
|
|
|
880
|
-
|
|
881
|
-
|
|
590
|
+
if (result.core.version === 0) await this._migrateCore(result, discoveryKey, create)
|
|
591
|
+
return result
|
|
592
|
+
}
|
|
882
593
|
|
|
883
|
-
|
|
884
|
-
|
|
885
|
-
|
|
594
|
+
// not allowed to throw validation errors as its a shared tx!
|
|
595
|
+
async _create (view, { key, manifest, keyPair, encryptionKey, discoveryKey, alias, userData }) {
|
|
596
|
+
const rx = new CorestoreRX(this.db, view)
|
|
597
|
+
const tx = new CorestoreTX(view)
|
|
886
598
|
|
|
887
|
-
|
|
888
|
-
|
|
889
|
-
const start = state.start
|
|
890
|
-
encoding.encode(state, value)
|
|
599
|
+
const corePromise = rx.getCore(discoveryKey)
|
|
600
|
+
const headPromise = rx.getHead()
|
|
891
601
|
|
|
892
|
-
|
|
602
|
+
rx.tryFlush()
|
|
893
603
|
|
|
894
|
-
|
|
895
|
-
|
|
604
|
+
let [core, head] = await Promise.all([corePromise, headPromise])
|
|
605
|
+
if (core) return this._resumeFromPointers(view, discoveryKey, true, core)
|
|
896
606
|
|
|
897
|
-
|
|
898
|
-
|
|
899
|
-
const state = { start: 0, end, buffer: b4a.allocUnsafe(end) }
|
|
900
|
-
const start = state.start
|
|
901
|
-
UINT.encode(state, TL.CORE)
|
|
902
|
-
UINT.encode(state, pointer)
|
|
903
|
-
UINT.encode(state, type)
|
|
904
|
-
c.string.encode(state, name)
|
|
607
|
+
if (head === null) head = initStoreHead()
|
|
608
|
+
if (head.defaultDiscoveryKey === null) head.defaultDiscoveryKey = discoveryKey
|
|
905
609
|
|
|
906
|
-
|
|
907
|
-
|
|
610
|
+
const corePointer = head.allocated.cores++
|
|
611
|
+
const dataPointer = head.allocated.datas++
|
|
908
612
|
|
|
909
|
-
|
|
910
|
-
const state = ensureSlab(128)
|
|
911
|
-
const start = state.start
|
|
912
|
-
UINT.encode(state, TL.CORE)
|
|
913
|
-
UINT.encode(state, pointer)
|
|
914
|
-
UINT.encode(state, type)
|
|
915
|
-
if (index !== undefined) UINT.encode(state, index)
|
|
613
|
+
core = { version: VERSION, corePointer, dataPointer, alias }
|
|
916
614
|
|
|
917
|
-
|
|
918
|
-
|
|
615
|
+
tx.setHead(head)
|
|
616
|
+
tx.putCore(discoveryKey, core)
|
|
617
|
+
if (alias) tx.putCoreByAlias(alias, discoveryKey)
|
|
919
618
|
|
|
920
|
-
|
|
921
|
-
|
|
922
|
-
const start = state.start
|
|
923
|
-
UINT.encode(state, TL.DATA)
|
|
924
|
-
UINT.encode(state, pointer)
|
|
925
|
-
UINT.encode(state, type)
|
|
926
|
-
if (index !== undefined) UINT.encode(state, index)
|
|
619
|
+
const ptr = { corePointer, dataPointer, dependencies: [] }
|
|
620
|
+
const ctx = new CoreTX(ptr, this.db, view, tx.changes)
|
|
927
621
|
|
|
928
|
-
|
|
929
|
-
|
|
622
|
+
ctx.setAuth({
|
|
623
|
+
key,
|
|
624
|
+
discoveryKey,
|
|
625
|
+
manifest,
|
|
626
|
+
keyPair,
|
|
627
|
+
encryptionKey
|
|
628
|
+
})
|
|
930
629
|
|
|
931
|
-
|
|
932
|
-
|
|
933
|
-
|
|
934
|
-
|
|
935
|
-
|
|
936
|
-
UINT.encode(state, pointer)
|
|
937
|
-
UINT.encode(state, type)
|
|
938
|
-
c.string.encode(state, key)
|
|
630
|
+
if (userData) {
|
|
631
|
+
for (const { key, value } of userData) {
|
|
632
|
+
ctx.putUserData(key, value)
|
|
633
|
+
}
|
|
634
|
+
}
|
|
939
635
|
|
|
940
|
-
|
|
941
|
-
}
|
|
636
|
+
tx.apply()
|
|
942
637
|
|
|
943
|
-
|
|
944
|
-
|
|
945
|
-
const start = state.start
|
|
946
|
-
UINT.encode(state, TL.DKEYS)
|
|
947
|
-
c.fixed32.encode(state, discoveryKey)
|
|
948
|
-
return state.buffer.subarray(start, state.start)
|
|
949
|
-
}
|
|
638
|
+
return new HypercoreStorage(this, this.db.session(), ptr, EMPTY, false)
|
|
639
|
+
}
|
|
950
640
|
|
|
951
|
-
async
|
|
952
|
-
|
|
641
|
+
async create (data) {
|
|
642
|
+
if (this.version === 0) await this._migrateStore()
|
|
953
643
|
|
|
954
|
-
|
|
955
|
-
while (dep) {
|
|
956
|
-
const { data, length } = c.decode(m.DataDependency, dep)
|
|
957
|
-
if (treeLength === -1 || length <= treeLength) dependencies.push({ data, length })
|
|
644
|
+
const view = await this._enter()
|
|
958
645
|
|
|
959
|
-
|
|
646
|
+
try {
|
|
647
|
+
return await this._create(view, data)
|
|
648
|
+
} finally {
|
|
649
|
+
await this._exit()
|
|
650
|
+
}
|
|
960
651
|
}
|
|
961
|
-
|
|
962
|
-
return dependencies
|
|
963
652
|
}
|
|
964
653
|
|
|
965
|
-
|
|
966
|
-
for (const { data, length } of dependencies) {
|
|
967
|
-
if (index < length) return data
|
|
968
|
-
}
|
|
969
|
-
return null
|
|
970
|
-
}
|
|
654
|
+
module.exports = CorestoreStorage
|
|
971
655
|
|
|
972
|
-
function
|
|
973
|
-
|
|
974
|
-
|
|
656
|
+
function initStoreHead () {
|
|
657
|
+
return {
|
|
658
|
+
version: 0, // cause we wanna run the migration
|
|
659
|
+
allocated: {
|
|
660
|
+
datas: 0,
|
|
661
|
+
cores: 0
|
|
662
|
+
},
|
|
663
|
+
seed: null,
|
|
664
|
+
defaultDiscoveryKey: null
|
|
975
665
|
}
|
|
976
|
-
return null
|
|
977
|
-
}
|
|
978
|
-
|
|
979
|
-
function initialiseCoreInfo (db, { key, manifest, keyPair, encryptionKey }) {
|
|
980
|
-
db.setCoreAuth({ key, manifest })
|
|
981
|
-
if (keyPair) db.setLocalKeyPair(keyPair)
|
|
982
|
-
if (encryptionKey) db.setEncryptionKey(encryptionKey)
|
|
983
666
|
}
|
|
984
667
|
|
|
985
|
-
function
|
|
986
|
-
|
|
987
|
-
|
|
988
|
-
for (const { key, value } of userData) {
|
|
989
|
-
db.setUserData(key, value)
|
|
990
|
-
}
|
|
668
|
+
function getBatch (sessions, name, alloc) {
|
|
669
|
+
for (let i = 0; i < sessions.length; i++) {
|
|
670
|
+
if (sessions[i].name === name) return sessions[i]
|
|
991
671
|
}
|
|
992
|
-
}
|
|
993
672
|
|
|
994
|
-
|
|
673
|
+
if (!alloc) return null
|
|
995
674
|
|
|
996
|
-
|
|
997
|
-
|
|
998
|
-
|
|
999
|
-
|
|
675
|
+
const result = { name, dataPointer: -1 }
|
|
676
|
+
sessions.push(result)
|
|
677
|
+
return result
|
|
678
|
+
}
|
|
1000
679
|
|
|
1001
|
-
|
|
680
|
+
function isCorestoreStorage (s) {
|
|
681
|
+
return typeof s === 'object' && !!s && typeof s.setDefaultDiscoveryKey === 'function'
|
|
1002
682
|
}
|
|
1003
683
|
|
|
1004
|
-
|
|
1005
|
-
|
|
1006
|
-
|
|
1007
|
-
|
|
1008
|
-
|
|
1009
|
-
|
|
1010
|
-
|
|
684
|
+
function createColumnFamily (db) {
|
|
685
|
+
const col = new RocksDB.ColumnFamily(COLUMN_FAMILY, {
|
|
686
|
+
// tuning! atm just the default tuning from rocks, TODO: tweak
|
|
687
|
+
enableBlobFiles: false,
|
|
688
|
+
minBlobSize: 0,
|
|
689
|
+
blobFileSize: 0,
|
|
690
|
+
enableBlobGarbageCollection: true,
|
|
691
|
+
tableBlockSize: 16384,
|
|
692
|
+
tableCacheIndexAndFilterBlocks: true,
|
|
693
|
+
tableFormatVersion: 4
|
|
694
|
+
})
|
|
1011
695
|
|
|
1012
|
-
|
|
696
|
+
return db.columnFamily(col)
|
|
1013
697
|
}
|