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/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
- }