@optimystic/db-p2p 0.24.2 → 0.25.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.
Files changed (249) hide show
  1. package/{readme.md → README.md} +688 -668
  2. package/dist/src/cluster/block-transfer-service.d.ts +133 -10
  3. package/dist/src/cluster/block-transfer-service.d.ts.map +1 -1
  4. package/dist/src/cluster/block-transfer-service.js +155 -9
  5. package/dist/src/cluster/block-transfer-service.js.map +1 -1
  6. package/dist/src/cluster/block-transfer.d.ts +67 -7
  7. package/dist/src/cluster/block-transfer.d.ts.map +1 -1
  8. package/dist/src/cluster/block-transfer.js +127 -24
  9. package/dist/src/cluster/block-transfer.js.map +1 -1
  10. package/dist/src/cluster/certified-claims.d.ts +170 -0
  11. package/dist/src/cluster/certified-claims.d.ts.map +1 -0
  12. package/dist/src/cluster/certified-claims.js +200 -0
  13. package/dist/src/cluster/certified-claims.js.map +1 -0
  14. package/dist/src/cluster/client-signature-verifier.d.ts +23 -0
  15. package/dist/src/cluster/client-signature-verifier.d.ts.map +1 -0
  16. package/dist/src/cluster/client-signature-verifier.js +33 -0
  17. package/dist/src/cluster/client-signature-verifier.js.map +1 -0
  18. package/dist/src/cluster/cluster-policy.d.ts +9 -3
  19. package/dist/src/cluster/cluster-policy.d.ts.map +1 -1
  20. package/dist/src/cluster/cluster-policy.js +111 -27
  21. package/dist/src/cluster/cluster-policy.js.map +1 -1
  22. package/dist/src/cluster/cluster-repo.d.ts +140 -11
  23. package/dist/src/cluster/cluster-repo.d.ts.map +1 -1
  24. package/dist/src/cluster/cluster-repo.js +485 -29
  25. package/dist/src/cluster/cluster-repo.js.map +1 -1
  26. package/dist/src/cluster/commit-proof.d.ts +134 -0
  27. package/dist/src/cluster/commit-proof.d.ts.map +1 -0
  28. package/dist/src/cluster/commit-proof.js +263 -0
  29. package/dist/src/cluster/commit-proof.js.map +1 -0
  30. package/dist/src/cluster/quorum-restore.d.ts +95 -8
  31. package/dist/src/cluster/quorum-restore.d.ts.map +1 -1
  32. package/dist/src/cluster/quorum-restore.js +113 -14
  33. package/dist/src/cluster/quorum-restore.js.map +1 -1
  34. package/dist/src/cluster/rebalance-monitor.d.ts +84 -0
  35. package/dist/src/cluster/rebalance-monitor.d.ts.map +1 -1
  36. package/dist/src/cluster/rebalance-monitor.js +207 -5
  37. package/dist/src/cluster/rebalance-monitor.js.map +1 -1
  38. package/dist/src/cluster/reconcile-block.d.ts +27 -7
  39. package/dist/src/cluster/reconcile-block.d.ts.map +1 -1
  40. package/dist/src/cluster/reconcile-block.js +179 -38
  41. package/dist/src/cluster/reconcile-block.js.map +1 -1
  42. package/dist/src/cluster/service.d.ts +13 -4
  43. package/dist/src/cluster/service.d.ts.map +1 -1
  44. package/dist/src/cluster/service.js +17 -18
  45. package/dist/src/cluster/service.js.map +1 -1
  46. package/dist/src/cluster/spread-on-churn.d.ts +8 -2
  47. package/dist/src/cluster/spread-on-churn.d.ts.map +1 -1
  48. package/dist/src/cluster/spread-on-churn.js +7 -7
  49. package/dist/src/cluster/spread-on-churn.js.map +1 -1
  50. package/dist/src/cohort-topic/host.d.ts.map +1 -1
  51. package/dist/src/cohort-topic/host.js +8 -7
  52. package/dist/src/cohort-topic/host.js.map +1 -1
  53. package/dist/src/cohort-topic/stream-util.d.ts +7 -6
  54. package/dist/src/cohort-topic/stream-util.d.ts.map +1 -1
  55. package/dist/src/cohort-topic/stream-util.js +14 -41
  56. package/dist/src/cohort-topic/stream-util.js.map +1 -1
  57. package/dist/src/dispute/cascade.d.ts +7 -8
  58. package/dist/src/dispute/cascade.d.ts.map +1 -1
  59. package/dist/src/dispute/cascade.js +24 -7
  60. package/dist/src/dispute/cascade.js.map +1 -1
  61. package/dist/src/dispute/dispute-service.d.ts +5 -0
  62. package/dist/src/dispute/dispute-service.d.ts.map +1 -1
  63. package/dist/src/dispute/dispute-service.js +13 -3
  64. package/dist/src/dispute/dispute-service.js.map +1 -1
  65. package/dist/src/dispute/invalidation.d.ts +33 -15
  66. package/dist/src/dispute/invalidation.d.ts.map +1 -1
  67. package/dist/src/dispute/invalidation.js +127 -34
  68. package/dist/src/dispute/invalidation.js.map +1 -1
  69. package/dist/src/dispute/service.d.ts.map +1 -1
  70. package/dist/src/dispute/service.js +2 -1
  71. package/dist/src/dispute/service.js.map +1 -1
  72. package/dist/src/index.d.ts +7 -0
  73. package/dist/src/index.d.ts.map +1 -1
  74. package/dist/src/index.js +7 -0
  75. package/dist/src/index.js.map +1 -1
  76. package/dist/src/libp2p-key-network.d.ts +14 -10
  77. package/dist/src/libp2p-key-network.d.ts.map +1 -1
  78. package/dist/src/libp2p-key-network.js +28 -58
  79. package/dist/src/libp2p-key-network.js.map +1 -1
  80. package/dist/src/libp2p-node-base.d.ts +38 -7
  81. package/dist/src/libp2p-node-base.d.ts.map +1 -1
  82. package/dist/src/libp2p-node-base.js +148 -63
  83. package/dist/src/libp2p-node-base.js.map +1 -1
  84. package/dist/src/network/open-protocol-stream.d.ts +56 -0
  85. package/dist/src/network/open-protocol-stream.d.ts.map +1 -0
  86. package/dist/src/network/open-protocol-stream.js +60 -0
  87. package/dist/src/network/open-protocol-stream.js.map +1 -0
  88. package/dist/src/network/register-protocol-handler.d.ts +66 -0
  89. package/dist/src/network/register-protocol-handler.d.ts.map +1 -0
  90. package/dist/src/network/register-protocol-handler.js +51 -0
  91. package/dist/src/network/register-protocol-handler.js.map +1 -0
  92. package/dist/src/peer-address-book.d.ts +60 -2
  93. package/dist/src/peer-address-book.d.ts.map +1 -1
  94. package/dist/src/peer-address-book.js +89 -1
  95. package/dist/src/peer-address-book.js.map +1 -1
  96. package/dist/src/pend-validation.d.ts +65 -0
  97. package/dist/src/pend-validation.d.ts.map +1 -0
  98. package/dist/src/pend-validation.js +69 -0
  99. package/dist/src/pend-validation.js.map +1 -0
  100. package/dist/src/reactivity/notify-transport.d.ts.map +1 -1
  101. package/dist/src/reactivity/notify-transport.js +2 -1
  102. package/dist/src/reactivity/notify-transport.js.map +1 -1
  103. package/dist/src/reactivity/push-state-gossip.d.ts.map +1 -1
  104. package/dist/src/reactivity/push-state-gossip.js +2 -1
  105. package/dist/src/reactivity/push-state-gossip.js.map +1 -1
  106. package/dist/src/repo/cluster-coordinator.d.ts +32 -6
  107. package/dist/src/repo/cluster-coordinator.d.ts.map +1 -1
  108. package/dist/src/repo/cluster-coordinator.js +44 -10
  109. package/dist/src/repo/cluster-coordinator.js.map +1 -1
  110. package/dist/src/repo/coordinator-repo.d.ts +181 -25
  111. package/dist/src/repo/coordinator-repo.d.ts.map +1 -1
  112. package/dist/src/repo/coordinator-repo.js +650 -62
  113. package/dist/src/repo/coordinator-repo.js.map +1 -1
  114. package/dist/src/repo/served-repo-proxy.d.ts +30 -0
  115. package/dist/src/repo/served-repo-proxy.d.ts.map +1 -0
  116. package/dist/src/repo/served-repo-proxy.js +50 -0
  117. package/dist/src/repo/served-repo-proxy.js.map +1 -0
  118. package/dist/src/repo/service.d.ts +16 -1
  119. package/dist/src/repo/service.d.ts.map +1 -1
  120. package/dist/src/repo/service.js +18 -16
  121. package/dist/src/repo/service.js.map +1 -1
  122. package/dist/src/reputation/types.d.ts +3 -1
  123. package/dist/src/reputation/types.d.ts.map +1 -1
  124. package/dist/src/reputation/types.js +3 -1
  125. package/dist/src/reputation/types.js.map +1 -1
  126. package/dist/src/rn.d.ts +24 -2
  127. package/dist/src/rn.d.ts.map +1 -1
  128. package/dist/src/rn.js +33 -4
  129. package/dist/src/rn.js.map +1 -1
  130. package/dist/src/storage/block-archive.d.ts +153 -0
  131. package/dist/src/storage/block-archive.d.ts.map +1 -0
  132. package/dist/src/storage/block-archive.js +194 -0
  133. package/dist/src/storage/block-archive.js.map +1 -0
  134. package/dist/src/storage/block-latch.d.ts +92 -0
  135. package/dist/src/storage/block-latch.d.ts.map +1 -0
  136. package/dist/src/storage/block-latch.js +132 -0
  137. package/dist/src/storage/block-latch.js.map +1 -0
  138. package/dist/src/storage/block-storage.d.ts +169 -12
  139. package/dist/src/storage/block-storage.d.ts.map +1 -1
  140. package/dist/src/storage/block-storage.js +415 -152
  141. package/dist/src/storage/block-storage.js.map +1 -1
  142. package/dist/src/storage/cached-raw-storage.d.ts +5 -1
  143. package/dist/src/storage/cached-raw-storage.d.ts.map +1 -1
  144. package/dist/src/storage/cached-raw-storage.js +16 -1
  145. package/dist/src/storage/cached-raw-storage.js.map +1 -1
  146. package/dist/src/storage/cached-store-driver.d.ts +14 -4
  147. package/dist/src/storage/cached-store-driver.d.ts.map +1 -1
  148. package/dist/src/storage/cached-store-driver.js +39 -5
  149. package/dist/src/storage/cached-store-driver.js.map +1 -1
  150. package/dist/src/storage/i-block-storage.d.ts +70 -19
  151. package/dist/src/storage/i-block-storage.d.ts.map +1 -1
  152. package/dist/src/storage/i-block-storage.js +21 -1
  153. package/dist/src/storage/i-block-storage.js.map +1 -1
  154. package/dist/src/storage/i-raw-storage.d.ts +38 -0
  155. package/dist/src/storage/i-raw-storage.d.ts.map +1 -1
  156. package/dist/src/storage/kv-raw-storage.d.ts +14 -8
  157. package/dist/src/storage/kv-raw-storage.d.ts.map +1 -1
  158. package/dist/src/storage/kv-raw-storage.js +29 -9
  159. package/dist/src/storage/kv-raw-storage.js.map +1 -1
  160. package/dist/src/storage/memory-store-driver.d.ts +4 -1
  161. package/dist/src/storage/memory-store-driver.d.ts.map +1 -1
  162. package/dist/src/storage/memory-store-driver.js +22 -2
  163. package/dist/src/storage/memory-store-driver.js.map +1 -1
  164. package/dist/src/storage/raw-store-codec.js +2 -2
  165. package/dist/src/storage/raw-store-driver.d.ts +44 -3
  166. package/dist/src/storage/raw-store-driver.d.ts.map +1 -1
  167. package/dist/src/storage/shared-cache-pool.d.ts +60 -3
  168. package/dist/src/storage/shared-cache-pool.d.ts.map +1 -1
  169. package/dist/src/storage/shared-cache-pool.js +68 -4
  170. package/dist/src/storage/shared-cache-pool.js.map +1 -1
  171. package/dist/src/storage/storage-repo.d.ts +204 -26
  172. package/dist/src/storage/storage-repo.d.ts.map +1 -1
  173. package/dist/src/storage/storage-repo.js +422 -119
  174. package/dist/src/storage/storage-repo.js.map +1 -1
  175. package/dist/src/storage/store-identity.d.ts +21 -0
  176. package/dist/src/storage/store-identity.d.ts.map +1 -0
  177. package/dist/src/storage/store-identity.js +33 -0
  178. package/dist/src/storage/store-identity.js.map +1 -0
  179. package/dist/src/storage/struct.d.ts +15 -0
  180. package/dist/src/storage/struct.d.ts.map +1 -1
  181. package/dist/src/storage/with-read-cache.d.ts +92 -0
  182. package/dist/src/storage/with-read-cache.d.ts.map +1 -0
  183. package/dist/src/storage/with-read-cache.js +132 -0
  184. package/dist/src/storage/with-read-cache.js.map +1 -0
  185. package/dist/src/sync/service.d.ts +13 -3
  186. package/dist/src/sync/service.d.ts.map +1 -1
  187. package/dist/src/sync/service.js +7 -30
  188. package/dist/src/sync/service.js.map +1 -1
  189. package/dist/src/testing/mesh-harness.d.ts +75 -2
  190. package/dist/src/testing/mesh-harness.d.ts.map +1 -1
  191. package/dist/src/testing/mesh-harness.js +193 -81
  192. package/dist/src/testing/mesh-harness.js.map +1 -1
  193. package/dist/src/testing/raw-storage-conformance.d.ts +8 -0
  194. package/dist/src/testing/raw-storage-conformance.d.ts.map +1 -1
  195. package/dist/src/testing/raw-storage-conformance.js +97 -2
  196. package/dist/src/testing/raw-storage-conformance.js.map +1 -1
  197. package/package.json +5 -6
  198. package/src/cluster/block-transfer-service.ts +528 -312
  199. package/src/cluster/block-transfer.ts +547 -424
  200. package/src/cluster/certified-claims.ts +319 -0
  201. package/src/cluster/client-signature-verifier.ts +34 -0
  202. package/src/cluster/cluster-policy.ts +294 -203
  203. package/src/cluster/cluster-repo.ts +2501 -2034
  204. package/src/cluster/commit-proof.ts +344 -0
  205. package/src/cluster/quorum-restore.ts +348 -223
  206. package/src/cluster/rebalance-monitor.ts +539 -254
  207. package/src/cluster/reconcile-block.ts +404 -203
  208. package/src/cluster/service.ts +23 -20
  209. package/src/cluster/spread-on-churn.ts +352 -346
  210. package/src/cohort-topic/host.ts +8 -7
  211. package/src/cohort-topic/stream-util.ts +14 -43
  212. package/src/dispute/cascade.ts +31 -15
  213. package/src/dispute/dispute-service.ts +706 -696
  214. package/src/dispute/invalidation.ts +162 -52
  215. package/src/dispute/service.ts +126 -125
  216. package/src/index.ts +7 -0
  217. package/src/libp2p-key-network.ts +1204 -1235
  218. package/src/libp2p-node-base.ts +185 -70
  219. package/src/network/open-protocol-stream.ts +87 -0
  220. package/src/network/register-protocol-handler.ts +85 -0
  221. package/src/peer-address-book.ts +96 -2
  222. package/src/pend-validation.ts +89 -0
  223. package/src/reactivity/notify-transport.ts +2 -1
  224. package/src/reactivity/push-state-gossip.ts +2 -1
  225. package/src/repo/cluster-coordinator.ts +65 -11
  226. package/src/repo/coordinator-repo.ts +729 -71
  227. package/src/repo/served-repo-proxy.ts +55 -0
  228. package/src/repo/service.ts +25 -16
  229. package/src/reputation/types.ts +3 -1
  230. package/src/rn.ts +34 -4
  231. package/src/storage/block-archive.ts +242 -0
  232. package/src/storage/block-latch.ts +144 -0
  233. package/src/storage/block-storage.ts +787 -498
  234. package/src/storage/cached-raw-storage.ts +21 -1
  235. package/src/storage/cached-store-driver.ts +898 -859
  236. package/src/storage/i-block-storage.ts +172 -116
  237. package/src/storage/i-raw-storage.ts +40 -0
  238. package/src/storage/kv-raw-storage.ts +34 -9
  239. package/src/storage/memory-store-driver.ts +26 -2
  240. package/src/storage/raw-store-codec.ts +42 -42
  241. package/src/storage/raw-store-driver.ts +45 -3
  242. package/src/storage/shared-cache-pool.ts +516 -452
  243. package/src/storage/storage-repo.ts +1421 -1014
  244. package/src/storage/store-identity.ts +41 -0
  245. package/src/storage/struct.ts +52 -34
  246. package/src/storage/with-read-cache.ts +182 -0
  247. package/src/sync/service.ts +178 -198
  248. package/src/testing/mesh-harness.ts +524 -344
  249. package/src/testing/raw-storage-conformance.ts +114 -2
