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