autobee 2.6.0 → 2.7.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 +5 -1
- package/encoding/spec/autobee/index.js +23 -6
- package/encoding/spec/autobee/schema.json +10 -0
- package/index.js +17 -8
- package/lib/fast-forward.js +7 -5
- package/lib/system.js +33 -1
- package/lib/writers.js +16 -5
- package/package.json +4 -3
package/README.md
CHANGED
|
@@ -256,13 +256,17 @@ Return the oplog head you most recently vouched for, given the `target` view bei
|
|
|
256
256
|
|
|
257
257
|
Called at flush time to stamp your own oplog (with your view as `target` and a `null` reference), and again per candidate during discovery.
|
|
258
258
|
|
|
259
|
+
During discovery `target` is a view opened for the candidate, and reads through it carry the fast-forward's timeout. Anything else the hook awaits is unbounded, so if it reaches beyond that view, put a timeout on it: the candidate waits on the hook, and so does closing it.
|
|
260
|
+
|
|
259
261
|
#### `warmup(view)`
|
|
260
262
|
|
|
261
263
|
Prepare a candidate `view` before the fast-forward onto it is applied.
|
|
262
264
|
|
|
263
265
|
Called as soon as the candidate's view head is known, and runs alongside the block downloads the fast-forward is already doing rather than after them. Throw or reject to reject the candidate: the fast-forward is abandoned and the usual apply path catches up instead.
|
|
264
266
|
|
|
265
|
-
`view` is opened and closed through the `open` and `close` handlers, so don't build or close a db of your own.
|
|
267
|
+
`view` is opened and closed through the `open` and `close` handlers, so don't build or close a db of your own. Reads through it carry the fast-forward's timeout, so a warmup that needs blocks nobody serves fails the candidate instead of hanging.
|
|
268
|
+
|
|
269
|
+
Only reads through `view` are bounded. Anything else the handler awaits, such as other cores or your own network calls, is up to you to time out: the fast-forward waits on the handler, and an abandoned candidate cannot finish closing until it returns.
|
|
266
270
|
|
|
267
271
|
#### `fastForward.boot`
|
|
268
272
|
|
|
@@ -494,13 +494,14 @@ const encoding18 = {
|
|
|
494
494
|
c.uint.preencode(state, m.flushes)
|
|
495
495
|
encoding22.preencode(state, m.view)
|
|
496
496
|
encoding18_3.preencode(state, m.heads)
|
|
497
|
-
state.end++ // max flag is
|
|
497
|
+
state.end++ // max flag is 4 so always one byte
|
|
498
498
|
|
|
499
499
|
if (m.indexers) encoding18_4.preencode(state, m.indexers)
|
|
500
500
|
if (m.pending) encoding18_5.preencode(state, m.pending)
|
|
501
|
+
if (m.hash) c.fixed8.preencode(state, m.hash)
|
|
501
502
|
},
|
|
502
503
|
encode(state, m) {
|
|
503
|
-
const flags = (m.indexers ? 1 : 0) | (m.pending ? 2 : 0)
|
|
504
|
+
const flags = (m.indexers ? 1 : 0) | (m.pending ? 2 : 0) | (m.hash ? 4 : 0)
|
|
504
505
|
|
|
505
506
|
c.uint.encode(state, m.timestamp)
|
|
506
507
|
c.uint.encode(state, m.flushes)
|
|
@@ -510,6 +511,7 @@ const encoding18 = {
|
|
|
510
511
|
|
|
511
512
|
if (m.indexers) encoding18_4.encode(state, m.indexers)
|
|
512
513
|
if (m.pending) encoding18_5.encode(state, m.pending)
|
|
514
|
+
if (m.hash) c.fixed8.encode(state, m.hash)
|
|
513
515
|
},
|
|
514
516
|
decode(state) {
|
|
515
517
|
const v = c.uint.decode(state)
|
|
@@ -526,7 +528,8 @@ const encoding18 = {
|
|
|
526
528
|
view: r2,
|
|
527
529
|
heads: r3,
|
|
528
530
|
indexers: (flags & 1) !== 0 ? encoding18_4.decode(state) : null,
|
|
529
|
-
pending: (flags & 2) !== 0 ? encoding18_5.decode(state) : null
|
|
531
|
+
pending: (flags & 2) !== 0 ? encoding18_5.decode(state) : null,
|
|
532
|
+
hash: (flags & 4) !== 0 ? c.fixed8.decode(state) : null
|
|
530
533
|
}
|
|
531
534
|
}
|
|
532
535
|
}
|
|
@@ -1215,9 +1218,19 @@ const encoding37_6 = c.array(encoding36)
|
|
|
1215
1218
|
// @autobee/oplog-message-v4
|
|
1216
1219
|
const encoding37 = {
|
|
1217
1220
|
preencode(state, m) {
|
|
1221
|
+
const flags =
|
|
1222
|
+
(m.batch ? 1 : 0) |
|
|
1223
|
+
(m.views ? 2 : 0) |
|
|
1224
|
+
(m.trusted ? 4 : 0) |
|
|
1225
|
+
(m.witness ? 8 : 0) |
|
|
1226
|
+
(m.approvals ? 16 : 0) |
|
|
1227
|
+
(m.optimistic ? 32 : 0) |
|
|
1228
|
+
(m.value ? 64 : 0) |
|
|
1229
|
+
(m.hash ? 128 : 0)
|
|
1230
|
+
|
|
1218
1231
|
c.uint.preencode(state, m.timestamp)
|
|
1219
1232
|
encoding37_1.preencode(state, m.links)
|
|
1220
|
-
state
|
|
1233
|
+
c.uint.preencode(state, flags)
|
|
1221
1234
|
|
|
1222
1235
|
if (m.batch) encoding24.preencode(state, m.batch)
|
|
1223
1236
|
if (m.views) encoding25.preencode(state, m.views)
|
|
@@ -1225,6 +1238,7 @@ const encoding37 = {
|
|
|
1225
1238
|
if (m.witness) encoding35.preencode(state, m.witness)
|
|
1226
1239
|
if (m.approvals) encoding37_6.preencode(state, m.approvals)
|
|
1227
1240
|
if (m.value) c.buffer.preencode(state, m.value)
|
|
1241
|
+
if (m.hash) c.fixed8.preencode(state, m.hash)
|
|
1228
1242
|
},
|
|
1229
1243
|
encode(state, m) {
|
|
1230
1244
|
const flags =
|
|
@@ -1234,7 +1248,8 @@ const encoding37 = {
|
|
|
1234
1248
|
(m.witness ? 8 : 0) |
|
|
1235
1249
|
(m.approvals ? 16 : 0) |
|
|
1236
1250
|
(m.optimistic ? 32 : 0) |
|
|
1237
|
-
(m.value ? 64 : 0)
|
|
1251
|
+
(m.value ? 64 : 0) |
|
|
1252
|
+
(m.hash ? 128 : 0)
|
|
1238
1253
|
|
|
1239
1254
|
c.uint.encode(state, m.timestamp)
|
|
1240
1255
|
encoding37_1.encode(state, m.links)
|
|
@@ -1246,6 +1261,7 @@ const encoding37 = {
|
|
|
1246
1261
|
if (m.witness) encoding35.encode(state, m.witness)
|
|
1247
1262
|
if (m.approvals) encoding37_6.encode(state, m.approvals)
|
|
1248
1263
|
if (m.value) c.buffer.encode(state, m.value)
|
|
1264
|
+
if (m.hash) c.fixed8.encode(state, m.hash)
|
|
1249
1265
|
},
|
|
1250
1266
|
decode(state) {
|
|
1251
1267
|
const v = c.uint.decode(state)
|
|
@@ -1263,7 +1279,8 @@ const encoding37 = {
|
|
|
1263
1279
|
witness: (flags & 8) !== 0 ? encoding35.decode(state) : null,
|
|
1264
1280
|
approvals: (flags & 16) !== 0 ? encoding37_6.decode(state) : null,
|
|
1265
1281
|
optimistic: (flags & 32) !== 0,
|
|
1266
|
-
value: (flags & 64) !== 0 ? c.buffer.decode(state) : null
|
|
1282
|
+
value: (flags & 64) !== 0 ? c.buffer.decode(state) : null,
|
|
1283
|
+
hash: (flags & 128) !== 0 ? c.fixed8.decode(state) : null
|
|
1267
1284
|
}
|
|
1268
1285
|
}
|
|
1269
1286
|
}
|
|
@@ -434,6 +434,11 @@
|
|
|
434
434
|
"array": true,
|
|
435
435
|
"type": "bool",
|
|
436
436
|
"version": 1
|
|
437
|
+
},
|
|
438
|
+
{
|
|
439
|
+
"name": "hash",
|
|
440
|
+
"type": "fixed8",
|
|
441
|
+
"version": 1
|
|
437
442
|
}
|
|
438
443
|
]
|
|
439
444
|
},
|
|
@@ -1018,6 +1023,11 @@
|
|
|
1018
1023
|
"name": "value",
|
|
1019
1024
|
"type": "buffer",
|
|
1020
1025
|
"version": 1
|
|
1026
|
+
},
|
|
1027
|
+
{
|
|
1028
|
+
"name": "hash",
|
|
1029
|
+
"type": "fixed8",
|
|
1030
|
+
"version": 1
|
|
1021
1031
|
}
|
|
1022
1032
|
]
|
|
1023
1033
|
}
|
package/index.js
CHANGED
|
@@ -437,15 +437,17 @@ module.exports = class Autobee extends ReadyResource {
|
|
|
437
437
|
}
|
|
438
438
|
|
|
439
439
|
// the view an oplog head points at, opened and closed through the handlers
|
|
440
|
-
openViewAt(oplog) {
|
|
440
|
+
openViewAt(oplog, { timeout } = {}) {
|
|
441
441
|
const v = oplog.op.views.view
|
|
442
442
|
if (!v) return null
|
|
443
443
|
|
|
444
|
-
return this.openView({ key: v.key, length: v.start + v.length })
|
|
444
|
+
return this.openView({ key: v.key, length: v.start + v.length }, { timeout })
|
|
445
445
|
}
|
|
446
446
|
|
|
447
|
-
|
|
448
|
-
|
|
447
|
+
// timeout bounds every hypercore read done through the view, defaults to the main bee's
|
|
448
|
+
openView(head, { timeout } = {}) {
|
|
449
|
+
const bee = this.bee.checkout({ key: head.key, length: head.length, timeout })
|
|
450
|
+
return new ApplyView(bee, this)
|
|
449
451
|
}
|
|
450
452
|
|
|
451
453
|
openCore(key) {
|
|
@@ -1196,9 +1198,14 @@ module.exports = class Autobee extends ReadyResource {
|
|
|
1196
1198
|
node.weight = await resolveWeight(this, node)
|
|
1197
1199
|
for (const n of batch) n.weight = node.weight
|
|
1198
1200
|
|
|
1199
|
-
|
|
1200
|
-
|
|
1201
|
-
|
|
1201
|
+
if (
|
|
1202
|
+
node.hash &&
|
|
1203
|
+
this.system.hash &&
|
|
1204
|
+
b4a.equals(node.hash, this.system.hash) &&
|
|
1205
|
+
topo.isLinkingAll(node, this.system.heads)
|
|
1206
|
+
) {
|
|
1207
|
+
return { undo: null, view: null, tip: [batch] }
|
|
1208
|
+
}
|
|
1202
1209
|
|
|
1203
1210
|
const t = await topo.sort(this, batch)
|
|
1204
1211
|
|
|
@@ -1352,6 +1359,7 @@ module.exports = class Autobee extends ReadyResource {
|
|
|
1352
1359
|
await this.local.ready()
|
|
1353
1360
|
|
|
1354
1361
|
const links = this.system.getLinks(this.local.key)
|
|
1362
|
+
const hash = this.system.hash
|
|
1355
1363
|
|
|
1356
1364
|
// never stamp before anything we link
|
|
1357
1365
|
const t = Math.max(this._now(), this.system.timestamp)
|
|
@@ -1379,7 +1387,8 @@ module.exports = class Autobee extends ReadyResource {
|
|
|
1379
1387
|
lnk,
|
|
1380
1388
|
optimistic,
|
|
1381
1389
|
i === 0 ? witness : null,
|
|
1382
|
-
i === 0 ? approvals : null
|
|
1390
|
+
i === 0 ? approvals : null,
|
|
1391
|
+
i === 0 ? hash : null
|
|
1383
1392
|
)
|
|
1384
1393
|
batch.push(node)
|
|
1385
1394
|
}
|
package/lib/fast-forward.js
CHANGED
|
@@ -56,7 +56,9 @@ class FastForward {
|
|
|
56
56
|
return null
|
|
57
57
|
}
|
|
58
58
|
|
|
59
|
-
return new FastForward(auto, batchToHead(verified.op.views.system)
|
|
59
|
+
return new FastForward(auto, batchToHead(verified.op.views.system), {
|
|
60
|
+
timeout: timeout || DEFAULT_OP_TIMEOUT
|
|
61
|
+
})
|
|
60
62
|
}
|
|
61
63
|
|
|
62
64
|
static async fromHeads(auto, heads, { force = false, timeout = 0 } = {}) {
|
|
@@ -107,7 +109,7 @@ class FastForward {
|
|
|
107
109
|
candidates.sort((a, b) => b.op.views.flushes - a.op.views.flushes)
|
|
108
110
|
|
|
109
111
|
for (const res of candidates) {
|
|
110
|
-
const trusted = await FastForward.mostRecentTrusted(auto, res, reference)
|
|
112
|
+
const trusted = await FastForward.mostRecentTrusted(auto, res, reference, timeout)
|
|
111
113
|
if (trusted === null) continue
|
|
112
114
|
if (auto.fastForwarding || auto.fastForwardTo) return null
|
|
113
115
|
|
|
@@ -130,8 +132,8 @@ class FastForward {
|
|
|
130
132
|
}
|
|
131
133
|
}
|
|
132
134
|
|
|
133
|
-
static async mostRecentTrusted(auto, head, reference) {
|
|
134
|
-
const opened = auto.openViewAt(head)
|
|
135
|
+
static async mostRecentTrusted(auto, head, reference, timeout) {
|
|
136
|
+
const opened = auto.openViewAt(head, { timeout })
|
|
135
137
|
if (opened === null) return null
|
|
136
138
|
|
|
137
139
|
try {
|
|
@@ -206,7 +208,7 @@ class FastForward {
|
|
|
206
208
|
_startWarmup() {
|
|
207
209
|
if (!this.auto._warmup || !this.system.view.length) return false
|
|
208
210
|
|
|
209
|
-
this.warmupView = this.auto.openView(this.system.view)
|
|
211
|
+
this.warmupView = this.auto.openView(this.system.view, { timeout: this.timeout })
|
|
210
212
|
|
|
211
213
|
// ensure thenable
|
|
212
214
|
this.warmup = Promise.resolve(this.auto._warmup(this.warmupView.view))
|
package/lib/system.js
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
const Hyperbee = require('hyperbee2')
|
|
2
2
|
const Promotions = require('./promotions.js')
|
|
3
|
+
const sodium = require('sodium-native')
|
|
4
|
+
const c = require('compact-encoding')
|
|
3
5
|
const b4a = require('b4a')
|
|
4
6
|
|
|
5
7
|
const { getViewMap, getCoreManifest } = require('./migrations.js')
|
|
@@ -13,6 +15,10 @@ const INFO_LEGACY_KEY = b4a.concat([b4a.from([0, 0]), b4a.from('info')])
|
|
|
13
15
|
const EMPTY_HEAD = { length: 0, key: null }
|
|
14
16
|
const EMPTY = b4a.from([])
|
|
15
17
|
|
|
18
|
+
const HASH = c.fixed(sodium.crypto_shorthash_BYTES)
|
|
19
|
+
const HASH_KEY = b4a.from('autobee-orderkey') // crypto_shorthash_KEYBYTES
|
|
20
|
+
const HASH_SEED = b4a.alloc(sodium.crypto_shorthash_BYTES)
|
|
21
|
+
|
|
16
22
|
class SystemSnapshot {
|
|
17
23
|
constructor(bee) {
|
|
18
24
|
this.bee = bee
|
|
@@ -41,6 +47,7 @@ module.exports = class Systembee {
|
|
|
41
47
|
this.bee = new Hyperbee(store, opts)
|
|
42
48
|
this.view = null
|
|
43
49
|
this.heads = []
|
|
50
|
+
this.hash = null
|
|
44
51
|
this.migration = null
|
|
45
52
|
this.version = 0
|
|
46
53
|
this.timestamp = 0
|
|
@@ -142,6 +149,7 @@ module.exports = class Systembee {
|
|
|
142
149
|
this.view = info ? info.view : EMPTY_HEAD
|
|
143
150
|
this.migration = null
|
|
144
151
|
this.heads = info ? info.heads : []
|
|
152
|
+
this.hash = info ? info.hash : null
|
|
145
153
|
this.timestamp = info ? info.timestamp : 0
|
|
146
154
|
this.flushes = info ? info.flushes : 0
|
|
147
155
|
|
|
@@ -224,6 +232,8 @@ module.exports = class Systembee {
|
|
|
224
232
|
this.heads.push({ key: node.key, length: node.length })
|
|
225
233
|
if (node.timestamp > this.timestamp) this.timestamp = node.timestamp // TODO: support smoothing
|
|
226
234
|
|
|
235
|
+
this.hash = advanceHash(this.hash, node)
|
|
236
|
+
|
|
227
237
|
// resolved sort weight, re-stamped on every reapplication - this is what
|
|
228
238
|
// topo reads back out of the changes stream
|
|
229
239
|
this.update(node.key, node.length, node.weight, -1, false, false, false, node.timestamp, null)
|
|
@@ -443,7 +453,8 @@ module.exports = class Systembee {
|
|
|
443
453
|
timestamp: this.timestamp,
|
|
444
454
|
flushes: this.flushes,
|
|
445
455
|
indexers: null, // legacy
|
|
446
|
-
pending: this.promotions.digest
|
|
456
|
+
pending: this.promotions.digest,
|
|
457
|
+
hash: this.hash
|
|
447
458
|
}
|
|
448
459
|
|
|
449
460
|
w.tryPut(INFO_KEY, encoding.encodeSystemInfo(info))
|
|
@@ -488,3 +499,24 @@ module.exports = class Systembee {
|
|
|
488
499
|
return this.promotions.hint(key, opts)
|
|
489
500
|
}
|
|
490
501
|
}
|
|
502
|
+
|
|
503
|
+
function advanceHash(prev, node) {
|
|
504
|
+
const state = { start: 0, end: 0, buffer: null }
|
|
505
|
+
|
|
506
|
+
HASH.preencode(state, prev || HASH_SEED)
|
|
507
|
+
c.fixed32.preencode(state, node.key)
|
|
508
|
+
c.uint.preencode(state, node.length)
|
|
509
|
+
c.uint.preencode(state, node.weight)
|
|
510
|
+
|
|
511
|
+
state.buffer = b4a.allocUnsafe(state.end)
|
|
512
|
+
|
|
513
|
+
HASH.encode(state, prev || HASH_SEED)
|
|
514
|
+
c.fixed32.encode(state, node.key)
|
|
515
|
+
c.uint.encode(state, node.length)
|
|
516
|
+
c.uint.encode(state, node.weight)
|
|
517
|
+
|
|
518
|
+
const hash = b4a.allocUnsafe(sodium.crypto_shorthash_BYTES)
|
|
519
|
+
sodium.crypto_shorthash(hash, state.buffer, HASH_KEY)
|
|
520
|
+
|
|
521
|
+
return hash
|
|
522
|
+
}
|
package/lib/writers.js
CHANGED
|
@@ -86,8 +86,17 @@ class ActiveWriters {
|
|
|
86
86
|
return this.localWriter.getLatest()
|
|
87
87
|
}
|
|
88
88
|
|
|
89
|
-
appendLocal(value, timestamp, batch, links, optimistic, witness, approvals = null) {
|
|
90
|
-
return this.localWriter.append(
|
|
89
|
+
appendLocal(value, timestamp, batch, links, optimistic, witness, approvals = null, hash = null) {
|
|
90
|
+
return this.localWriter.append(
|
|
91
|
+
value,
|
|
92
|
+
timestamp,
|
|
93
|
+
batch,
|
|
94
|
+
links,
|
|
95
|
+
optimistic,
|
|
96
|
+
witness,
|
|
97
|
+
approvals,
|
|
98
|
+
hash
|
|
99
|
+
)
|
|
91
100
|
}
|
|
92
101
|
|
|
93
102
|
async rotateLocalWriter(core) {
|
|
@@ -693,7 +702,7 @@ class Writer {
|
|
|
693
702
|
return this.pending !== null
|
|
694
703
|
}
|
|
695
704
|
|
|
696
|
-
append(value, timestamp, batch, links, optimistic, witness, approvals = null) {
|
|
705
|
+
append(value, timestamp, batch, links, optimistic, witness, approvals = null, hash = null) {
|
|
697
706
|
if (this.pending === null) this.pending = []
|
|
698
707
|
|
|
699
708
|
const oplog = {
|
|
@@ -706,7 +715,8 @@ class Writer {
|
|
|
706
715
|
witness: witness || null,
|
|
707
716
|
approvals,
|
|
708
717
|
optimistic: optimistic && !this.writers.writable,
|
|
709
|
-
value
|
|
718
|
+
value,
|
|
719
|
+
hash
|
|
710
720
|
}
|
|
711
721
|
|
|
712
722
|
const node = createNode(this.core, this.appendLength++, -1, oplog)
|
|
@@ -783,7 +793,8 @@ function createNode(core, length, weight, oplog) {
|
|
|
783
793
|
witness: oplog.witness || null,
|
|
784
794
|
approvals: oplog.approvals || null,
|
|
785
795
|
optimistic: oplog.optimistic,
|
|
786
|
-
value: oplog.value
|
|
796
|
+
value: oplog.value,
|
|
797
|
+
hash: oplog.hash || null
|
|
787
798
|
}
|
|
788
799
|
}
|
|
789
800
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "autobee",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.7.1",
|
|
4
4
|
"description": "Bee based autobase",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"author": "Holepunch Inc.",
|
|
@@ -27,7 +27,7 @@
|
|
|
27
27
|
"autobee-wakeup": "^1.1.3",
|
|
28
28
|
"b4a": "^1.8.0",
|
|
29
29
|
"compact-encoding": "^3.3.0",
|
|
30
|
-
"hyperbee2": "^2.
|
|
30
|
+
"hyperbee2": "^2.16.0",
|
|
31
31
|
"hypercore": "^11.33.2",
|
|
32
32
|
"hypercore-id-encoding": "^1.3.0",
|
|
33
33
|
"hyperschema": "^1.20.1",
|
|
@@ -35,7 +35,8 @@
|
|
|
35
35
|
"ready-guard": "^1.0.0",
|
|
36
36
|
"ready-resource": "^1.2.0",
|
|
37
37
|
"resolve-reject-promise": "^1.1.0",
|
|
38
|
-
"safety-catch": "^1.0.3"
|
|
38
|
+
"safety-catch": "^1.0.3",
|
|
39
|
+
"sodium-native": "^5.1.0"
|
|
39
40
|
},
|
|
40
41
|
"devDependencies": {
|
|
41
42
|
"brittle": "^4.1.0",
|