autobee 2.0.0-rc.9 → 2.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +48 -1
- package/encoding/legacy.js +30 -5
- package/encoding/spec/autobee/index.js +289 -10
- package/encoding/spec/autobee/schema.json +217 -1
- package/index.js +578 -331
- package/lib/apply-calls.js +5 -6
- package/lib/apply-view.js +25 -0
- package/lib/boot.js +14 -182
- package/lib/constants.js +3 -6
- package/lib/encoding.js +72 -3
- package/lib/fast-forward.js +346 -0
- package/lib/migrations.js +457 -0
- package/lib/promotions.js +164 -0
- package/lib/system.js +97 -179
- package/lib/topo.js +83 -88
- package/lib/trusted.js +44 -0
- package/lib/updates.js +1 -38
- package/lib/witness.js +34 -65
- package/lib/writers.js +180 -116
- package/package.json +4 -3
- package/lib/reboot.js +0 -238
package/lib/apply-calls.js
CHANGED
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
const ID = require('hypercore-id-encoding')
|
|
2
|
-
const b4a = require('b4a')
|
|
3
2
|
|
|
4
3
|
class ApplyCalls {
|
|
5
4
|
constructor(auto) {
|
|
@@ -35,15 +34,15 @@ class ApplyCalls {
|
|
|
35
34
|
if (typeof key === 'string') key = ID.decode(key)
|
|
36
35
|
// no witness needed for genesis
|
|
37
36
|
const isGenesis = !this.applying
|
|
38
|
-
|
|
37
|
+
const coord = this.applying
|
|
38
|
+
? { key: this.applying[0].key, length: this.applying[0].length }
|
|
39
|
+
: null
|
|
40
|
+
const carrier = this.applying ? this.applying[0].weight : -1
|
|
41
|
+
return this.auto.system.addWriter(key, { weight, isGenesis, coord, carrier })
|
|
39
42
|
}
|
|
40
43
|
|
|
41
44
|
ackWriter(key) {
|
|
42
45
|
if (typeof key === 'string') key = ID.decode(key)
|
|
43
|
-
// acking an optimistic node means we owe it a linking node
|
|
44
|
-
if (this.applying && this.applying[0].optimistic && !b4a.equals(key, this.auto.local.key)) {
|
|
45
|
-
this.auto._ackRequired = true
|
|
46
|
-
}
|
|
47
46
|
return this.auto.system.ackWriter(key)
|
|
48
47
|
}
|
|
49
48
|
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
module.exports = class ApplyView {
|
|
2
|
+
constructor(bee, auto) {
|
|
3
|
+
this.bee = bee
|
|
4
|
+
this.auto = auto
|
|
5
|
+
this.view = ApplyView.open(bee, auto)
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
static open(bee, auto) {
|
|
9
|
+
return auto._handlers.open ? auto._handlers.open(bee, auto) : bee
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
static close(view, auto) {
|
|
13
|
+
if (!auto._handlers.close) return Promise.resolve()
|
|
14
|
+
return auto._handlers.close(view)
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
apply(userBatch) {
|
|
18
|
+
return this.auto._handlers.apply(userBatch, this.view, this.auto._host)
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
async close() {
|
|
22
|
+
await ApplyView.close(this.view, this.auto)
|
|
23
|
+
await this.bee.close()
|
|
24
|
+
}
|
|
25
|
+
}
|
package/lib/boot.js
CHANGED
|
@@ -1,20 +1,6 @@
|
|
|
1
|
-
const c = require('compact-encoding')
|
|
2
1
|
const b4a = require('b4a')
|
|
3
|
-
const crypto = require('hypercore-crypto')
|
|
4
|
-
const { AutobeeEncryption } = require('autobee-encryption')
|
|
5
|
-
const { decodeBlock } = require('hyperbee2/lib/encoding.js')
|
|
6
|
-
|
|
7
2
|
const encoding = require('./encoding.js')
|
|
8
|
-
const {
|
|
9
|
-
|
|
10
|
-
const { getEncoding } = require('../encoding/spec/autobee')
|
|
11
|
-
|
|
12
|
-
const SystemInfoV1 = getEncoding('@autobase-compat/info-v1')
|
|
13
|
-
const SystemInfoV2 = getEncoding('@autobase-compat/info-v2')
|
|
14
|
-
|
|
15
|
-
const EMPTY = b4a.alloc(0)
|
|
16
|
-
const INDEX_VERSION = 1
|
|
17
|
-
const [NS_SIGNER_NAMESPACE] = crypto.namespace('autobase', 1)
|
|
3
|
+
const { checkAutobaseMigration } = require('./migrations.js')
|
|
18
4
|
|
|
19
5
|
module.exports = async function boot(
|
|
20
6
|
corestore,
|
|
@@ -109,7 +95,7 @@ module.exports = async function boot(
|
|
|
109
95
|
await result.bootstrap.setUserData('autobee/local', result.local.key)
|
|
110
96
|
await result.local.setUserData('referrer', result.key)
|
|
111
97
|
|
|
112
|
-
result.migration = await
|
|
98
|
+
result.migration = await migrateWithFallback(
|
|
113
99
|
corestore,
|
|
114
100
|
result.local,
|
|
115
101
|
result.bootstrap,
|
|
@@ -153,59 +139,20 @@ module.exports = async function boot(
|
|
|
153
139
|
return result
|
|
154
140
|
}
|
|
155
141
|
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
// setup encryption
|
|
167
|
-
const encryptionKey = await local.getUserData('autobase/encryption')
|
|
168
|
-
await AutobeeEncryption.setSystemEncryption(bootstrap.key, encryptionKey, core)
|
|
169
|
-
|
|
170
|
-
const catchup = await getCatchupHeads(core)
|
|
171
|
-
const nodes = await Promise.all(
|
|
172
|
-
catchup.map((n) => getWriterBatch(store, n, bootstrap.key, encryptionKey))
|
|
173
|
-
)
|
|
142
|
+
// the bootstrap pointer names the rotated-to writer, whose legacy boot record
|
|
143
|
+
// can be unusable. the namespace local still carries the pre-rotation record,
|
|
144
|
+
// which points at the last system core that was actually signed
|
|
145
|
+
async function migrateWithFallback(corestore, local, bootstrap, legacyViews) {
|
|
146
|
+
try {
|
|
147
|
+
return await checkAutobaseMigration(corestore, local, bootstrap, legacyViews)
|
|
148
|
+
} catch (err) {
|
|
149
|
+
const prev = corestore.get({ name: 'local', active: false })
|
|
150
|
+
await prev.ready()
|
|
174
151
|
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
assert(node.keys.length > 0, 'bad system block')
|
|
152
|
+
if (b4a.equals(prev.key, local.key)) throw err
|
|
153
|
+
if (!(await prev.getUserData('autobase/boot'))) throw err
|
|
178
154
|
|
|
179
|
-
|
|
180
|
-
const info = decodeLegacySystemInfo(node.keys[0].value)
|
|
181
|
-
|
|
182
|
-
const indexerManifests = await Promise.all(
|
|
183
|
-
info.indexers.map((idx) => getCoreManifest(store, idx.key))
|
|
184
|
-
)
|
|
185
|
-
const views = await Promise.all(info.views.map((v) => getPersistedView(store, v)))
|
|
186
|
-
const entropy =
|
|
187
|
-
info.version > 1 && info.entropy ? info.entropy : indexerManifests[0].signers[0].namespace
|
|
188
|
-
|
|
189
|
-
const mappedViews = new Map()
|
|
190
|
-
for (const name of legacyViews) {
|
|
191
|
-
const v = await findViewByName(
|
|
192
|
-
store,
|
|
193
|
-
bootstrap.key,
|
|
194
|
-
encryptionKey,
|
|
195
|
-
indexerManifests,
|
|
196
|
-
views,
|
|
197
|
-
entropy,
|
|
198
|
-
name
|
|
199
|
-
)
|
|
200
|
-
|
|
201
|
-
mappedViews.set(name, v)
|
|
202
|
-
}
|
|
203
|
-
|
|
204
|
-
return {
|
|
205
|
-
encryptionKey,
|
|
206
|
-
system,
|
|
207
|
-
views: mappedViews,
|
|
208
|
-
catchup: nodes
|
|
155
|
+
return checkAutobaseMigration(corestore, prev, bootstrap, legacyViews)
|
|
209
156
|
}
|
|
210
157
|
}
|
|
211
158
|
|
|
@@ -218,118 +165,3 @@ async function getLocalKey(bootstrap) {
|
|
|
218
165
|
if (current) return current
|
|
219
166
|
return legacy
|
|
220
167
|
}
|
|
221
|
-
|
|
222
|
-
async function getPersistedView(store, view) {
|
|
223
|
-
const core = store.get(view.key)
|
|
224
|
-
await core.ready()
|
|
225
|
-
|
|
226
|
-
const length = core.length
|
|
227
|
-
await core.close()
|
|
228
|
-
|
|
229
|
-
return { key: view.key, length }
|
|
230
|
-
}
|
|
231
|
-
|
|
232
|
-
async function getCatchupHeads(core) {
|
|
233
|
-
const session = core.session({ name: 'batch' })
|
|
234
|
-
await session.ready()
|
|
235
|
-
|
|
236
|
-
const nodes = []
|
|
237
|
-
for (let i = core.length; i < session.length; i++) {
|
|
238
|
-
const node = await session.get(i, { wait: false })
|
|
239
|
-
if (!node) throw new Error('Expect nodes to exist locally')
|
|
240
|
-
|
|
241
|
-
const entry = decodeBlock(node).keys[0]
|
|
242
|
-
if (entry.key[0] !== 0x01) continue
|
|
243
|
-
|
|
244
|
-
const { key, length } = encoding.decodeSystemWriter(entry.key, entry.value)
|
|
245
|
-
nodes.push({ key, length })
|
|
246
|
-
}
|
|
247
|
-
|
|
248
|
-
return nodes
|
|
249
|
-
}
|
|
250
|
-
|
|
251
|
-
async function getWriterBatch(store, head, key, encryptionKey, nodes = []) {
|
|
252
|
-
const batch = []
|
|
253
|
-
|
|
254
|
-
const core = store.get(head.key)
|
|
255
|
-
await core.ready()
|
|
256
|
-
|
|
257
|
-
await core.setEncryption(AutobeeEncryption.getWriterEncryption(key, encryptionKey))
|
|
258
|
-
|
|
259
|
-
let seq = head.length - 1
|
|
260
|
-
const block = await core.get(seq--, { wait: false })
|
|
261
|
-
if (!block) throw new Error('Expect writer node to exist locally')
|
|
262
|
-
|
|
263
|
-
const node = encoding.decodeOplog(block)
|
|
264
|
-
|
|
265
|
-
batch.unshift({ ...head, ...node, from: core })
|
|
266
|
-
|
|
267
|
-
while (seq >= 0) {
|
|
268
|
-
const block = await core.get(seq--, { wait: false })
|
|
269
|
-
if (!block) break
|
|
270
|
-
|
|
271
|
-
const node = encoding.decodeOplog(block)
|
|
272
|
-
if (!node.batch.end) break
|
|
273
|
-
|
|
274
|
-
batch.unshift({ key: head.key, length: seq + 2, from: core, ...node })
|
|
275
|
-
}
|
|
276
|
-
|
|
277
|
-
return batch
|
|
278
|
-
}
|
|
279
|
-
|
|
280
|
-
async function getCoreManifest(store, key) {
|
|
281
|
-
const core = store.get(key)
|
|
282
|
-
await core.ready()
|
|
283
|
-
|
|
284
|
-
const manifest = core.manifest
|
|
285
|
-
await core.close()
|
|
286
|
-
|
|
287
|
-
return manifest
|
|
288
|
-
}
|
|
289
|
-
|
|
290
|
-
function decodeLegacySystemInfo(buffer) {
|
|
291
|
-
const state = { start: 0, end: buffer.length, buffer }
|
|
292
|
-
const version = c.uint.decode(state)
|
|
293
|
-
|
|
294
|
-
state.start--
|
|
295
|
-
|
|
296
|
-
switch (version) {
|
|
297
|
-
case 1:
|
|
298
|
-
return SystemInfoV1.decode(state)
|
|
299
|
-
case 2:
|
|
300
|
-
return SystemInfoV2.decode(state)
|
|
301
|
-
default:
|
|
302
|
-
bail('Expected legacy system info')
|
|
303
|
-
}
|
|
304
|
-
}
|
|
305
|
-
|
|
306
|
-
function deriveNamespace(name, bootstrap, entropy, encryptionKey) {
|
|
307
|
-
const encryptionId = crypto.hash(encryptionKey || EMPTY)
|
|
308
|
-
const version = c.encode(c.uint, INDEX_VERSION)
|
|
309
|
-
|
|
310
|
-
return crypto.hash([
|
|
311
|
-
NS_SIGNER_NAMESPACE,
|
|
312
|
-
version,
|
|
313
|
-
bootstrap,
|
|
314
|
-
encryptionId,
|
|
315
|
-
entropy,
|
|
316
|
-
b4a.from(name)
|
|
317
|
-
])
|
|
318
|
-
}
|
|
319
|
-
|
|
320
|
-
async function findViewByName(store, key, encryptionKey, indexerManifests, views, entropy, name) {
|
|
321
|
-
if (indexerManifests.length === 0) return null
|
|
322
|
-
|
|
323
|
-
const namespace = deriveNamespace(name, key, entropy, encryptionKey)
|
|
324
|
-
|
|
325
|
-
for (const v of views) {
|
|
326
|
-
const manifest = await getCoreManifest(store, v.key)
|
|
327
|
-
if (manifest.signers.length === 0) continue
|
|
328
|
-
|
|
329
|
-
const signer = manifest.signers[0]
|
|
330
|
-
|
|
331
|
-
if (b4a.equals(signer.namespace, namespace)) return v
|
|
332
|
-
}
|
|
333
|
-
|
|
334
|
-
return null
|
|
335
|
-
}
|
package/lib/constants.js
CHANGED
|
@@ -1,16 +1,13 @@
|
|
|
1
|
-
const crypto = require('hypercore-crypto')
|
|
2
|
-
|
|
3
1
|
const LEGACY_OPLOG_VERSION = 2
|
|
4
|
-
const OPLOG_VERSION =
|
|
2
|
+
const OPLOG_VERSION = 4
|
|
5
3
|
const LEGACY_AUTOBASE_VERSION = 2
|
|
6
4
|
const AUTOBEE_VERSION = 3
|
|
7
|
-
|
|
8
|
-
const [NS_WITNESS] = crypto.namespace('autobee-witness', 1)
|
|
5
|
+
const DEFAULT_OP_TIMEOUT = 5000
|
|
9
6
|
|
|
10
7
|
module.exports = {
|
|
11
8
|
LEGACY_OPLOG_VERSION,
|
|
12
9
|
OPLOG_VERSION,
|
|
13
10
|
LEGACY_AUTOBASE_VERSION,
|
|
14
11
|
AUTOBEE_VERSION,
|
|
15
|
-
|
|
12
|
+
DEFAULT_OP_TIMEOUT
|
|
16
13
|
}
|
package/lib/encoding.js
CHANGED
|
@@ -12,6 +12,7 @@ const SystemInfo = getEncoding('@autobee/system-info')
|
|
|
12
12
|
const SystemWriter = getEncoding('@autobee/system-writer')
|
|
13
13
|
const ManifestData = getEncoding('@autobee/manifest-data')
|
|
14
14
|
const AutobaseBootRecord = getEncoding('@autobase-compat/boot-record')
|
|
15
|
+
const MigratedHead = getEncoding('@autobee/migrated-head')
|
|
15
16
|
|
|
16
17
|
exports.OPLOG_VERSION = OPLOG_VERSION
|
|
17
18
|
|
|
@@ -21,13 +22,23 @@ exports.ManifestData = ManifestData
|
|
|
21
22
|
exports.encodeBootRecord = encodeBootRecord
|
|
22
23
|
exports.decodeBootRecord = decodeBootRecord
|
|
23
24
|
|
|
25
|
+
exports.encodeMigratedHead = encodeMigratedHead
|
|
26
|
+
exports.decodeMigratedHead = decodeMigratedHead
|
|
27
|
+
|
|
24
28
|
exports.encodePreviousDrain = encodePreviousDrain
|
|
25
29
|
exports.decodePreviousDrain = decodePreviousDrain
|
|
26
30
|
|
|
27
31
|
exports.decodeAutobaseBootRecord = decodeAutobaseBootRecord
|
|
28
32
|
|
|
33
|
+
exports.encodeSystemGrantKey = encodeSystemGrantKey
|
|
34
|
+
exports.encodeSystemGrantHint = encodeSystemGrantHint
|
|
35
|
+
exports.decodeSystemGrantHint = decodeSystemGrantHint
|
|
36
|
+
exports.encodeSystemPendingKey = encodeSystemPendingKey
|
|
37
|
+
exports.decodeSystemPendingKey = decodeSystemPendingKey
|
|
38
|
+
exports.encodeSystemPendingRange = encodeSystemPendingRange
|
|
39
|
+
exports.encodeSystemPending = encodeSystemPending
|
|
40
|
+
exports.decodeSystemPending = decodeSystemPending
|
|
29
41
|
exports.encodeSystemWriterKey = encodeSystemWriterKey
|
|
30
|
-
exports.encodeSystemIndexerKey = encodeSystemIndexerKey
|
|
31
42
|
exports.encodeSystemWriter = encodeSystemWriter
|
|
32
43
|
exports.decodeSystemWriter = decodeSystemWriter
|
|
33
44
|
|
|
@@ -39,6 +50,14 @@ function decodeBootRecord(value) {
|
|
|
39
50
|
return c.decode(Link, value)
|
|
40
51
|
}
|
|
41
52
|
|
|
53
|
+
function encodeMigratedHead(m) {
|
|
54
|
+
return c.encode(MigratedHead, m)
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
function decodeMigratedHead(value) {
|
|
58
|
+
return c.decode(MigratedHead, value)
|
|
59
|
+
}
|
|
60
|
+
|
|
42
61
|
function encodePreviousDrain(d) {
|
|
43
62
|
return c.encode(c.uint, d)
|
|
44
63
|
}
|
|
@@ -59,13 +78,57 @@ function encodeSystemWriterKey(key) {
|
|
|
59
78
|
return buf
|
|
60
79
|
}
|
|
61
80
|
|
|
62
|
-
function
|
|
81
|
+
function encodeSystemPendingKey(key) {
|
|
82
|
+
const buf = b4a.allocUnsafe(33)
|
|
83
|
+
buf[0] = 5
|
|
84
|
+
buf.set(key, 1)
|
|
85
|
+
return buf
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
function decodeSystemPendingKey(buf) {
|
|
89
|
+
return buf.subarray(1, 33)
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
function encodeSystemPendingRange() {
|
|
93
|
+
return { gte: b4a.from([5]), lt: b4a.from([6]) }
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
function encodeSystemPending(entry) {
|
|
97
|
+
return c.encode(c.uint, entry.weight)
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
function decodeSystemPending(value) {
|
|
101
|
+
return { weight: c.decode(c.uint, value) }
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
function encodeSystemGrantKey(key) {
|
|
63
105
|
const buf = b4a.allocUnsafe(33)
|
|
64
|
-
buf[0] =
|
|
106
|
+
buf[0] = 4
|
|
65
107
|
buf.set(key, 1)
|
|
66
108
|
return buf
|
|
67
109
|
}
|
|
68
110
|
|
|
111
|
+
function encodeSystemGrantHint(hint) {
|
|
112
|
+
const state = { start: 0, end: 0, buffer: null }
|
|
113
|
+
c.fixed32.preencode(state, hint.key)
|
|
114
|
+
c.uint.preencode(state, hint.length)
|
|
115
|
+
c.uint.preencode(state, hint.weight)
|
|
116
|
+
state.buffer = b4a.alloc(state.end)
|
|
117
|
+
c.fixed32.encode(state, hint.key)
|
|
118
|
+
c.uint.encode(state, hint.length)
|
|
119
|
+
c.uint.encode(state, hint.weight)
|
|
120
|
+
return state.buffer
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
function decodeSystemGrantHint(value) {
|
|
124
|
+
const state = { start: 0, end: value.byteLength, buffer: value }
|
|
125
|
+
return {
|
|
126
|
+
key: c.fixed32.decode(state),
|
|
127
|
+
length: c.uint.decode(state),
|
|
128
|
+
weight: c.uint.decode(state)
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
|
|
69
132
|
function encodeSystemWriter(m) {
|
|
70
133
|
return c.encode(SystemWriter, m)
|
|
71
134
|
}
|
|
@@ -107,6 +170,9 @@ function decodeOplog(buf) {
|
|
|
107
170
|
links: m.node.heads,
|
|
108
171
|
batch: { start: 0, end: m.node.batch - 1 },
|
|
109
172
|
views: null,
|
|
173
|
+
trusted: null,
|
|
174
|
+
witness: null,
|
|
175
|
+
approvals: null,
|
|
110
176
|
optimistic: !!m.optimistic,
|
|
111
177
|
value: m.node.value
|
|
112
178
|
}
|
|
@@ -153,6 +219,9 @@ function createValue(value, opts = {}) {
|
|
|
153
219
|
links,
|
|
154
220
|
batch: null,
|
|
155
221
|
views,
|
|
222
|
+
trusted: null,
|
|
223
|
+
witness: null,
|
|
224
|
+
approvals: null,
|
|
156
225
|
optimistic,
|
|
157
226
|
value
|
|
158
227
|
}
|