autobee 2.0.0-rc.9 → 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 CHANGED
@@ -78,7 +78,10 @@ Options:
78
78
  encryptionKey: Buffer, // 32-byte key to encrypt all data at rest
79
79
  encrypted: false, // set true if using encryptionKey
80
80
  keyPair: { publicKey, secretKey }, // custom signing key pair for the local writer
81
- optimistic: true // allow optimistic writes from unknown writers
81
+ optimistic: true, // allow optimistic writes from unknown writers
82
+ isTrusted (key, reference) {}, // do we trust this writer, see Fast-forward
83
+ mostRecentTrusted (target, reference) {}, // the head we vouch for, see Fast-forward
84
+ fastForward: {} // see Fast-forward
82
85
  }
83
86
  ```
84
87
 
@@ -222,6 +225,50 @@ Create an anchor node. Returns `{ key, length }`. Anchors are used to create a v
222
225
 
223
226
  `true` if the system has not yet processed any nodes. Use this to bootstrap the first writer.
224
227
 
228
+ ### Fast-forward
229
+
230
+ Fast-forward only ever deals in oplog heads — `{ key, length }` of a writer's core, never a system head.
231
+
232
+ Each flush stamps the head you vouch for into your own oplog, and peers read those stamps out of the writers they wake up on, so trust travels with the log.
233
+
234
+ #### `isTrusted(key, reference)`
235
+
236
+ Return whether `key` is a writer you trust, judged against the `reference` view.
237
+
238
+ Positive answers are cached until an undo rewinds the view, and the default is `true` unless you supply `mostRecentTrusted`, in which case it is `false`.
239
+
240
+ #### `mostRecentTrusted(target, reference)`
241
+
242
+ Return the oplog head you most recently vouched for, given the `target` view being considered.
243
+
244
+ Called at flush time to stamp your own oplog (with your view as `target` and a `null` reference), and again per candidate during discovery.
245
+
246
+ #### `fastForward.boot`
247
+
248
+ ```js
249
+ {
250
+ head: { key, length }, // oplog head to boot from
251
+ legacy: { key, length }, // pre-2.0 pointer, see below
252
+ bootCondition (target, reference) {} // optional gate on the view we would land on
253
+ }
254
+ ```
255
+
256
+ Pass one of `head` or `legacy`, not both.
257
+
258
+ Without `bootCondition` this is a single attempt that gives up if the head cannot be read; with one it parks until the condition is satisfied, which today means indefinitely if it never is.
259
+
260
+ `legacy` is for records written before boot heads were oplog heads: the key is a _system_ head and a `0` length is resolved from the core. It boots ungated - `bootCondition` does not apply - and will be removed, so don't reach for it. A bare `{ key, length }` in place of the whole struct means the same thing, older still.
261
+
262
+ #### `fastForward.conservative`
263
+
264
+ Defaults to `true`: only fast-forward onto a head a connected peer can serve whole.
265
+
266
+ The check covers the oplog head only, not the system and view cores the fast-forward then reads.
267
+
268
+ #### `await db.moveTo(head)`
269
+
270
+ Fast-forward onto an oplog head, ignoring the usual distance and conservative checks. Resolves `{ to, from }`.
271
+
225
272
  ### Encryption
226
273
 
227
274
  Pass an `encryptionKey` to encrypt all writer cores and the view at rest.
@@ -280,8 +280,8 @@ const SystemWriterV0 = {
280
280
 
281
281
  return {
282
282
  version: 0,
283
- isIndexer: flags & 1,
284
- isRemoved: flags & 2,
283
+ isIndexer: (flags & 1) !== 0,
284
+ isRemoved: (flags & 2) !== 0,
285
285
  length: c.uint.decode(state)
286
286
  }
287
287
  }
@@ -300,11 +300,35 @@ function infoLegacyMap(info) {
300
300
  }
301
301
 
302
302
  function memberLegacyMap(m) {
303
+ const weight = m.isIndexer ? 2 : 1
304
+
303
305
  return {
304
306
  version: 0,
305
307
  isRemoved: m.isRemoved,
306
- weight: m.isIndexer ? 2 : 1,
307
- length: m.length
308
+ isOplog: false,
309
+ isGenesis: false,
310
+ weight,
311
+ maxWeight: weight,
312
+ length: m.length,
313
+ timestamp: 0
314
+ }
315
+ }
316
+
317
+ // a v3 node's witness is a signed attestation, which confers weight through a
318
+ // mechanism that no longer exists - it maps to null, so the node sorts at the
319
+ // standing its record already carries instead of failing to decode
320
+ function oplogLegacyMap(m) {
321
+ return {
322
+ version: m.version,
323
+ timestamp: m.timestamp,
324
+ links: m.links,
325
+ batch: m.batch,
326
+ views: m.views,
327
+ trusted: m.trusted,
328
+ witness: null,
329
+ approvals: null,
330
+ optimistic: m.optimistic,
331
+ value: m.value
308
332
  }
309
333
  }
310
334
 
@@ -314,5 +338,6 @@ module.exports = {
314
338
  OplogMessageV1,
315
339
  SystemWriterV0,
316
340
  infoLegacyMap,
317
- memberLegacyMap
341
+ memberLegacyMap,
342
+ oplogLegacyMap
318
343
  }
@@ -12,6 +12,36 @@ const VERSION = 1
12
12
  // eslint-disable-next-line no-unused-vars
13
13
  let version = VERSION
14
14
 
15
+ // array of bools, packed as a bitfield
16
+ const boolArray = {
17
+ preencode(state, m) {
18
+ c.uint.preencode(state, m.length)
19
+ state.end += Math.ceil(m.length / 8)
20
+ },
21
+ encode(state, m) {
22
+ c.uint.encode(state, m.length)
23
+ for (let i = 0; i < m.length; i += 8) {
24
+ let byte = 0
25
+ for (let j = 0; j < 8 && i + j < m.length; j++) {
26
+ if (m[i + j]) byte |= 1 << j
27
+ }
28
+ state.buffer[state.start++] = byte
29
+ }
30
+ },
31
+ decode(state) {
32
+ const n = c.uint.decode(state)
33
+ if (state.end - state.start < Math.ceil(n / 8)) throw new Error('Out of bounds')
34
+ const m = new Array(n)
35
+ for (let i = 0; i < n; i += 8) {
36
+ const byte = state.buffer[state.start++]
37
+ for (let j = 0; j < 8 && i + j < n; j++) {
38
+ m[i + j] = (byte & (1 << j)) !== 0
39
+ }
40
+ }
41
+ return m
42
+ }
43
+ }
44
+
15
45
  // @autobase-compat/checkout
16
46
  const encoding0 = {
17
47
  preencode(state, m) {
@@ -454,6 +484,9 @@ const encoding16 = {
454
484
 
455
485
  const encoding17 = external0.SystemWriterV0
456
486
 
487
+ // @autobee/system-info-v3.pending
488
+ const encoding18_5 = boolArray
489
+
457
490
  // @autobee/system-info-v3
458
491
  const encoding18 = {
459
492
  preencode(state, m) {
@@ -461,12 +494,13 @@ const encoding18 = {
461
494
  c.uint.preencode(state, m.flushes)
462
495
  encoding22.preencode(state, m.view)
463
496
  encoding18_3.preencode(state, m.heads)
464
- state.end++ // max flag is 1 so always one byte
497
+ state.end++ // max flag is 2 so always one byte
465
498
 
466
499
  if (m.indexers) encoding18_4.preencode(state, m.indexers)
500
+ if (m.pending) encoding18_5.preencode(state, m.pending)
467
501
  },
468
502
  encode(state, m) {
469
- const flags = m.indexers ? 1 : 0
503
+ const flags = (m.indexers ? 1 : 0) | (m.pending ? 2 : 0)
470
504
 
471
505
  c.uint.encode(state, m.timestamp)
472
506
  c.uint.encode(state, m.flushes)
@@ -475,6 +509,7 @@ const encoding18 = {
475
509
  c.uint.encode(state, flags)
476
510
 
477
511
  if (m.indexers) encoding18_4.encode(state, m.indexers)
512
+ if (m.pending) encoding18_5.encode(state, m.pending)
478
513
  },
479
514
  decode(state) {
480
515
  const v = c.uint.decode(state)
@@ -490,7 +525,8 @@ const encoding18 = {
490
525
  flushes: r1,
491
526
  view: r2,
492
527
  heads: r3,
493
- indexers: (flags & 1) !== 0 ? encoding18_4.decode(state) : null
528
+ indexers: (flags & 1) !== 0 ? encoding18_4.decode(state) : null,
529
+ pending: (flags & 2) !== 0 ? encoding18_5.decode(state) : null
494
530
  }
495
531
  }
496
532
  }
@@ -607,7 +643,7 @@ const encoding20 = {
607
643
  // @autobee/system-writer
608
644
  const encoding21 = {
609
645
  preencode(state, m) {
610
- const v = m.version ?? 4
646
+ const v = m.version ?? 5
611
647
  c.uint.preencode(state, v)
612
648
  switch (v) {
613
649
  case 0:
@@ -619,12 +655,15 @@ const encoding21 = {
619
655
  case 4:
620
656
  encoding20.preencode(state, m)
621
657
  break
658
+ case 5:
659
+ encoding34.preencode(state, m)
660
+ break
622
661
  default:
623
662
  throw new Error('Unsupported version')
624
663
  }
625
664
  },
626
665
  encode(state, m) {
627
- const v = m.version ?? 4
666
+ const v = m.version ?? 5
628
667
  c.uint.encode(state, v)
629
668
  switch (v) {
630
669
  case 0:
@@ -636,6 +675,9 @@ const encoding21 = {
636
675
  case 4:
637
676
  encoding20.encode(state, m)
638
677
  break
678
+ case 5:
679
+ encoding34.encode(state, m)
680
+ break
639
681
  default:
640
682
  throw new Error('Unsupported version')
641
683
  }
@@ -657,6 +699,10 @@ const encoding21 = {
657
699
  const decoded = encoding20.decode(state)
658
700
  return decoded
659
701
  }
702
+ case 5: {
703
+ const decoded = encoding34.decode(state)
704
+ return decoded
705
+ }
660
706
  default:
661
707
  throw new Error('Unsupported version')
662
708
  }
@@ -856,15 +902,28 @@ const encoding29_7 = c.array(c.frame(encoding28))
856
902
  // @autobee/oplog-message-v3
857
903
  const encoding29 = {
858
904
  preencode(state, m) {
905
+ let flags =
906
+ (m.batch ? 1 : 0) |
907
+ (m.views ? 2 : 0) |
908
+ (m.optimistic ? 8 : 0) |
909
+ (m.value ? 16 : 0) |
910
+ (m.witness ? 32 : 0) |
911
+ (m.attestations ? 64 : 0) |
912
+ (m.trusted ? 128 : 0)
913
+ if (m.views) {
914
+ flags |= m.views.view ? 4 : 0
915
+ }
916
+
859
917
  c.uint.preencode(state, m.timestamp)
860
918
  encoding29_1.preencode(state, m.links)
861
- state.end++ // max flag is 64 so always one byte
919
+ c.uint.preencode(state, flags)
862
920
 
863
921
  if (m.batch) encoding24.preencode(state, m.batch)
864
922
  if (m.views) encoding25_inline.preencode(state, m.views)
865
923
  if (m.value) c.buffer.preencode(state, m.value)
866
924
  if (m.witness) encoding29_6.preencode(state, m.witness)
867
925
  if (m.attestations) encoding29_7.preencode(state, m.attestations)
926
+ if (m.trusted) encoding29_8.preencode(state, m.trusted)
868
927
  },
869
928
  encode(state, m) {
870
929
  let flags =
@@ -873,7 +932,8 @@ const encoding29 = {
873
932
  (m.optimistic ? 8 : 0) |
874
933
  (m.value ? 16 : 0) |
875
934
  (m.witness ? 32 : 0) |
876
- (m.attestations ? 64 : 0)
935
+ (m.attestations ? 64 : 0) |
936
+ (m.trusted ? 128 : 0)
877
937
  if (m.views) {
878
938
  flags |= m.views.view ? 4 : 0
879
939
  }
@@ -887,6 +947,7 @@ const encoding29 = {
887
947
  if (m.value) c.buffer.encode(state, m.value)
888
948
  if (m.witness) encoding29_6.encode(state, m.witness)
889
949
  if (m.attestations) encoding29_7.encode(state, m.attestations)
950
+ if (m.trusted) encoding29_8.encode(state, m.trusted)
890
951
  },
891
952
  decode(state) {
892
953
  const v = c.uint.decode(state)
@@ -903,7 +964,8 @@ const encoding29 = {
903
964
  optimistic: (flags & 8) !== 0,
904
965
  value: (flags & 16) !== 0 ? c.buffer.decode(state) : null,
905
966
  witness: (flags & 32) !== 0 ? encoding29_6.decode(state) : null,
906
- attestations: (flags & 64) !== 0 ? encoding29_7.decode(state) : null
967
+ attestations: (flags & 64) !== 0 ? encoding29_7.decode(state) : null,
968
+ trusted: (flags & 128) !== 0 ? encoding29_8.decode(state) : null
907
969
  }
908
970
  }
909
971
  }
@@ -911,7 +973,7 @@ const encoding29 = {
911
973
  // @autobee/oplog
912
974
  const encoding30 = {
913
975
  preencode(state, m) {
914
- const v = m.version ?? 3
976
+ const v = m.version ?? 4
915
977
  c.uint.preencode(state, v)
916
978
  switch (v) {
917
979
  case 0:
@@ -926,12 +988,15 @@ const encoding30 = {
926
988
  case 3:
927
989
  encoding29.preencode(state, m)
928
990
  break
991
+ case 4:
992
+ encoding37.preencode(state, m)
993
+ break
929
994
  default:
930
995
  throw new Error('Unsupported version')
931
996
  }
932
997
  },
933
998
  encode(state, m) {
934
- const v = m.version ?? 3
999
+ const v = m.version ?? 4
935
1000
  c.uint.encode(state, v)
936
1001
  switch (v) {
937
1002
  case 0:
@@ -946,6 +1011,9 @@ const encoding30 = {
946
1011
  case 3:
947
1012
  encoding29.encode(state, m)
948
1013
  break
1014
+ case 4:
1015
+ encoding37.encode(state, m)
1016
+ break
949
1017
  default:
950
1018
  throw new Error('Unsupported version')
951
1019
  }
@@ -969,6 +1037,11 @@ const encoding30 = {
969
1037
  }
970
1038
  case 3: {
971
1039
  const decoded = encoding29.decode(state)
1040
+ const map = external0.oplogLegacyMap
1041
+ return map(decoded)
1042
+ }
1043
+ case 4: {
1044
+ const decoded = encoding37.decode(state)
972
1045
  return decoded
973
1046
  }
974
1047
  default:
@@ -1007,12 +1080,206 @@ const encoding31 = {
1007
1080
  }
1008
1081
  }
1009
1082
 
1083
+ // @autobee/trusted-head
1084
+ const encoding32 = {
1085
+ preencode(state, m) {
1086
+ c.fixed32.preencode(state, m.key)
1087
+ c.uint.preencode(state, m.length)
1088
+ c.uint.preencode(state, m.flushes)
1089
+ },
1090
+ encode(state, m) {
1091
+ c.fixed32.encode(state, m.key)
1092
+ c.uint.encode(state, m.length)
1093
+ c.uint.encode(state, m.flushes)
1094
+ },
1095
+ decode(state) {
1096
+ const r0 = c.fixed32.decode(state)
1097
+ const r1 = c.uint.decode(state)
1098
+ const r2 = c.uint.decode(state)
1099
+
1100
+ return {
1101
+ key: r0,
1102
+ length: r1,
1103
+ flushes: r2
1104
+ }
1105
+ }
1106
+ }
1107
+
1108
+ // @autobee/migrated-head
1109
+ const encoding33 = {
1110
+ preencode(state, m) {
1111
+ encoding22.preencode(state, m.system)
1112
+ state.end++ // flags are fixed size
1113
+
1114
+ if (m.view) encoding22.preencode(state, m.view)
1115
+ },
1116
+ encode(state, m) {
1117
+ const flags = m.view ? 1 : 0
1118
+
1119
+ encoding22.encode(state, m.system)
1120
+ c.uint8.encode(state, flags)
1121
+
1122
+ if (m.view) encoding22.encode(state, m.view)
1123
+ },
1124
+ decode(state) {
1125
+ const r0 = encoding22.decode(state)
1126
+ const flags = c.uint8.decode(state)
1127
+
1128
+ return {
1129
+ system: r0,
1130
+ view: (flags & 1) !== 0 ? encoding22.decode(state) : null
1131
+ }
1132
+ }
1133
+ }
1134
+
1135
+ // @autobee/system-writer-v5
1136
+ const encoding34 = {
1137
+ preencode(state, m) {
1138
+ state.end++ // max flag is 4 so always one byte
1139
+ c.uint.preencode(state, m.weight)
1140
+ c.uint.preencode(state, m.maxWeight)
1141
+ c.uint.preencode(state, m.length)
1142
+ c.uint.preencode(state, m.timestamp)
1143
+ },
1144
+ encode(state, m) {
1145
+ const flags = (m.isRemoved ? 1 : 0) | (m.isOplog ? 2 : 0) | (m.isGenesis ? 4 : 0)
1146
+
1147
+ c.uint.encode(state, flags)
1148
+ c.uint.encode(state, m.weight)
1149
+ c.uint.encode(state, m.maxWeight)
1150
+ c.uint.encode(state, m.length)
1151
+ c.uint.encode(state, m.timestamp)
1152
+ },
1153
+ decode(state) {
1154
+ const v = c.uint.decode(state)
1155
+ const flags = c.uint.decode(state)
1156
+
1157
+ return {
1158
+ version: v,
1159
+ isRemoved: (flags & 1) !== 0,
1160
+ isOplog: (flags & 2) !== 0,
1161
+ isGenesis: (flags & 4) !== 0,
1162
+ weight: c.uint.decode(state),
1163
+ maxWeight: c.uint.decode(state),
1164
+ length: c.uint.decode(state),
1165
+ timestamp: c.uint.decode(state)
1166
+ }
1167
+ }
1168
+ }
1169
+
1170
+ // @autobee/grant-witness
1171
+ const encoding35 = {
1172
+ preencode(state, m) {
1173
+ c.uint.preencode(state, m.weight)
1174
+ encoding22.preencode(state, m.link)
1175
+ },
1176
+ encode(state, m) {
1177
+ c.uint.encode(state, m.weight)
1178
+ encoding22.encode(state, m.link)
1179
+ },
1180
+ decode(state) {
1181
+ const r0 = c.uint.decode(state)
1182
+ const r1 = encoding22.decode(state)
1183
+
1184
+ return {
1185
+ weight: r0,
1186
+ link: r1
1187
+ }
1188
+ }
1189
+ }
1190
+
1191
+ // @autobee/approval
1192
+ const encoding36 = {
1193
+ preencode(state, m) {
1194
+ c.fixed32.preencode(state, m.key)
1195
+ c.uint.preencode(state, m.weight)
1196
+ },
1197
+ encode(state, m) {
1198
+ c.fixed32.encode(state, m.key)
1199
+ c.uint.encode(state, m.weight)
1200
+ },
1201
+ decode(state) {
1202
+ const r0 = c.fixed32.decode(state)
1203
+ const r1 = c.uint.decode(state)
1204
+
1205
+ return {
1206
+ key: r0,
1207
+ weight: r1
1208
+ }
1209
+ }
1210
+ }
1211
+
1212
+ // @autobee/oplog-message-v4.approvals
1213
+ const encoding37_6 = c.array(encoding36)
1214
+
1215
+ // @autobee/oplog-message-v4
1216
+ const encoding37 = {
1217
+ preencode(state, m) {
1218
+ c.uint.preencode(state, m.timestamp)
1219
+ encoding37_1.preencode(state, m.links)
1220
+ state.end++ // max flag is 64 so always one byte
1221
+
1222
+ if (m.batch) encoding24.preencode(state, m.batch)
1223
+ if (m.views) encoding25.preencode(state, m.views)
1224
+ if (m.trusted) encoding37_4.preencode(state, m.trusted)
1225
+ if (m.witness) encoding35.preencode(state, m.witness)
1226
+ if (m.approvals) encoding37_6.preencode(state, m.approvals)
1227
+ if (m.value) c.buffer.preencode(state, m.value)
1228
+ },
1229
+ encode(state, m) {
1230
+ const flags =
1231
+ (m.batch ? 1 : 0) |
1232
+ (m.views ? 2 : 0) |
1233
+ (m.trusted ? 4 : 0) |
1234
+ (m.witness ? 8 : 0) |
1235
+ (m.approvals ? 16 : 0) |
1236
+ (m.optimistic ? 32 : 0) |
1237
+ (m.value ? 64 : 0)
1238
+
1239
+ c.uint.encode(state, m.timestamp)
1240
+ encoding37_1.encode(state, m.links)
1241
+ c.uint.encode(state, flags)
1242
+
1243
+ if (m.batch) encoding24.encode(state, m.batch)
1244
+ if (m.views) encoding25.encode(state, m.views)
1245
+ if (m.trusted) encoding37_4.encode(state, m.trusted)
1246
+ if (m.witness) encoding35.encode(state, m.witness)
1247
+ if (m.approvals) encoding37_6.encode(state, m.approvals)
1248
+ if (m.value) c.buffer.encode(state, m.value)
1249
+ },
1250
+ decode(state) {
1251
+ const v = c.uint.decode(state)
1252
+ const r0 = c.uint.decode(state)
1253
+ const r1 = encoding37_1.decode(state)
1254
+ const flags = c.uint.decode(state)
1255
+
1256
+ return {
1257
+ version: v,
1258
+ timestamp: r0,
1259
+ links: r1,
1260
+ batch: (flags & 1) !== 0 ? encoding24.decode(state) : null,
1261
+ views: (flags & 2) !== 0 ? encoding25.decode(state) : null,
1262
+ trusted: (flags & 4) !== 0 ? encoding37_4.decode(state) : null,
1263
+ witness: (flags & 8) !== 0 ? encoding35.decode(state) : null,
1264
+ approvals: (flags & 16) !== 0 ? encoding37_6.decode(state) : null,
1265
+ optimistic: (flags & 32) !== 0,
1266
+ value: (flags & 64) !== 0 ? c.buffer.decode(state) : null
1267
+ }
1268
+ }
1269
+ }
1270
+
1010
1271
  // @autobee/system-info-v3.heads, deferred due to recusive use
1011
1272
  const encoding18_3 = c.array(encoding22)
1012
1273
  // @autobee/system-info-v3.indexers, deferred due to recusive use
1013
1274
  const encoding18_4 = encoding18_3
1014
1275
  // @autobee/oplog-message-v3.links, deferred due to recusive use
1015
1276
  const encoding29_1 = encoding18_3
1277
+ // @autobee/oplog-message-v3.trusted, deferred due to recusive use
1278
+ const encoding29_8 = c.array(c.frame(encoding32))
1279
+ // @autobee/oplog-message-v4.links, deferred due to recusive use
1280
+ const encoding37_1 = encoding18_3
1281
+ // @autobee/oplog-message-v4.trusted, deferred due to recusive use
1282
+ const encoding37_4 = encoding18_3
1016
1283
 
1017
1284
  function setVersion(v) {
1018
1285
  version = v
@@ -1101,6 +1368,18 @@ function getEncoding(name) {
1101
1368
  return encoding30
1102
1369
  case '@autobee/manifest-data':
1103
1370
  return encoding31
1371
+ case '@autobee/trusted-head':
1372
+ return encoding32
1373
+ case '@autobee/migrated-head':
1374
+ return encoding33
1375
+ case '@autobee/system-writer-v5':
1376
+ return encoding34
1377
+ case '@autobee/grant-witness':
1378
+ return encoding35
1379
+ case '@autobee/approval':
1380
+ return encoding36
1381
+ case '@autobee/oplog-message-v4':
1382
+ return encoding37
1104
1383
  default:
1105
1384
  throw new Error('Encoder not found ' + name)
1106
1385
  }