@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.
- package/{readme.md → README.md} +688 -668
- package/dist/src/cluster/block-transfer-service.d.ts +133 -10
- package/dist/src/cluster/block-transfer-service.d.ts.map +1 -1
- package/dist/src/cluster/block-transfer-service.js +155 -9
- package/dist/src/cluster/block-transfer-service.js.map +1 -1
- package/dist/src/cluster/block-transfer.d.ts +67 -7
- package/dist/src/cluster/block-transfer.d.ts.map +1 -1
- package/dist/src/cluster/block-transfer.js +127 -24
- package/dist/src/cluster/block-transfer.js.map +1 -1
- package/dist/src/cluster/certified-claims.d.ts +170 -0
- package/dist/src/cluster/certified-claims.d.ts.map +1 -0
- package/dist/src/cluster/certified-claims.js +200 -0
- package/dist/src/cluster/certified-claims.js.map +1 -0
- package/dist/src/cluster/client-signature-verifier.d.ts +23 -0
- package/dist/src/cluster/client-signature-verifier.d.ts.map +1 -0
- package/dist/src/cluster/client-signature-verifier.js +33 -0
- package/dist/src/cluster/client-signature-verifier.js.map +1 -0
- package/dist/src/cluster/cluster-policy.d.ts +9 -3
- package/dist/src/cluster/cluster-policy.d.ts.map +1 -1
- package/dist/src/cluster/cluster-policy.js +111 -27
- package/dist/src/cluster/cluster-policy.js.map +1 -1
- package/dist/src/cluster/cluster-repo.d.ts +140 -11
- package/dist/src/cluster/cluster-repo.d.ts.map +1 -1
- package/dist/src/cluster/cluster-repo.js +485 -29
- package/dist/src/cluster/cluster-repo.js.map +1 -1
- package/dist/src/cluster/commit-proof.d.ts +134 -0
- package/dist/src/cluster/commit-proof.d.ts.map +1 -0
- package/dist/src/cluster/commit-proof.js +263 -0
- package/dist/src/cluster/commit-proof.js.map +1 -0
- package/dist/src/cluster/quorum-restore.d.ts +95 -8
- package/dist/src/cluster/quorum-restore.d.ts.map +1 -1
- package/dist/src/cluster/quorum-restore.js +113 -14
- package/dist/src/cluster/quorum-restore.js.map +1 -1
- package/dist/src/cluster/rebalance-monitor.d.ts +84 -0
- package/dist/src/cluster/rebalance-monitor.d.ts.map +1 -1
- package/dist/src/cluster/rebalance-monitor.js +207 -5
- package/dist/src/cluster/rebalance-monitor.js.map +1 -1
- package/dist/src/cluster/reconcile-block.d.ts +27 -7
- package/dist/src/cluster/reconcile-block.d.ts.map +1 -1
- package/dist/src/cluster/reconcile-block.js +179 -38
- package/dist/src/cluster/reconcile-block.js.map +1 -1
- package/dist/src/cluster/service.d.ts +13 -4
- package/dist/src/cluster/service.d.ts.map +1 -1
- package/dist/src/cluster/service.js +17 -18
- package/dist/src/cluster/service.js.map +1 -1
- package/dist/src/cluster/spread-on-churn.d.ts +8 -2
- package/dist/src/cluster/spread-on-churn.d.ts.map +1 -1
- package/dist/src/cluster/spread-on-churn.js +7 -7
- package/dist/src/cluster/spread-on-churn.js.map +1 -1
- package/dist/src/cohort-topic/host.d.ts.map +1 -1
- package/dist/src/cohort-topic/host.js +8 -7
- package/dist/src/cohort-topic/host.js.map +1 -1
- package/dist/src/cohort-topic/stream-util.d.ts +7 -6
- package/dist/src/cohort-topic/stream-util.d.ts.map +1 -1
- package/dist/src/cohort-topic/stream-util.js +14 -41
- package/dist/src/cohort-topic/stream-util.js.map +1 -1
- package/dist/src/dispute/cascade.d.ts +7 -8
- package/dist/src/dispute/cascade.d.ts.map +1 -1
- package/dist/src/dispute/cascade.js +24 -7
- package/dist/src/dispute/cascade.js.map +1 -1
- package/dist/src/dispute/dispute-service.d.ts +5 -0
- package/dist/src/dispute/dispute-service.d.ts.map +1 -1
- package/dist/src/dispute/dispute-service.js +13 -3
- package/dist/src/dispute/dispute-service.js.map +1 -1
- package/dist/src/dispute/invalidation.d.ts +33 -15
- package/dist/src/dispute/invalidation.d.ts.map +1 -1
- package/dist/src/dispute/invalidation.js +127 -34
- package/dist/src/dispute/invalidation.js.map +1 -1
- package/dist/src/dispute/service.d.ts.map +1 -1
- package/dist/src/dispute/service.js +2 -1
- package/dist/src/dispute/service.js.map +1 -1
- package/dist/src/index.d.ts +7 -0
- package/dist/src/index.d.ts.map +1 -1
- package/dist/src/index.js +7 -0
- package/dist/src/index.js.map +1 -1
- package/dist/src/libp2p-key-network.d.ts +14 -10
- package/dist/src/libp2p-key-network.d.ts.map +1 -1
- package/dist/src/libp2p-key-network.js +28 -58
- package/dist/src/libp2p-key-network.js.map +1 -1
- package/dist/src/libp2p-node-base.d.ts +38 -7
- package/dist/src/libp2p-node-base.d.ts.map +1 -1
- package/dist/src/libp2p-node-base.js +148 -63
- package/dist/src/libp2p-node-base.js.map +1 -1
- package/dist/src/network/open-protocol-stream.d.ts +56 -0
- package/dist/src/network/open-protocol-stream.d.ts.map +1 -0
- package/dist/src/network/open-protocol-stream.js +60 -0
- package/dist/src/network/open-protocol-stream.js.map +1 -0
- package/dist/src/network/register-protocol-handler.d.ts +66 -0
- package/dist/src/network/register-protocol-handler.d.ts.map +1 -0
- package/dist/src/network/register-protocol-handler.js +51 -0
- package/dist/src/network/register-protocol-handler.js.map +1 -0
- package/dist/src/peer-address-book.d.ts +60 -2
- package/dist/src/peer-address-book.d.ts.map +1 -1
- package/dist/src/peer-address-book.js +89 -1
- package/dist/src/peer-address-book.js.map +1 -1
- package/dist/src/pend-validation.d.ts +65 -0
- package/dist/src/pend-validation.d.ts.map +1 -0
- package/dist/src/pend-validation.js +69 -0
- package/dist/src/pend-validation.js.map +1 -0
- package/dist/src/reactivity/notify-transport.d.ts.map +1 -1
- package/dist/src/reactivity/notify-transport.js +2 -1
- package/dist/src/reactivity/notify-transport.js.map +1 -1
- package/dist/src/reactivity/push-state-gossip.d.ts.map +1 -1
- package/dist/src/reactivity/push-state-gossip.js +2 -1
- package/dist/src/reactivity/push-state-gossip.js.map +1 -1
- package/dist/src/repo/cluster-coordinator.d.ts +32 -6
- package/dist/src/repo/cluster-coordinator.d.ts.map +1 -1
- package/dist/src/repo/cluster-coordinator.js +44 -10
- package/dist/src/repo/cluster-coordinator.js.map +1 -1
- package/dist/src/repo/coordinator-repo.d.ts +181 -25
- package/dist/src/repo/coordinator-repo.d.ts.map +1 -1
- package/dist/src/repo/coordinator-repo.js +650 -62
- package/dist/src/repo/coordinator-repo.js.map +1 -1
- package/dist/src/repo/served-repo-proxy.d.ts +30 -0
- package/dist/src/repo/served-repo-proxy.d.ts.map +1 -0
- package/dist/src/repo/served-repo-proxy.js +50 -0
- package/dist/src/repo/served-repo-proxy.js.map +1 -0
- package/dist/src/repo/service.d.ts +16 -1
- package/dist/src/repo/service.d.ts.map +1 -1
- package/dist/src/repo/service.js +18 -16
- package/dist/src/repo/service.js.map +1 -1
- package/dist/src/reputation/types.d.ts +3 -1
- package/dist/src/reputation/types.d.ts.map +1 -1
- package/dist/src/reputation/types.js +3 -1
- package/dist/src/reputation/types.js.map +1 -1
- package/dist/src/rn.d.ts +24 -2
- package/dist/src/rn.d.ts.map +1 -1
- package/dist/src/rn.js +33 -4
- package/dist/src/rn.js.map +1 -1
- package/dist/src/storage/block-archive.d.ts +153 -0
- package/dist/src/storage/block-archive.d.ts.map +1 -0
- package/dist/src/storage/block-archive.js +194 -0
- package/dist/src/storage/block-archive.js.map +1 -0
- package/dist/src/storage/block-latch.d.ts +92 -0
- package/dist/src/storage/block-latch.d.ts.map +1 -0
- package/dist/src/storage/block-latch.js +132 -0
- package/dist/src/storage/block-latch.js.map +1 -0
- package/dist/src/storage/block-storage.d.ts +169 -12
- package/dist/src/storage/block-storage.d.ts.map +1 -1
- package/dist/src/storage/block-storage.js +415 -152
- package/dist/src/storage/block-storage.js.map +1 -1
- package/dist/src/storage/cached-raw-storage.d.ts +5 -1
- package/dist/src/storage/cached-raw-storage.d.ts.map +1 -1
- package/dist/src/storage/cached-raw-storage.js +16 -1
- package/dist/src/storage/cached-raw-storage.js.map +1 -1
- package/dist/src/storage/cached-store-driver.d.ts +14 -4
- package/dist/src/storage/cached-store-driver.d.ts.map +1 -1
- package/dist/src/storage/cached-store-driver.js +39 -5
- package/dist/src/storage/cached-store-driver.js.map +1 -1
- package/dist/src/storage/i-block-storage.d.ts +70 -19
- package/dist/src/storage/i-block-storage.d.ts.map +1 -1
- package/dist/src/storage/i-block-storage.js +21 -1
- package/dist/src/storage/i-block-storage.js.map +1 -1
- package/dist/src/storage/i-raw-storage.d.ts +38 -0
- package/dist/src/storage/i-raw-storage.d.ts.map +1 -1
- package/dist/src/storage/kv-raw-storage.d.ts +14 -8
- package/dist/src/storage/kv-raw-storage.d.ts.map +1 -1
- package/dist/src/storage/kv-raw-storage.js +29 -9
- package/dist/src/storage/kv-raw-storage.js.map +1 -1
- package/dist/src/storage/memory-store-driver.d.ts +4 -1
- package/dist/src/storage/memory-store-driver.d.ts.map +1 -1
- package/dist/src/storage/memory-store-driver.js +22 -2
- package/dist/src/storage/memory-store-driver.js.map +1 -1
- package/dist/src/storage/raw-store-codec.js +2 -2
- package/dist/src/storage/raw-store-driver.d.ts +44 -3
- package/dist/src/storage/raw-store-driver.d.ts.map +1 -1
- package/dist/src/storage/shared-cache-pool.d.ts +60 -3
- package/dist/src/storage/shared-cache-pool.d.ts.map +1 -1
- package/dist/src/storage/shared-cache-pool.js +68 -4
- package/dist/src/storage/shared-cache-pool.js.map +1 -1
- package/dist/src/storage/storage-repo.d.ts +204 -26
- package/dist/src/storage/storage-repo.d.ts.map +1 -1
- package/dist/src/storage/storage-repo.js +422 -119
- package/dist/src/storage/storage-repo.js.map +1 -1
- package/dist/src/storage/store-identity.d.ts +21 -0
- package/dist/src/storage/store-identity.d.ts.map +1 -0
- package/dist/src/storage/store-identity.js +33 -0
- package/dist/src/storage/store-identity.js.map +1 -0
- package/dist/src/storage/struct.d.ts +15 -0
- package/dist/src/storage/struct.d.ts.map +1 -1
- package/dist/src/storage/with-read-cache.d.ts +92 -0
- package/dist/src/storage/with-read-cache.d.ts.map +1 -0
- package/dist/src/storage/with-read-cache.js +132 -0
- package/dist/src/storage/with-read-cache.js.map +1 -0
- package/dist/src/sync/service.d.ts +13 -3
- package/dist/src/sync/service.d.ts.map +1 -1
- package/dist/src/sync/service.js +7 -30
- package/dist/src/sync/service.js.map +1 -1
- package/dist/src/testing/mesh-harness.d.ts +75 -2
- package/dist/src/testing/mesh-harness.d.ts.map +1 -1
- package/dist/src/testing/mesh-harness.js +193 -81
- package/dist/src/testing/mesh-harness.js.map +1 -1
- package/dist/src/testing/raw-storage-conformance.d.ts +8 -0
- package/dist/src/testing/raw-storage-conformance.d.ts.map +1 -1
- package/dist/src/testing/raw-storage-conformance.js +97 -2
- package/dist/src/testing/raw-storage-conformance.js.map +1 -1
- package/package.json +5 -6
- package/src/cluster/block-transfer-service.ts +528 -312
- package/src/cluster/block-transfer.ts +547 -424
- package/src/cluster/certified-claims.ts +319 -0
- package/src/cluster/client-signature-verifier.ts +34 -0
- package/src/cluster/cluster-policy.ts +294 -203
- package/src/cluster/cluster-repo.ts +2501 -2034
- package/src/cluster/commit-proof.ts +344 -0
- package/src/cluster/quorum-restore.ts +348 -223
- package/src/cluster/rebalance-monitor.ts +539 -254
- package/src/cluster/reconcile-block.ts +404 -203
- package/src/cluster/service.ts +23 -20
- package/src/cluster/spread-on-churn.ts +352 -346
- package/src/cohort-topic/host.ts +8 -7
- package/src/cohort-topic/stream-util.ts +14 -43
- package/src/dispute/cascade.ts +31 -15
- package/src/dispute/dispute-service.ts +706 -696
- package/src/dispute/invalidation.ts +162 -52
- package/src/dispute/service.ts +126 -125
- package/src/index.ts +7 -0
- package/src/libp2p-key-network.ts +1204 -1235
- package/src/libp2p-node-base.ts +185 -70
- package/src/network/open-protocol-stream.ts +87 -0
- package/src/network/register-protocol-handler.ts +85 -0
- package/src/peer-address-book.ts +96 -2
- package/src/pend-validation.ts +89 -0
- package/src/reactivity/notify-transport.ts +2 -1
- package/src/reactivity/push-state-gossip.ts +2 -1
- package/src/repo/cluster-coordinator.ts +65 -11
- package/src/repo/coordinator-repo.ts +729 -71
- package/src/repo/served-repo-proxy.ts +55 -0
- package/src/repo/service.ts +25 -16
- package/src/reputation/types.ts +3 -1
- package/src/rn.ts +34 -4
- package/src/storage/block-archive.ts +242 -0
- package/src/storage/block-latch.ts +144 -0
- package/src/storage/block-storage.ts +787 -498
- package/src/storage/cached-raw-storage.ts +21 -1
- package/src/storage/cached-store-driver.ts +898 -859
- package/src/storage/i-block-storage.ts +172 -116
- package/src/storage/i-raw-storage.ts +40 -0
- package/src/storage/kv-raw-storage.ts +34 -9
- package/src/storage/memory-store-driver.ts +26 -2
- package/src/storage/raw-store-codec.ts +42 -42
- package/src/storage/raw-store-driver.ts +45 -3
- package/src/storage/shared-cache-pool.ts +516 -452
- package/src/storage/storage-repo.ts +1421 -1014
- package/src/storage/store-identity.ts +41 -0
- package/src/storage/struct.ts +52 -34
- package/src/storage/with-read-cache.ts +182 -0
- package/src/sync/service.ts +178 -198
- package/src/testing/mesh-harness.ts +524 -344
- 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
|
|
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
|
|
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,
|
|
1
|
+
{"version":3,"file":"block-storage.d.ts","sourceRoot":"","sources":["../../../src/storage/block-storage.ts"],"names":[],"mappings":"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"}
|