@@ -1,7 +1,9 @@
1
1
  import type { BlockId, IBlock, Transform, ActionId, ActionRev } from "@optimystic/db-core";
2
+ import type { BlockCommitProof } from "../cluster/commit-proof.js";
2
3
  import type { RestoreCallback } from "./struct.js";
3
4
  import type { IRawStorage } from "./i-raw-storage.js";
4
- import type { IBlockStorage } from "./i-block-storage.js";
5
+ import { type IBlockStorage } from "./i-block-storage.js";
6
+ import type { BlockWriteLatch } from "./block-latch.js";
5
7
  export declare class BlockStorage implements IBlockStorage {
6
8
  private readonly blockId;
7
9
  private readonly storage;
@@ -20,27 +22,43 @@ export declare class BlockStorage implements IBlockStorage {
20
22
  */
21
23
  checkpointInterval?: number);
22
24
  getLatest(): Promise<ActionRev | undefined>;
25
+ /**
26
+ * Guard every write: the token must have been minted for THIS block and must still be live. The
27
+ * type already proves the caller went through `acquireBlockWriteLatch`; this catches the two
28
+ * things the type cannot — a token for block A presented to block B's storage, and a token
29
+ * stashed by a callback and used after its scope released the latch.
30
+ */
31
+ private assertLatch;
32
+ /**
33
+ * LOCAL-ONLY read — never fetches from a peer. A revision outside `meta.ranges` throws
34
+ * {@link RevisionNotCoveredError}; the one caller allowed to heal that (`StorageRepo.get`) does so
35
+ * with {@link restoreRevision} under the block's write latch and re-reads. Keeping the fetch out of
36
+ * here is what lets the commit path hold N block latches with no network I/O inside them.
37
+ */
23
38
  getBlock(rev?: number): Promise<{
24
39
  block: IBlock;
25
40
  actionRev: ActionRev;
26
41
  } | undefined>;
42
+ restoreRevision(rev: number, latch: BlockWriteLatch): Promise<void>;
27
43
  getTransaction(actionId: ActionId): Promise<Transform | undefined>;
44
+ getBlockProof(rev: number): Promise<BlockCommitProof | undefined>;
45
+ saveBlockProof(rev: number, proof: BlockCommitProof, latch: BlockWriteLatch): Promise<void>;
28
46
  getPendingTransaction(actionId: ActionId): Promise<Transform | undefined>;
29
47
  listPendingTransactions(): AsyncIterable<ActionId>;
30
- savePendingTransaction(actionId: ActionId, transform: Transform): Promise<void>;
31
- deletePendingTransaction(actionId: ActionId): Promise<void>;
48
+ savePendingTransaction(actionId: ActionId, transform: Transform, latch: BlockWriteLatch): Promise<void>;
49
+ deletePendingTransaction(actionId: ActionId, latch: BlockWriteLatch): Promise<void>;
32
50
  listRevisions(startRev: number, endRev: number): AsyncIterable<ActionRev>;
33
- saveMaterializedBlock(actionId: ActionId, block: IBlock | undefined): Promise<void>;
34
- pruneSupersededMaterialization(prior: ActionRev): Promise<void>;
35
- saveRevision(rev: number, actionId: ActionId): Promise<void>;
36
- promotePendingTransaction(actionId: ActionId): Promise<void>;
37
- setLatest(latest: ActionRev): Promise<void>;
38
- recover(): Promise<{
51
+ saveMaterializedBlock(actionId: ActionId, block: IBlock | undefined, latch: BlockWriteLatch): Promise<void>;
52
+ pruneSupersededMaterialization(prior: ActionRev, latch: BlockWriteLatch): Promise<void>;
53
+ saveRevision(rev: number, actionId: ActionId, latch: BlockWriteLatch): Promise<void>;
54
+ promotePendingTransaction(actionId: ActionId, latch: BlockWriteLatch): Promise<void>;
55
+ setLatest(latest: ActionRev, latch: BlockWriteLatch): Promise<void>;
56
+ recover(latch: BlockWriteLatch): Promise<{
39
57
  reconciled: boolean;
40
58
  latest?: ActionRev;
41
59
  }>;
42
- saveReplica(block: IBlock, source?: ActionRev): Promise<ActionRev>;
43
- saveDeletion(source: ActionRev): Promise<ActionRev>;
60
+ saveReplica(block: IBlock, source: ActionRev | undefined, proof: BlockCommitProof | undefined, latch: BlockWriteLatch): Promise<ActionRev>;
61
+ saveDeletion(source: ActionRev, latch: BlockWriteLatch): Promise<ActionRev>;
44
62
  /**
45
63
  * Shared forward-write path for saveReplica and saveDeletion. Both append a single new revision
46
64
  * that ADVANCES `latest` (never rewrites history): acquire the block's metadata latch, apply the
@@ -50,11 +68,150 @@ export declare class BlockStorage implements IBlockStorage {
50
68
  * plus the materialized `block`; a deletion carries `{ delete: true }` and no block. `rev` and
51
69
  * `actionId` are passed alongside `body` because the guard and the `latest` advance need them
52
70
  * independently of the archive body.
71
+ *
72
+ * `verifiedProof` travels OUTSIDE `body` on purpose — see {@link saveRestored}: it is the one
73
+ * channel that persists a proof, and only {@link saveReplica} (whose caller verified the proof
74
+ * against `body.block`) supplies it.
53
75
  */
54
76
  private saveForwardRevision;
55
- private ensureRevision;
56
77
  private materializeBlock;
57
78
  private restoreBlock;
79
+ /**
80
+ * Vet an archive fetched for a PINNED restore of `rev`, returning the revision coverage to record
81
+ * for it — or `undefined` when the archive must be refused, in which case nothing is written at
82
+ * all and the reason is logged.
83
+ *
84
+ * This is the whole trust boundary for the restore wire. `restoreBlock`'s
85
+ * `RestorationCoordinator` verifies nothing about a response (`queryPeer` returns
86
+ * `response.archive` straight through), so every field below is a remote peer's assertion, and
87
+ * `saveRestored` writes keyed by REVISION and by ACTION ID — meaning an archive naming a
88
+ * revision or action id this node already holds would otherwise overwrite content that was never
89
+ * in question. The checks, in order:
90
+ *
91
+ * - **The archive is about this block.** `saveRestored` writes under `this.blockId` and ignores
92
+ * `archive.blockId`, so an answer about a different block would land as this block's history.
93
+ * - **Every revision key is a real revision.** Keys arrive as JSON strings; a non-numeric key
94
+ * coerces to `NaN` and would be stored as a garbage revision number. Min/max are folded rather
95
+ * than spread through `Math.min`/`Math.max`, which throws `RangeError` past ~125k arguments —
96
+ * reachable inside the 8 MiB sync-response cap (see `maxArchiveRevision`, same hazard).
97
+ * - **Each entry's own `rev`, when it declares one, agrees with the key it is filed under.**
98
+ * That disagreement IS the mislabel this ticket's family of bugs is about, in miniature.
99
+ * - **The archive answers the pin.** NOT "carries revision `rev`" — `ActionContext.rev` is a
100
+ * COLLECTION-wide revision, so it routinely sits above the revision at which this particular
101
+ * block last changed. A peer answering a pin at 9 for a block whose last commit was rev 2
102
+ * correctly serves rev 2, labelled as rev 2 (pinned in `test/block-archive-proof.spec.ts`).
103
+ * So the rule is that the archive's LOWEST revision is at or below the pin: `materializeBlock`
104
+ * descends from `rev`, so an archive entirely above the pin answers a different question and
105
+ * is exactly the "old bytes under a newer label" shape that overwrites good local data.
106
+ * - **The declared `range` agrees with the revisions actually carried** — it starts at the
107
+ * lowest (the floor must be present, or the descending walk has nothing to land on) and ends
108
+ * past the highest. An OPEN-ENDED range is refused outright: it would claim infinite coverage
109
+ * and permanently disable restore for this block on one unverified peer's say-so.
110
+ * (`RestoreCallback` allows open-ended for the UNPINNED call; `restoreRevision` never makes one.)
111
+ * - **Nothing already held is overwritten with different content** — see
112
+ * {@link noDivergentRewrite}.
113
+ *
114
+ * ## What gets recorded, and the one thing taken on trust
115
+ *
116
+ * The coverage returned is `[lowest, rev + 1)` — the archive's floor, up to the PIN and no
117
+ * further. Both halves of that are deliberate.
118
+ *
119
+ * Extending UP to the pin is an INFERENCE, and the only one here: a peer
120
+ * answering a pinned fetch with revision M ≤ N means "M is my highest committed revision of this
121
+ * block at or below N", i.e. nothing changed in (M, N]. This node cannot verify that locally.
122
+ *
123
+ * It is recorded anyway because the alternative is worse. `meta.ranges` is what
124
+ * {@link restoreRevision} consults to decide whether to fetch at all, so recording only the
125
+ * archive's literal `[M, M+1)` leaves `inRanges(N)` false and re-runs the ENTIRE restore — network
126
+ * round trip plus a full `saveRestored` write — on every later read at that pin, forever, never
127
+ * converging. The inference is also unavoidable rather than merely convenient: having the peer
128
+ * state the claim on the wire instead would not make it verifiable, only explicit, while breaking
129
+ * repair against every peer running an older build.
130
+ *
131
+ * Stopping AT the pin, on the other hand, discards coverage for any revision the archive
132
+ * volunteered ABOVE it. Those entries are still WRITTEN — an honest peer serves a contiguous
133
+ * span, so an archive of `{2, 3, 4}` answering a pin at 3 is normal, not hostile — they are
134
+ * merely not CLAIMED. `rev` is the one number in the exchange this node chose, so it is as far as
135
+ * its trust in the answer should reach; recording `highest + 1` instead would let the peer set
136
+ * the width of its own credibility by padding the archive with fabricated high revisions, and
137
+ * reads across that padded span would then be served from local content without ever re-asking.
138
+ * The cost is one redundant fetch the first time a revision above the pin is read; that fetch is
139
+ * idempotent (identical content is not a conflict, see {@link noDivergentRewrite}) and the
140
+ * coverage converges.
141
+ *
142
+ * NOTE: accepted tradeoff — a lying peer's answer is therefore STICKY across the whole span it
143
+ * was asked about: reads between M and N are served locally from M's content and never re-ask, so
144
+ * a later honest peer is never consulted for them. Weighed against an unbounded re-fetch loop and
145
+ * kept; that is the same "ranges records what this node can locally reconstruct, freshness is a
146
+ * separate concern" position `setLatest` and `saveForwardRevision` already take. Revisit if a
147
+ * restore ever gains a way to verify an archive (a commit proof chain over the served revision
148
+ * would do it) — at that point record only what verifies.
149
+ */
150
+ private vetRestoredArchive;
151
+ /**
152
+ * True when none of `entries` would overwrite content this node ALREADY holds with different
153
+ * content. False (having logged which entry, via `refuse`) when any would.
154
+ *
155
+ * The refusal is all-or-nothing: one divergent entry rejects the WHOLE archive rather than
156
+ * landing the entries this node happens to lack. Two reasons. An archive that contradicts locally
157
+ * held content is evidence the peer is wrong or hostile about this block, which makes the rest of
158
+ * it no more trustworthy than the part that was caught; and a partial apply would leave
159
+ * {@link vetRestoredArchive}'s coverage claiming a span the applied subset may not support.
160
+ *
161
+ * Identical content is NOT a conflict — a re-restore of the same archive must stay idempotent,
162
+ * which it has to be for the pin-extended coverage above to converge.
163
+ *
164
+ * NOTE: accepted tradeoff — first writer wins, permanently. Once a revision record is held, no
165
+ * later archive can replace it, so a lying peer that answers a gap FIRST makes every honest
166
+ * archive restating that revision refuse forever: the revision becomes unreadable rather than
167
+ * wrong. That is the deliberate direction — this guard exists precisely so a peer cannot rewrite
168
+ * held history, and it cannot tell "the held copy is the lie" from "the incoming copy is". Repair
169
+ * from that state is an operator action (drop the block's local records and re-fetch). Revisit
170
+ * only alongside a way to verify an archive, which would let the guard prefer the provable copy
171
+ * instead of the earlier one.
172
+ *
173
+ * Comparison is by `canonicalJson`, db-core's one deterministic encoding, so key ORDER across a
174
+ * JSON round trip over the wire never reads as divergence.
175
+ *
176
+ * The three keys mirror {@link saveRestored}'s three writes exactly; an entry that carries no
177
+ * `block` writes no materialization, so it cannot clobber one and is not checked for it.
178
+ *
179
+ * NOTE: costs up to three raw-storage reads per revision entry, on the restore path only — which
180
+ * has already paid for a network round trip, so it is not the term that matters. If a restore
181
+ * ever carries thousands of revisions and this shows up, check `getRevision` first and skip the
182
+ * other two for a revision this node does not hold at all.
183
+ */
184
+ private noDivergentRewrite;
185
+ /**
186
+ * Persist a fetched archive's revisions locally.
187
+ *
188
+ * A revision entry's own `proof` is deliberately IGNORED. An archive is remote wire data —
189
+ * {@link restoreBlock}'s `RestorationCoordinator` fetch verifies nothing, and a peer chooses
190
+ * what to attach — so persisting a proof read out of the archive body would re-serve a hostile
191
+ * peer's artifact as evidence this node retained itself. A proof reaches storage only through
192
+ * `verified`, passed out-of-band alongside the archive, which exactly one caller chain supplies:
193
+ * `cluster/reconcile-block.ts` → `StorageRepo.saveReplicatedBlock` → {@link saveReplica} →
194
+ * {@link saveForwardRevision}, where `certifyContent` had already bound the proof to these exact
195
+ * bytes. A separate parameter rather than a caller obligation to strip is what makes "an
196
+ * unverified proof reached `saveBlockProof`" unrepresentable instead of merely documented.
197
+ *
198
+ * This is a WRITER, not a gate: it trusts what it is handed, and each of its two callers is
199
+ * responsible for having earned that on its own terms.
200
+ *
201
+ * - {@link restoreRevision} — the unverified restore wire — runs {@link vetRestoredArchive}
202
+ * first. Those checks are ABOUT the pinned request (does the archive answer the revision that
203
+ * was asked for?), and this function has no pin to check against, so they cannot live here.
204
+ * - {@link saveForwardRevision} — reached by `saveReplica`/`saveDeletion` through
205
+ * `StorageRepo.saveReplicatedBlock` — builds the archive it passes from local arguments, and
206
+ * on the replica path `cluster/reconcile-block.ts` has already bound those bytes to a verified
207
+ * proof. It writes strictly ABOVE its own `latest` (the monotonic guard returns first
208
+ * otherwise), so it cannot rewrite held history, and it deliberately pays nothing for the
209
+ * restore wire's checks.
210
+ *
211
+ * A THIRD caller would not inherit either argument. Any future one that takes an archive off a
212
+ * network must route through `vetRestoredArchive` (or an equivalent for its own trust model)
213
+ * before reaching here.
214
+ */
58
215
  private saveRestored;
59
216
  private inRanges;
60
217
  /**
@@ -1 +1 @@
1
- {"version":3,"file":"block-storage.d.ts","sourceRoot":"","sources":["../../../src/storage/block-storage.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,EAAE,SAAS,EAAE,QAAQ,EAAE,SAAS,EAAmB,MAAM,qBAAqB,CAAC;AAE5G,OAAO,KAAK,EAA+B,eAAe,EAAiB,MAAM,aAAa,CAAC;AAC/F,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;AAEtD,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC;AAY1D,qBAAa,YAAa,YAAW,aAAa;IAEhD,OAAO,CAAC,QAAQ,CAAC,OAAO;IACxB,OAAO,CAAC,QAAQ,CAAC,OAAO;IACxB,OAAO,CAAC,QAAQ,CAAC,eAAe,CAAC;IACjC;;;;OAIG;IACH,OAAO,CAAC,QAAQ,CAAC,kBAAkB;gBARlB,OAAO,EAAE,OAAO,EAChB,OAAO,EAAE,WAAW,EACpB,eAAe,CAAC,EAAE,eAAe,YAAA;IAClD;;;;OAIG;IACc,kBAAkB,GAAE,MAA4B;IAG5D,SAAS,IAAI,OAAO,CAAC,SAAS,GAAG,SAAS,CAAC;IAK3C,QAAQ,CAAC,GAAG,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,SAAS,EAAE,SAAS,CAAA;KAAE,GAAG,SAAS,CAAC;IA2DpF,cAAc,CAAC,QAAQ,EAAE,QAAQ,GAAG,OAAO,CAAC,SAAS,GAAG,SAAS,CAAC;IAIlE,qBAAqB,CAAC,QAAQ,EAAE,QAAQ,GAAG,OAAO,CAAC,SAAS,GAAG,SAAS,CAAC;IAIxE,uBAAuB,IAAI,aAAa,CAAC,QAAQ,CAAC;IAInD,sBAAsB,CAAC,QAAQ,EAAE,QAAQ,EAAE,SAAS,EAAE,SAAS,GAAG,OAAO,CAAC,IAAI,CAAC;IAe/E,wBAAwB,CAAC,QAAQ,EAAE,QAAQ,GAAG,OAAO,CAAC,IAAI,CAAC;IAK1D,aAAa,CAAC,QAAQ,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,aAAa,CAAC,SAAS,CAAC;IAI1E,qBAAqB,CAAC,QAAQ,EAAE,QAAQ,EAAE,KAAK,EAAE,MAAM,GAAG,SAAS,GAAG,OAAO,CAAC,IAAI,CAAC;IAInF,8BAA8B,CAAC,KAAK,EAAE,SAAS,GAAG,OAAO,CAAC,IAAI,CAAC;IAmB/D,YAAY,CAAC,GAAG,EAAE,MAAM,EAAE,QAAQ,EAAE,QAAQ,GAAG,OAAO,CAAC,IAAI,CAAC;IAI5D,yBAAyB,CAAC,QAAQ,EAAE,QAAQ,GAAG,OAAO,CAAC,IAAI,CAAC;IAK5D,SAAS,CAAC,MAAM,EAAE,SAAS,GAAG,OAAO,CAAC,IAAI,CAAC;IA0B3C,OAAO,IAAI,OAAO,CAAC;QAAE,UAAU,EAAE,OAAO,CAAC;QAAC,MAAM,CAAC,EAAE,SAAS,CAAA;KAAE,CAAC;IAuC/D,WAAW,CAAC,KAAK,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,SAAS,GAAG,OAAO,CAAC,SAAS,CAAC;IAkBlE,YAAY,CAAC,MAAM,EAAE,SAAS,GAAG,OAAO,CAAC,SAAS,CAAC;IAczD;;;;;;;;;OASG;YACW,mBAAmB;YA+EnB,cAAc;YA+Bd,gBAAgB;YAiEhB,YAAY;YAKZ,YAAY;IAc1B,OAAO,CAAC,QAAQ;IAMhB;;;;;;;;;OASG;IACH,OAAO,CAAC,aAAa;IAMrB;;sGAEkG;IAClG,OAAO,CAAC,YAAY;CASpB"}
1
+ {"version":3,"file":"block-storage.d.ts","sourceRoot":"","sources":["../../../src/storage/block-storage.ts"],"names":[],"mappings":"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"}