autobee 2.0.0-rc.8 → 2.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +48 -1
- package/encoding/compat.js +1 -1
- package/encoding/legacy.js +33 -8
- package/encoding/spec/autobee/index.js +312 -25
- package/encoding/spec/autobee/schema.json +228 -2
- package/index.js +555 -310
- 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 +110 -52
- 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 +5 -4
- package/lib/reboot.js +0 -238
package/lib/reboot.js
DELETED
|
@@ -1,238 +0,0 @@
|
|
|
1
|
-
const safetyCatch = require('safety-catch')
|
|
2
|
-
const b4a = require('b4a')
|
|
3
|
-
const c = require('compact-encoding')
|
|
4
|
-
const crypto = require('hypercore-crypto')
|
|
5
|
-
|
|
6
|
-
const { decodeBlock } = require('hyperbee2/lib/encoding.js')
|
|
7
|
-
|
|
8
|
-
const System = require('./system.js')
|
|
9
|
-
const { assert } = require('./asserts.js')
|
|
10
|
-
const { LEGACY_AUTOBASE_VERSION } = require('./constants.js')
|
|
11
|
-
const { getEncoding } = require('../encoding/spec/autobee')
|
|
12
|
-
|
|
13
|
-
const SystemInfoV1 = getEncoding('@autobase-compat/info-v1')
|
|
14
|
-
const SystemInfoV2 = getEncoding('@autobase-compat/info-v2')
|
|
15
|
-
|
|
16
|
-
const INDEX_VERSION = 1
|
|
17
|
-
const [NS_SIGNER_NAMESPACE] = crypto.namespace('autobase', 1)
|
|
18
|
-
|
|
19
|
-
const EMPTY = b4a.alloc(0)
|
|
20
|
-
const DEFAULT_OP_TIMEOUT = 5_000
|
|
21
|
-
|
|
22
|
-
module.exports = class Reboot {
|
|
23
|
-
constructor(auto, head, tip, { timeout = DEFAULT_OP_TIMEOUT } = {}) {
|
|
24
|
-
this.auto = auto
|
|
25
|
-
this.system = new System(auto.store.namespace('reboot'), null, {
|
|
26
|
-
getEncryptionProvider: this.auto.getSystemEncryption,
|
|
27
|
-
encrypted: this.encrypted
|
|
28
|
-
})
|
|
29
|
-
|
|
30
|
-
// length -1 marks an uninitialised head: we know the key but not the length
|
|
31
|
-
this.head = { key: head.key, length: head.length ?? 0 }
|
|
32
|
-
this.timeout = timeout
|
|
33
|
-
this.tip = tip || null
|
|
34
|
-
this.destroyed = false
|
|
35
|
-
this.running = null
|
|
36
|
-
this.failed = false
|
|
37
|
-
this.cores = []
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
async run() {
|
|
41
|
-
try {
|
|
42
|
-
if (!this.running) this.running = this._run()
|
|
43
|
-
|
|
44
|
-
return await this.running
|
|
45
|
-
} catch (err) {
|
|
46
|
-
safetyCatch(err)
|
|
47
|
-
this.failed = true
|
|
48
|
-
return null
|
|
49
|
-
} finally {
|
|
50
|
-
await this.close()
|
|
51
|
-
}
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
async _run() {
|
|
55
|
-
// an uninitialised head has an unknown length: wait for the core to catch up
|
|
56
|
-
if (!this.head.length) {
|
|
57
|
-
this.head.length = await this._resolveLength()
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
// zero-length boot is unsupported
|
|
61
|
-
if (!this.head.length) return null
|
|
62
|
-
|
|
63
|
-
await this.system.boot(this.head, { timeout: this.timeout })
|
|
64
|
-
|
|
65
|
-
// a legacy system needs migrating rather than a plain fast-forward
|
|
66
|
-
if (this.system.version <= LEGACY_AUTOBASE_VERSION) {
|
|
67
|
-
// already migrated, don't migrate again
|
|
68
|
-
if (this.auto.system.version > LEGACY_AUTOBASE_VERSION) return null
|
|
69
|
-
return this._migrate()
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
const promises = []
|
|
73
|
-
|
|
74
|
-
// ensure local key is locally available always
|
|
75
|
-
promises.push(this.system.get(this.auto.local.key, { timeout: this.timeout }))
|
|
76
|
-
|
|
77
|
-
const view = this.auto.store.get({ key: this.system.view.key, active: true })
|
|
78
|
-
this.cores.push(view)
|
|
79
|
-
|
|
80
|
-
promises.push(view.get(this.system.view.length - 1, { timeout: this.timeout }))
|
|
81
|
-
|
|
82
|
-
for (const head of this.system.heads) {
|
|
83
|
-
promises.push(this.system.get(head.key, { timeout: this.timeout }))
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
await Promise.all(promises)
|
|
87
|
-
if (this.destroyed) return null
|
|
88
|
-
|
|
89
|
-
const shared = await this._commonAncestor()
|
|
90
|
-
if (this.destroyed) return null
|
|
91
|
-
|
|
92
|
-
return {
|
|
93
|
-
head: this.head,
|
|
94
|
-
tip: this.tip,
|
|
95
|
-
migrate: null,
|
|
96
|
-
shared
|
|
97
|
-
}
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
async _commonAncestor() {
|
|
101
|
-
try {
|
|
102
|
-
return await this.auto.system.commonAncestor(this.head)
|
|
103
|
-
} catch (err) {
|
|
104
|
-
safetyCatch(err)
|
|
105
|
-
return null
|
|
106
|
-
}
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
// Upgrade a legacy (pre-AUTOBEE_VERSION) system in place. The system already
|
|
110
|
-
// booted above (coerced to the current struct), but the original v1/v2 struct
|
|
111
|
-
// is needed to recover the indexers/views/entropy, so decode the raw block.
|
|
112
|
-
async _migrate() {
|
|
113
|
-
const core = this.auto.store.get({ key: this.head.key, encryption: null })
|
|
114
|
-
this.cores.push(core)
|
|
115
|
-
await core.ready()
|
|
116
|
-
|
|
117
|
-
// setup encryption
|
|
118
|
-
await core.setEncryption(this.auto.getSystemEncryption())
|
|
119
|
-
|
|
120
|
-
// Decode hyperbee block using hyperbee2 compat
|
|
121
|
-
const node = decodeBlock(await core.get(this.head.length - 1, { timeout: this.timeout }))
|
|
122
|
-
assert(node.keys.length > 0, 'bad system block')
|
|
123
|
-
|
|
124
|
-
// Decode system info from hyperbee block using autobee compat
|
|
125
|
-
const info = decodeLegacySystemInfo(node.keys[0].value)
|
|
126
|
-
|
|
127
|
-
const indexerManifests = await Promise.all(
|
|
128
|
-
info.indexers.map((idx) => this.getCoreManifest(idx.key, idx.length))
|
|
129
|
-
)
|
|
130
|
-
const entropy =
|
|
131
|
-
info.version > 1 && info.entropy ? info.entropy : indexerManifests[0].signers[0].namespace
|
|
132
|
-
|
|
133
|
-
const views = new Map()
|
|
134
|
-
const viewProms = []
|
|
135
|
-
|
|
136
|
-
for (const name of this.auto.legacyViews) {
|
|
137
|
-
viewProms.push(this.findViewByName(views, indexerManifests, info.views, entropy, name))
|
|
138
|
-
}
|
|
139
|
-
|
|
140
|
-
await Promise.all(viewProms)
|
|
141
|
-
if (this.destroyed) return null
|
|
142
|
-
|
|
143
|
-
return {
|
|
144
|
-
head: this.head,
|
|
145
|
-
tip: null,
|
|
146
|
-
migrate: views
|
|
147
|
-
}
|
|
148
|
-
}
|
|
149
|
-
|
|
150
|
-
async _resolveLength() {
|
|
151
|
-
const core = this.auto.store.get({ key: this.head.key })
|
|
152
|
-
this.cores.push(core)
|
|
153
|
-
|
|
154
|
-
await core.ready()
|
|
155
|
-
|
|
156
|
-
return coreLength(core, this.timeout)
|
|
157
|
-
}
|
|
158
|
-
|
|
159
|
-
async getCoreManifest(key, length) {
|
|
160
|
-
const core = this.auto.store.get(key)
|
|
161
|
-
this.cores.push(core)
|
|
162
|
-
|
|
163
|
-
await core.ready()
|
|
164
|
-
|
|
165
|
-
try {
|
|
166
|
-
if (!core.manifest) await core.get(length - 1, { timeout: this.timeout })
|
|
167
|
-
} catch {
|
|
168
|
-
return null
|
|
169
|
-
}
|
|
170
|
-
|
|
171
|
-
return core.manifest
|
|
172
|
-
}
|
|
173
|
-
|
|
174
|
-
async findViewByName(result, indexerManifests, views, entropy, name) {
|
|
175
|
-
if (indexerManifests.length === 0) return null
|
|
176
|
-
|
|
177
|
-
const namespace = deriveNamespace(name, this.auto.key, entropy, this.auto.encryptionKey)
|
|
178
|
-
|
|
179
|
-
for (const v of views) {
|
|
180
|
-
const manifest = await this.getCoreManifest(v.key, v.length)
|
|
181
|
-
if (!manifest) continue
|
|
182
|
-
|
|
183
|
-
if (manifest.signers.length === 0) continue
|
|
184
|
-
|
|
185
|
-
const signer = manifest.signers[0]
|
|
186
|
-
|
|
187
|
-
if (b4a.equals(signer.namespace, namespace)) {
|
|
188
|
-
result.set(name, v)
|
|
189
|
-
return
|
|
190
|
-
}
|
|
191
|
-
}
|
|
192
|
-
}
|
|
193
|
-
|
|
194
|
-
async close() {
|
|
195
|
-
this.destroyed = true
|
|
196
|
-
if (this.system) await this.system.close()
|
|
197
|
-
for (const core of this.cores) await core.close()
|
|
198
|
-
}
|
|
199
|
-
}
|
|
200
|
-
|
|
201
|
-
function decodeLegacySystemInfo(buffer) {
|
|
202
|
-
const state = { start: 0, end: buffer.length, buffer }
|
|
203
|
-
const version = c.uint.decode(state)
|
|
204
|
-
|
|
205
|
-
state.start--
|
|
206
|
-
|
|
207
|
-
switch (version) {
|
|
208
|
-
case 1:
|
|
209
|
-
return SystemInfoV1.decode(state)
|
|
210
|
-
case 2:
|
|
211
|
-
return SystemInfoV2.decode(state)
|
|
212
|
-
default:
|
|
213
|
-
return { version }
|
|
214
|
-
}
|
|
215
|
-
}
|
|
216
|
-
|
|
217
|
-
function coreLength(core, timeout) {
|
|
218
|
-
if (core.length) return core.length
|
|
219
|
-
|
|
220
|
-
return new Promise((resolve) => {
|
|
221
|
-
core.on('append', () => resolve(core.length))
|
|
222
|
-
setTimeout(resolve, timeout, 0)
|
|
223
|
-
})
|
|
224
|
-
}
|
|
225
|
-
|
|
226
|
-
function deriveNamespace(name, bootstrap, entropy, encryptionKey) {
|
|
227
|
-
const encryptionId = crypto.hash(encryptionKey || EMPTY)
|
|
228
|
-
const version = c.encode(c.uint, INDEX_VERSION)
|
|
229
|
-
|
|
230
|
-
return crypto.hash([
|
|
231
|
-
NS_SIGNER_NAMESPACE,
|
|
232
|
-
version,
|
|
233
|
-
bootstrap,
|
|
234
|
-
encryptionId,
|
|
235
|
-
entropy,
|
|
236
|
-
b4a.from(name)
|
|
237
|
-
])
|
|
238
|
-
}
|