@solarpunkltd/swarm-id 0.0.0-stage → 0.8.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/LICENSE +202 -0
- package/README.md +89 -2
- package/dist/bus/account-bus.d.ts +104 -0
- package/dist/bus/account-delta.d.ts +52 -0
- package/dist/bus/bus-context.d.ts +7 -0
- package/dist/bus/envelope.d.ts +9 -0
- package/dist/bus/messages.d.ts +241 -0
- package/dist/bus/presence.d.ts +62 -0
- package/dist/bus/signaling-transport.d.ts +79 -0
- package/dist/chunk/bmt.d.ts +16 -0
- package/dist/chunk/cac.d.ts +17 -0
- package/dist/chunk/constants.d.ts +10 -0
- package/dist/chunk/encrypted-cac.d.ts +47 -0
- package/dist/chunk/encryption.d.ts +85 -0
- package/dist/chunk/index.d.ts +5 -0
- package/dist/chunks/chat-binding-DO5aGhPl.js +23 -0
- package/dist/chunks/chat-binding-DO5aGhPl.js.map +1 -0
- package/dist/errors.d.ts +35 -0
- package/dist/index.d.ts +27 -0
- package/dist/internal.d.ts +49 -0
- package/dist/internal.esm.js +2 -0
- package/dist/internal.esm.js.map +1 -0
- package/dist/proxy/act/act.d.ts +58 -0
- package/dist/proxy/act/crypto.d.ts +46 -0
- package/dist/proxy/act/grantee-list.d.ts +81 -0
- package/dist/proxy/act/history.d.ts +182 -0
- package/dist/proxy/act/index.d.ts +114 -0
- package/dist/proxy/app-stamp-limits.d.ts +62 -0
- package/dist/proxy/chunk-upload-stream.d.ts +37 -0
- package/dist/proxy/chunking.d.ts +15 -0
- package/dist/proxy/collection.d.ts +38 -0
- package/dist/proxy/download-data.d.ts +27 -0
- package/dist/proxy/errors.d.ts +8 -0
- package/dist/proxy/feed-manifest.d.ts +60 -0
- package/dist/proxy/feeds/epochs/async-finder.d.ts +70 -0
- package/dist/proxy/feeds/epochs/epoch.d.ts +79 -0
- package/dist/proxy/feeds/epochs/finder.d.ts +60 -0
- package/dist/proxy/feeds/epochs/index.d.ts +30 -0
- package/dist/proxy/feeds/epochs/types.d.ts +110 -0
- package/dist/proxy/feeds/epochs/updater.d.ts +76 -0
- package/dist/proxy/feeds/epochs/utils.d.ts +21 -0
- package/dist/proxy/feeds/index.d.ts +4 -0
- package/dist/proxy/feeds/sequence/async-finder.d.ts +14 -0
- package/dist/proxy/feeds/sequence/finder.d.ts +11 -0
- package/dist/proxy/feeds/sequence/index.d.ts +22 -0
- package/dist/proxy/feeds/sequence/types.d.ts +82 -0
- package/dist/proxy/feeds/sequence/updater.d.ts +20 -0
- package/dist/proxy/index.d.ts +5 -0
- package/dist/proxy/manifest-builder.d.ts +164 -0
- package/dist/proxy/mantaray.d.ts +76 -0
- package/dist/proxy/soc-restamp-slots.d.ts +48 -0
- package/dist/proxy/stamp-chunk.d.ts +45 -0
- package/dist/proxy/stamp-worker-handler.d.ts +32 -0
- package/dist/proxy/stamp-worker-pool.d.ts +52 -0
- package/dist/proxy/stamp-worker.d.ts +1 -0
- package/dist/proxy/types.d.ts +38 -0
- package/dist/proxy/upload.d.ts +207 -0
- package/dist/schemas.d.ts +671 -0
- package/dist/stamp-worker.iife.js +1 -0
- package/dist/storage/debounced-uploader.d.ts +61 -0
- package/dist/storage/utilization-store.d.ts +113 -0
- package/dist/swarm-id-client.d.ts +1143 -0
- package/dist/swarm-id-proxy.d.ts +1079 -0
- package/dist/swarm-id.esm.js +2 -0
- package/dist/swarm-id.esm.js.map +1 -0
- package/dist/sync/batch-write-coordinator.d.ts +348 -0
- package/dist/sync/device-roster.d.ts +59 -0
- package/dist/sync/device-state.d.ts +199 -0
- package/dist/sync/fold-account-from-swarm.d.ts +53 -0
- package/dist/sync/index.d.ts +11 -0
- package/dist/sync/merge-snapshot.d.ts +57 -0
- package/dist/sync/partition-intent.d.ts +218 -0
- package/dist/sync/partition-lease.d.ts +531 -0
- package/dist/sync/partition-lock.d.ts +218 -0
- package/dist/sync/partition-state.d.ts +287 -0
- package/dist/sync/session-check.d.ts +29 -0
- package/dist/sync/store-interfaces.d.ts +48 -0
- package/dist/sync/sync-account.d.ts +45 -0
- package/dist/sync/timing-constants.d.ts +32 -0
- package/dist/sync/types.d.ts +18 -0
- package/dist/types.d.ts +3042 -0
- package/dist/utils/account-state-snapshot.d.ts +43 -0
- package/dist/utils/active-devices.d.ts +45 -0
- package/dist/utils/avatar.d.ts +30 -0
- package/dist/utils/batch-utilization.d.ts +840 -0
- package/dist/utils/batch-write-lock.d.ts +12 -0
- package/dist/utils/bee-error.d.ts +11 -0
- package/dist/utils/chat-binding.d.ts +113 -0
- package/dist/utils/coalesced-task.d.ts +28 -0
- package/dist/utils/collection.d.ts +6 -0
- package/dist/utils/connection-info.d.ts +11 -0
- package/dist/utils/constants.d.ts +28 -0
- package/dist/utils/device-id.d.ts +109 -0
- package/dist/utils/hex.d.ts +31 -0
- package/dist/utils/json-rpc.d.ts +34 -0
- package/dist/utils/key-derivation.d.ts +124 -0
- package/dist/utils/lock-soc.d.ts +35 -0
- package/dist/utils/postage-contract.d.ts +143 -0
- package/dist/utils/postage-stamp-association.d.ts +39 -0
- package/dist/utils/promise.d.ts +37 -0
- package/dist/utils/sealed-values.d.ts +23 -0
- package/dist/utils/seed-encryption.d.ts +17 -0
- package/dist/utils/site-allow-list.d.ts +54 -0
- package/dist/utils/stamp-lifespan.d.ts +37 -0
- package/dist/utils/storage-managers.d.ts +62 -0
- package/dist/utils/storage-probe.d.ts +40 -0
- package/dist/utils/tag.d.ts +11 -0
- package/dist/utils/ttl.d.ts +120 -0
- package/dist/utils/url.d.ts +55 -0
- package/dist/utils/versioned-storage.d.ts +183 -0
- package/package.json +91 -3
|
@@ -0,0 +1,218 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Per-partition lock SOC for multi-device postage-batch sharing.
|
|
3
|
+
*
|
|
4
|
+
* A single shared SOC per partition is the cross-device authority for "who
|
|
5
|
+
* holds this partition". Multiple devices share the `backupSigner` so any of
|
|
6
|
+
* them can write the SOC; concurrent writers are ordered deterministically by
|
|
7
|
+
* a (timestampMs, tiebreaker) fencing token.
|
|
8
|
+
*
|
|
9
|
+
* identifier = keccak256("swarm-id-partition-lock-v1:" || batchIdHex || ":" || partition)
|
|
10
|
+
* owner = backup signer (shared across the account's devices)
|
|
11
|
+
* encryption = swarmEncryptionKey
|
|
12
|
+
*
|
|
13
|
+
* The identifier carries the batch and the partition, but not the account —
|
|
14
|
+
* domain separation across accounts comes from the per-account `owner`
|
|
15
|
+
* (derived from each account's `derivationKey`), so the SOC address still
|
|
16
|
+
* differs per account without accountId in the hash. The batch IS in there:
|
|
17
|
+
* keying on the partition alone made lane p of batch X and lane p of batch Y
|
|
18
|
+
* one address, so two coordinators sharing no slot took turns anyway (#589).
|
|
19
|
+
* See `makePartitionLockIdentifier` in `../utils/lock-soc`.
|
|
20
|
+
*
|
|
21
|
+
* Protocol (acquire / takeover / refresh):
|
|
22
|
+
* 1. Read the lock SOC.
|
|
23
|
+
* 2. If a live foreign holder exists → return "blocked" without writing.
|
|
24
|
+
* 3. Otherwise write our claim, wait `guardMs`, re-read.
|
|
25
|
+
* 4. If our generation is still the latest visible → "acquired".
|
|
26
|
+
* If a higher generation was written during the guard → "lost-race".
|
|
27
|
+
*
|
|
28
|
+
* See the "Multi-Device Postage Batches" page in docs-site for the full design.
|
|
29
|
+
*/
|
|
30
|
+
import { Bee, PrivateKey, type BatchId, type Stamper } from "@ethersphere/bee-js";
|
|
31
|
+
import { type PartitionLockGeneration, type PartitionLockPayload } from "../schemas";
|
|
32
|
+
export type { PartitionLockGeneration, PartitionLockPayload };
|
|
33
|
+
export { lockSocAddress, lockSocBucket, makePartitionLockIdentifier, } from "../utils/lock-soc";
|
|
34
|
+
/**
|
|
35
|
+
* Sentinel value placed in `holderDeviceId` to indicate "no current holder"
|
|
36
|
+
* (e.g. after an explicit release).
|
|
37
|
+
*/
|
|
38
|
+
export declare const NO_HOLDER_DEVICE_ID = "";
|
|
39
|
+
/**
|
|
40
|
+
* Outcome of an `acquirePartitionLock` call.
|
|
41
|
+
*
|
|
42
|
+
* - `"acquired"` — the lock is now held by this device (write + verify both
|
|
43
|
+
* succeeded).
|
|
44
|
+
* - `"blocked"` — a live foreign holder exists; no write was performed.
|
|
45
|
+
* - `"lost-race"` — we wrote, but a higher-generation writer appeared
|
|
46
|
+
* during the guard window. The verify-read returned that other write.
|
|
47
|
+
* - `"aborted"` — `shouldAbort` flipped before the claim write (the owning
|
|
48
|
+
* lease session closed, e.g. a release started); no write was performed.
|
|
49
|
+
*/
|
|
50
|
+
export interface AcquirePartitionLockResult {
|
|
51
|
+
outcome: "acquired" | "blocked" | "lost-race" | "aborted";
|
|
52
|
+
/** Latest payload observed by this call (after the verify-read). */
|
|
53
|
+
payload: PartitionLockPayload | undefined;
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* Outcome of a `releasePartitionLock` call.
|
|
57
|
+
*
|
|
58
|
+
* - `"released"` — the release sentinel was written.
|
|
59
|
+
* - `"skipped"` — the lock no longer carries the claim being released
|
|
60
|
+
* (successor claim, peer claim, or an existing sentinel); writing the
|
|
61
|
+
* sentinel would clobber state that is not ours to release. The lock is
|
|
62
|
+
* left untouched.
|
|
63
|
+
*/
|
|
64
|
+
export interface ReleasePartitionLockResult {
|
|
65
|
+
outcome: "released" | "skipped";
|
|
66
|
+
/** Payload observed by the pre-write read (undefined when missing/unreadable). */
|
|
67
|
+
observed?: PartitionLockPayload;
|
|
68
|
+
}
|
|
69
|
+
/**
|
|
70
|
+
* Compute the per-device tiebreaker: first 8 bytes of `keccak256(deviceId)`
|
|
71
|
+
* as a hex string. Two different deviceIds collide with probability 2⁻⁶⁴.
|
|
72
|
+
*/
|
|
73
|
+
export declare function makeDeviceTiebreaker(deviceId: string): string;
|
|
74
|
+
/**
|
|
75
|
+
* Deterministic per-device starting partition: `keccak256(deviceId) mod
|
|
76
|
+
* partitionCount`. Distinct devices map to distinct home partitions with high
|
|
77
|
+
* probability, so they spread across slots even when none can read peers' lock
|
|
78
|
+
* SOCs (the disjoint-gateway frozen-cache case — see
|
|
79
|
+
* docs/Partition-Lock-Hardening-Plan.md). Only changes the selection scan
|
|
80
|
+
* ORDER; visibly-held slots are still skipped, so behaviour under working
|
|
81
|
+
* reads is unchanged.
|
|
82
|
+
*
|
|
83
|
+
* ponytail: probabilistic spread, not mutual exclusion — colliding home
|
|
84
|
+
* partitions still race. The complete cross-gateway fix is the Phase 2
|
|
85
|
+
* intent-SOC protocol in the hardening plan.
|
|
86
|
+
*/
|
|
87
|
+
export declare function deviceHomePartition(deviceId: string, partitionCount: number): number;
|
|
88
|
+
/**
|
|
89
|
+
* Lexicographic comparison of two generations: timestamp first, then
|
|
90
|
+
* tiebreaker. Returns -1 / 0 / 1.
|
|
91
|
+
*/
|
|
92
|
+
export declare function compareGenerations(a: PartitionLockGeneration, b: PartitionLockGeneration): -1 | 0 | 1;
|
|
93
|
+
/**
|
|
94
|
+
* Read the partition-lock SOC. Returns `undefined` when the SOC has never
|
|
95
|
+
* been written (the underlying chunk is missing on the Bee node).
|
|
96
|
+
*/
|
|
97
|
+
export declare function readPartitionLock(opts: {
|
|
98
|
+
bee: Bee;
|
|
99
|
+
backupSigner: PrivateKey;
|
|
100
|
+
swarmEncryptionKey: Uint8Array;
|
|
101
|
+
batchId: BatchId;
|
|
102
|
+
partition: number;
|
|
103
|
+
}): Promise<PartitionLockPayload | undefined>;
|
|
104
|
+
/**
|
|
105
|
+
* Overwrite the partition-lock SOC with `payload`. LWW per SOC identifier
|
|
106
|
+
* — any prior chunk at this address is replaced (modulo Swarm propagation
|
|
107
|
+
* delay).
|
|
108
|
+
*/
|
|
109
|
+
export declare function writePartitionLock(opts: {
|
|
110
|
+
bee: Bee;
|
|
111
|
+
stamper: Stamper;
|
|
112
|
+
backupSigner: PrivateKey;
|
|
113
|
+
swarmEncryptionKey: Uint8Array;
|
|
114
|
+
batchId: BatchId;
|
|
115
|
+
partition: number;
|
|
116
|
+
payload: PartitionLockPayload;
|
|
117
|
+
}): Promise<void>;
|
|
118
|
+
/**
|
|
119
|
+
* Acquire (or refresh) the partition lock for this device.
|
|
120
|
+
*
|
|
121
|
+
* @returns `"acquired"` if we now hold the lock,
|
|
122
|
+
* `"blocked"` if a live foreign holder exists (no write performed),
|
|
123
|
+
* `"lost-race"` if a higher-generation writer superseded our write
|
|
124
|
+
* inside the guard window.
|
|
125
|
+
*/
|
|
126
|
+
export declare function acquirePartitionLock(opts: {
|
|
127
|
+
bee: Bee;
|
|
128
|
+
stamper: Stamper;
|
|
129
|
+
backupSigner: PrivateKey;
|
|
130
|
+
swarmEncryptionKey: Uint8Array;
|
|
131
|
+
batchId: BatchId;
|
|
132
|
+
partition: number;
|
|
133
|
+
deviceId: string;
|
|
134
|
+
ttlMs: number;
|
|
135
|
+
guardMs: number;
|
|
136
|
+
now?: () => number;
|
|
137
|
+
wait?: (ms: number) => Promise<void>;
|
|
138
|
+
/**
|
|
139
|
+
* Pre-built generation to claim with. Pass the SAME generation already
|
|
140
|
+
* advertised in a Phase-2 intent round so the winner's lock claim and its
|
|
141
|
+
* intent order identically. Defaults to a fresh `(now, tiebreaker)`.
|
|
142
|
+
*/
|
|
143
|
+
generation?: PartitionLockGeneration;
|
|
144
|
+
/**
|
|
145
|
+
* Checked immediately before the claim write. When it returns true the
|
|
146
|
+
* acquire aborts WITHOUT writing — used by `PartitionLease` to stop a
|
|
147
|
+
* refresh that overlaps a `release()` from minting a ghost claim the
|
|
148
|
+
* generation-fenced release would then refuse to clear (peers would have
|
|
149
|
+
* to wait out the TTL).
|
|
150
|
+
*/
|
|
151
|
+
shouldAbort?: () => boolean;
|
|
152
|
+
/**
|
|
153
|
+
* Skip the pre-write read entirely. Pass when the caller ALREADY read this
|
|
154
|
+
* partition's lock (timeout-bounded) moments ago and classified it as
|
|
155
|
+
* claimable — `PartitionLease.acquire`'s scan — so the duplicate read (an
|
|
156
|
+
* absent chunk that can block for tens of seconds on a gateway) stays off
|
|
157
|
+
* the acquire path. The live-foreign-holder "blocked" check is the caller's
|
|
158
|
+
* responsibility; the guard-window verify below still catches races.
|
|
159
|
+
*/
|
|
160
|
+
skipInitialRead?: boolean;
|
|
161
|
+
/**
|
|
162
|
+
* Bound for the initial + verify reads (default {@link SYNC_READ_TIMEOUT_MS}).
|
|
163
|
+
* Bee has no fast authoritative "not found", so an absent lock would
|
|
164
|
+
* otherwise block on peer exhaustion. A timed-out read maps to the same
|
|
165
|
+
* "unobserved" handling as any other read failure: initial → treated absent
|
|
166
|
+
* (claim proceeds; guard+verify arbitrate), verify → the optimistic
|
|
167
|
+
* "couldn't confirm our own write" branch.
|
|
168
|
+
*/
|
|
169
|
+
readTimeoutMs?: number;
|
|
170
|
+
}): Promise<AcquirePartitionLockResult>;
|
|
171
|
+
/**
|
|
172
|
+
* Write the release sentinel for a SPECIFIC claim — generation-fenced.
|
|
173
|
+
*
|
|
174
|
+
* A release is an action on the claim identified by `releasedGeneration`,
|
|
175
|
+
* not on the partition as such. The sentinel therefore carries that
|
|
176
|
+
* generation (it does NOT mint a fresh one), and it is only written when the
|
|
177
|
+
* lock still visibly carries the claim being released. Without the fence, a
|
|
178
|
+
* detached release that outlives a re-acquire writes its sentinel LAST —
|
|
179
|
+
* and Bee replaces a same-address SOC whenever the new chunk's postage
|
|
180
|
+
* stamp timestamp is newer, so the stale sentinel would deterministically
|
|
181
|
+
* clobber the successor's claim and peers would treat the partition as free
|
|
182
|
+
* while the successor still believes it holds it (issue #349).
|
|
183
|
+
*
|
|
184
|
+
* Skip rules (never write a sentinel over a claim that is not the one being
|
|
185
|
+
* released):
|
|
186
|
+
* - an existing sentinel (any generation) — already released; rewriting
|
|
187
|
+
* would mint a fresh stamp that could clobber a claim landing in between.
|
|
188
|
+
* Checked BEFORE the generation compare: a prior sentinel for this same
|
|
189
|
+
* claim carries the same generation and would otherwise match "ours".
|
|
190
|
+
* - a foreign claim, live or expired — never clobber a peer; an expired
|
|
191
|
+
* claim already reads as takeable.
|
|
192
|
+
* - our own claim with a NEWER generation — a successor re-acquired while
|
|
193
|
+
* this release was publishing (#349), or a release-overlapping refresh.
|
|
194
|
+
* - our own claim at a non-newer generation writes (`<=`, not `==`): a
|
|
195
|
+
* stale read returning an older refresh of the same holdership must not
|
|
196
|
+
* suppress the release.
|
|
197
|
+
* - a missing/unreadable lock writes (best-effort release; the sentinel is
|
|
198
|
+
* generation-fenced, so readers and successors can order it correctly).
|
|
199
|
+
*
|
|
200
|
+
* Residual (documented, accepted): a skip decision is only as fresh as the
|
|
201
|
+
* pre-write read. The same-device #349 race reads through the same Bee node
|
|
202
|
+
* (read-your-writes holds), so the fence is dependable there; cross-node
|
|
203
|
+
* staleness falls back to the displacement/TTL machinery.
|
|
204
|
+
*/
|
|
205
|
+
export declare function releasePartitionLock(opts: {
|
|
206
|
+
bee: Bee;
|
|
207
|
+
stamper: Stamper;
|
|
208
|
+
backupSigner: PrivateKey;
|
|
209
|
+
swarmEncryptionKey: Uint8Array;
|
|
210
|
+
batchId: BatchId;
|
|
211
|
+
partition: number;
|
|
212
|
+
deviceId: string;
|
|
213
|
+
/** Generation of the claim being released — fences the sentinel. */
|
|
214
|
+
releasedGeneration: PartitionLockGeneration;
|
|
215
|
+
/** `acquiredAt` of the claim being released (carried in the sentinel). */
|
|
216
|
+
acquiredAt: number;
|
|
217
|
+
now?: () => number;
|
|
218
|
+
}): Promise<ReleasePartitionLockResult>;
|
|
@@ -0,0 +1,287 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Partition-state pointer for multi-device postage-batch sharing.
|
|
3
|
+
*
|
|
4
|
+
* The current holder publishes its per-partition counter on each upload (and on
|
|
5
|
+
* release); the next holder reads it on acquire and resumes from exactly that
|
|
6
|
+
* counter, so the two devices never race for the same `(bucket, slot)` pair
|
|
7
|
+
* during an orderly hand-off.
|
|
8
|
+
*
|
|
9
|
+
* The counter is not a single blob: it is split into the same per-`chunkIndex`
|
|
10
|
+
* chunks the utilisation store uses, each uploaded with a random key to the
|
|
11
|
+
* partition's reserved slot (never a data slot), and a single "reference chunk"
|
|
12
|
+
* lists their 64-byte encrypted references (address‖key).
|
|
13
|
+
*
|
|
14
|
+
* The address of the latest reference chunk is published as a **rotating
|
|
15
|
+
* per-epoch state-pointer SOC** (`writeStatePointer`/`readStatePointer`): the
|
|
16
|
+
* identifier is `keccak256(topic ‖ uint64(floor(now / STATE_POINTER_EPOCH_MS)))`,
|
|
17
|
+
* so a reader COMPUTES the address directly — no epoch-tree walk — and the
|
|
18
|
+
* rotating address bypasses a gateway's frozen static-cache. The holder
|
|
19
|
+
* heartbeats the pointer on every refresh, so a taking-over device finds it at
|
|
20
|
+
* the current/previous bucket around `now` or the gone holder's `leasedUntil`.
|
|
21
|
+
* Mirrors the occupancy beacon (`partition-intent.ts`). See
|
|
22
|
+
* docs/Postage-Batch-Partitioning.md (§8 cross-device handoff).
|
|
23
|
+
*
|
|
24
|
+
* Topic = keccak256("swarm-id-partition-state-v1" ‖ batchId ‖ uint32(partition))
|
|
25
|
+
* Owner = backup signer (`deriveSecret(swarmEncryptionKey, BACKUP_KEY_LABEL)`)
|
|
26
|
+
*/
|
|
27
|
+
import { Bee, BatchId, EthAddress, PrivateKey, Topic, type Stamper } from "@ethersphere/bee-js";
|
|
28
|
+
import { z } from "zod";
|
|
29
|
+
/**
|
|
30
|
+
* Schema for the *decoded* partition state: the per-bucket counter
|
|
31
|
+
* reconstructed from the binary wire format (reference chunk → counter
|
|
32
|
+
* chunks, see the module doc above for the byte layout). The wire format is
|
|
33
|
+
* versioned by the feed topic domain (`PARTITION_STATE_TOPIC_DOMAIN`), not an
|
|
34
|
+
* in-blob version byte — a future v2 publishes under a v2 topic and never
|
|
35
|
+
* collides with v1 readers.
|
|
36
|
+
*
|
|
37
|
+
* `readPartitionState` validates the assembled counter against this schema
|
|
38
|
+
* before returning it; a failure is reported as `readFailed` (fail-safe —
|
|
39
|
+
* never a zero counter, which would re-issue used slots).
|
|
40
|
+
* `writePartitionState` validates its input against it before publishing.
|
|
41
|
+
*/
|
|
42
|
+
export declare const PartitionStateSchemaV1: z.ZodObject<{
|
|
43
|
+
counters: z.ZodCustom<Uint32Array<ArrayBuffer>, Uint32Array<ArrayBuffer>>;
|
|
44
|
+
}, z.core.$strip>;
|
|
45
|
+
export type PartitionState = z.infer<typeof PartitionStateSchemaV1>;
|
|
46
|
+
/**
|
|
47
|
+
* Build the per-partition state feed topic.
|
|
48
|
+
*
|
|
49
|
+
* Hashing rather than concatenating-to-string keeps the topic length
|
|
50
|
+
* fixed at 32 bytes regardless of how many partitions we support later.
|
|
51
|
+
*/
|
|
52
|
+
export declare function makePartitionStateTopic(batchId: BatchId, partition: number): Topic;
|
|
53
|
+
/**
|
|
54
|
+
* Granularity of the rotating state-pointer address — the SAME epoch the
|
|
55
|
+
* intent/occupancy beacons rotate on: a reader computes a writer's bucket from
|
|
56
|
+
* this shared constant, so the two must never drift apart (this module already
|
|
57
|
+
* imports from partition-intent; there is no import cycle).
|
|
58
|
+
*
|
|
59
|
+
* COUPLING: the takeover lookup (`readStatePointer`) scans the whole
|
|
60
|
+
* `LEASE_TTL_MS`-wide bucket span. The reads run CONCURRENTLY (each
|
|
61
|
+
* timeout-bounded), so widening the span costs more parallel reads but not more
|
|
62
|
+
* wall-clock — the lookup latency stays ~one read timeout regardless of the
|
|
63
|
+
* `STATE_POINTER_EPOCH_MS` vs `LEASE_TTL_MS` ratio.
|
|
64
|
+
*/
|
|
65
|
+
export declare const STATE_POINTER_EPOCH_MS: number;
|
|
66
|
+
/** Result of a `readStatePointer` lookup (see its doc for `inconclusive`). */
|
|
67
|
+
export interface StatePointerLookup {
|
|
68
|
+
pointer?: {
|
|
69
|
+
referenceHex: string;
|
|
70
|
+
};
|
|
71
|
+
inconclusive: boolean;
|
|
72
|
+
}
|
|
73
|
+
/** `floor(nowMs / STATE_POINTER_EPOCH_MS)` — the current state-pointer epoch bucket. */
|
|
74
|
+
export declare function statePointerEpochBucket(nowMs: number): number;
|
|
75
|
+
/**
|
|
76
|
+
* Publish the reference-chunk pointer at the current epoch bucket — a reserved-
|
|
77
|
+
* slot, encrypted SOC (mirrors the occupancy beacon's `writeReservedPartitionSoc`).
|
|
78
|
+
* Reserved-slot routed so it never consumes a data slot; overwrites within the
|
|
79
|
+
* same 30 s bucket. Called per publish AND on the refresh-tick heartbeat so a
|
|
80
|
+
* held partition always has a fresh pointer in the current bucket.
|
|
81
|
+
*/
|
|
82
|
+
export declare function writeStatePointer(opts: {
|
|
83
|
+
bee: Bee;
|
|
84
|
+
stamper: Stamper;
|
|
85
|
+
backupSigner: PrivateKey;
|
|
86
|
+
swarmEncryptionKey: Uint8Array;
|
|
87
|
+
batchId: BatchId;
|
|
88
|
+
partition: number;
|
|
89
|
+
referenceHex: string;
|
|
90
|
+
nowMs: number;
|
|
91
|
+
}): Promise<void>;
|
|
92
|
+
/**
|
|
93
|
+
* The 32-byte state-pointer SOC address for `partition` at the epoch bucket
|
|
94
|
+
* covering `nowMs`. Exposed so `writePartitionState` can exclude the pointer's
|
|
95
|
+
* bucket from the reserved-slot buckets its counter/reference chunks may use
|
|
96
|
+
* (both land at slot = partition; a collision would evict one). Same derivation
|
|
97
|
+
* `writeStatePointer` writes at.
|
|
98
|
+
*/
|
|
99
|
+
export declare function statePointerAddress(batchId: BatchId, partition: number, owner: EthAddress, nowMs: number): Uint8Array;
|
|
100
|
+
/**
|
|
101
|
+
* Read the latest state pointer by computing bucket addresses directly — no
|
|
102
|
+
* feed walk. Checks (newest-first) the current/previous bucket around `now` and,
|
|
103
|
+
* when given, the gone holder's lease span: every bucket from its last lock
|
|
104
|
+
* refresh (`leasedUntil`) back through `leasedUntil - LEASE_TTL_MS` plus
|
|
105
|
+
* {@link POINTER_LOOKUP_SLACK_BUCKETS} of slack. The holder heartbeats the
|
|
106
|
+
* pointer at `floor(now / EPOCH)` on every refresh, so its last DURABLE pointer
|
|
107
|
+
* lands somewhere in that span; scanning the whole span (rather than just the
|
|
108
|
+
* `leasedUntil` bucket) makes the lookup independent of the
|
|
109
|
+
* `STATE_POINTER_EPOCH_MS` vs `LEASE_TTL_MS` ratio AND tolerant of a couple of
|
|
110
|
+
* dropped final heartbeats. Returns the freshest pointer found (`pointer`), or
|
|
111
|
+
* `pointer: undefined` when none of the candidate buckets carry one.
|
|
112
|
+
* `inconclusive` marks a verdict that is NOT authoritative: with no pointer, a
|
|
113
|
+
* CLEAN scan (every candidate bucket read resolved) proves "no published state"
|
|
114
|
+
* while ≥1 timed-out bucket leaves a pointer we couldn't fetch possible; with a
|
|
115
|
+
* pointer found, a timed-out bucket NEWER than it may hide a fresher resume
|
|
116
|
+
* point (a later acked publish), so the found pointer is only a stale
|
|
117
|
+
* candidate. Either way the caller must fail safe on `inconclusive` rather
|
|
118
|
+
* than resume. Ceiling: a holder dark for longer than
|
|
119
|
+
* `LEASE_TTL_MS + POINTER_LOOKUP_SLACK_BUCKETS·EPOCH` past its last heartbeat
|
|
120
|
+
* resumes from zero.
|
|
121
|
+
*/
|
|
122
|
+
export declare function readStatePointer(opts: {
|
|
123
|
+
bee: Bee;
|
|
124
|
+
owner: EthAddress;
|
|
125
|
+
swarmEncryptionKey: Uint8Array;
|
|
126
|
+
batchId: BatchId;
|
|
127
|
+
partition: number;
|
|
128
|
+
nowMs: number;
|
|
129
|
+
holderLeasedUntilMs?: number;
|
|
130
|
+
}): Promise<StatePointerLookup>;
|
|
131
|
+
/**
|
|
132
|
+
* Read the latest partition-state-feed entry and reconstruct the
|
|
133
|
+
* `localCounter` array — the previous holder's per-bucket high-water.
|
|
134
|
+
*
|
|
135
|
+
* Returns a fresh zero-filled array when no prior snapshot exists — i.e.
|
|
136
|
+
* when a fresh partition is being seeded for the first time (Case A on a
|
|
137
|
+
* brand-new account, or Case B when no peer has yet published on the
|
|
138
|
+
* partition we're about to take). We resume at exactly the published
|
|
139
|
+
* counter: an unclean-crash window where a peer stamped past its last
|
|
140
|
+
* publish is not defended against — that data was never durably persisted
|
|
141
|
+
* anyway, so overwriting those slots loses nothing of value.
|
|
142
|
+
*
|
|
143
|
+
* Pass `knownReferenceHex` (the feed reference this device's local counter was
|
|
144
|
+
* last in sync with) to short-circuit: when the feed still points there,
|
|
145
|
+
* returns `{ unchanged: true }` WITHOUT downloading the reference/counter
|
|
146
|
+
* chunks, and the caller reuses its local counter. On a real read it returns
|
|
147
|
+
* the reconstructed `localCounter` plus the current `referenceHex` for the
|
|
148
|
+
* caller to cache.
|
|
149
|
+
*/
|
|
150
|
+
export declare function readPartitionState(opts: {
|
|
151
|
+
bee: Bee;
|
|
152
|
+
owner: EthAddress;
|
|
153
|
+
swarmEncryptionKey: Uint8Array;
|
|
154
|
+
batchId: BatchId;
|
|
155
|
+
partition: number;
|
|
156
|
+
batchDepth: number;
|
|
157
|
+
/** Gone holder's `leasedUntil` (from the lock SOC) — locates the pointer of
|
|
158
|
+
* a partition that's been free for a while at its last-heartbeat bucket.
|
|
159
|
+
* A holder heartbeats the pointer up to its `leasedUntil`, so reading that
|
|
160
|
+
* bucket (and the one before) finds a published pointer durably, however
|
|
161
|
+
* long ago: "no pointer found" then reliably means "no state was published". */
|
|
162
|
+
holderLeasedUntilMs?: number;
|
|
163
|
+
/**
|
|
164
|
+
* True when this partition's lock SOC could not be read conclusively (a
|
|
165
|
+
* transient/hanging read, NOT a clean 404). If we then find no published
|
|
166
|
+
* pointer we must NOT assume "no state" and resume from zero — the lock may
|
|
167
|
+
* be hiding an expired holder whose acked slots we'd re-issue. Treat that as
|
|
168
|
+
* `readFailed` (degrade to read-only + retry) instead. A genuinely fresh
|
|
169
|
+
* partition has a cleanly-absent lock (`false`) and still zero-seeds.
|
|
170
|
+
*/
|
|
171
|
+
lockUnreadable?: boolean;
|
|
172
|
+
/**
|
|
173
|
+
* Wall-clock ms to anchor the state-pointer lookup to — the reader half of
|
|
174
|
+
* the rendezvous. The lease passes its own clock (`this.now()`, = `Date.now()`
|
|
175
|
+
* in production) so the reader computes the same buckets the writer wrote at.
|
|
176
|
+
* Defaults to `Date.now()` for bare callers. Injectable for deterministic /
|
|
177
|
+
* per-device-skew tests (#385).
|
|
178
|
+
*/
|
|
179
|
+
nowMs?: number;
|
|
180
|
+
/**
|
|
181
|
+
* A `readStatePointer` result the caller already fetched (speculatively,
|
|
182
|
+
* overlapped with its lock/beacon scan) for this SAME
|
|
183
|
+
* (owner, batch, partition) with the SAME `nowMs` and NO
|
|
184
|
+
* `holderLeasedUntilMs` span. Only valid when `holderLeasedUntilMs` is
|
|
185
|
+
* undefined here too — identical inputs make it exactly the lookup this
|
|
186
|
+
* function would perform, so all fail-safe classification below applies
|
|
187
|
+
* unchanged. When provided, the pointer lookup is skipped.
|
|
188
|
+
*/
|
|
189
|
+
prefetchedPointer?: StatePointerLookup;
|
|
190
|
+
}, knownReferenceHex?: string): Promise<{
|
|
191
|
+
localCounter?: Uint32Array;
|
|
192
|
+
referenceHex?: string;
|
|
193
|
+
unchanged: boolean;
|
|
194
|
+
/**
|
|
195
|
+
* True when the feed HAS an entry but the reference chunk or a counter
|
|
196
|
+
* chunk could not be read. The caller must NOT proceed with a zero
|
|
197
|
+
* counter — the partition has a real resume point we failed to learn,
|
|
198
|
+
* and stamping from zero would re-issue every used slot (each overstamp
|
|
199
|
+
* evicts the existing data chunk from the reserve). Degrade to read-only
|
|
200
|
+
* and retry instead.
|
|
201
|
+
*/
|
|
202
|
+
readFailed?: boolean;
|
|
203
|
+
/**
|
|
204
|
+
* Buckets occupied by the published state chunks (counter chunks + the
|
|
205
|
+
* reference chunk) of the entry that was read. The caller hands these to
|
|
206
|
+
* the stamper so utilisation saves avoid overstamping (and evicting) the
|
|
207
|
+
* published resume point. Present only on a successful full read.
|
|
208
|
+
*/
|
|
209
|
+
stateBuckets?: number[];
|
|
210
|
+
/**
|
|
211
|
+
* The N per-chunk refs (sentinels included) the resume state was assembled
|
|
212
|
+
* from — i.e. the reference chunk's contents. The caller seeds these as the
|
|
213
|
+
* lease's `publishedReferences` so its FIRST publish is incremental against
|
|
214
|
+
* the resumed state (only the changed chunk + reference chunk), not a full
|
|
215
|
+
* re-publish. Present on a full read and (best-effort) on a cache hit;
|
|
216
|
+
* undefined when no prior state exists.
|
|
217
|
+
*/
|
|
218
|
+
references?: Uint8Array[];
|
|
219
|
+
}>;
|
|
220
|
+
/**
|
|
221
|
+
* Publish the current `localCounter` to the partition-state feed.
|
|
222
|
+
*
|
|
223
|
+
* The counter is split into the same per-`chunkIndex` chunks the utilisation
|
|
224
|
+
* store uses; each is uploaded with a random encryption key to this partition's
|
|
225
|
+
* reserved slot (so it never consumes a data slot), yielding a 64-byte
|
|
226
|
+
* encrypted reference (address‖key). A single "reference chunk" lists those
|
|
227
|
+
* references and is itself uploaded the same way; the feed points at it. A
|
|
228
|
+
* taking-over device follows the feed → reference chunk → counter chunks,
|
|
229
|
+
* decrypting each from the embedded key.
|
|
230
|
+
*
|
|
231
|
+
* INCREMENTAL: pass `previousReferences` (the refs the last publish returned)
|
|
232
|
+
* and `previousCounter` (the counter it published) to re-upload ONLY the chunks
|
|
233
|
+
* that changed, reusing the retained refs for the rest. A typical upload changes
|
|
234
|
+
* the data SOC's bucket plus the previous publish's feed-SOC bucket, so the
|
|
235
|
+
* publish is ~3-4 chunks instead of the whole counter. Omit them (release, or
|
|
236
|
+
* the first publish of a session) for a full publish.
|
|
237
|
+
*
|
|
238
|
+
* PARALLEL: every chunk address is content-derived, so all PUTs (counter chunks
|
|
239
|
+
* + reference chunk + state-pointer SOC) are computed first and fired in a
|
|
240
|
+
* single `Promise.all`. The pointer is written at the current rotating epoch
|
|
241
|
+
* bucket — no feed walk to publish, and the reader computes its address directly.
|
|
242
|
+
*/
|
|
243
|
+
export declare function writePartitionState(opts: {
|
|
244
|
+
bee: Bee;
|
|
245
|
+
stamper: Stamper;
|
|
246
|
+
batchId: BatchId;
|
|
247
|
+
batchDepth: number;
|
|
248
|
+
partition: number;
|
|
249
|
+
localCounter: Uint32Array;
|
|
250
|
+
backupSigner: PrivateKey;
|
|
251
|
+
swarmEncryptionKey: Uint8Array;
|
|
252
|
+
/** Refs from the previous publish; with `previousCounter`, enables incremental. */
|
|
253
|
+
previousReferences?: Uint8Array[];
|
|
254
|
+
/** The counter the previous publish wrote; diffed to find changed chunks. */
|
|
255
|
+
previousCounter?: Uint32Array;
|
|
256
|
+
/**
|
|
257
|
+
* This device's id. When provided, the publish also avoids the buckets of
|
|
258
|
+
* this device's current/previous-epoch intent beacons (the refresh tick
|
|
259
|
+
* writes them off-lock at the same reserved slot). Omit for read-only / the
|
|
260
|
+
* deviceId-independent paths — the pointer and occupancy buckets are avoided
|
|
261
|
+
* regardless.
|
|
262
|
+
*/
|
|
263
|
+
deviceId?: string;
|
|
264
|
+
/**
|
|
265
|
+
* Wall-clock ms the rotating pointer/beacon rendezvous addresses are anchored
|
|
266
|
+
* to. The lease passes its own clock (`this.now()`, = `Date.now()` in
|
|
267
|
+
* production) so writer, reader, and the collision detector all agree (#385);
|
|
268
|
+
* the reader anchors to the same clock. Defaults to `Date.now()` for bare
|
|
269
|
+
* callers (direct unit tests). Injectable so a test can drive rendezvous
|
|
270
|
+
* deterministically / model per-device skew.
|
|
271
|
+
*/
|
|
272
|
+
nowMs?: number;
|
|
273
|
+
}): Promise<{
|
|
274
|
+
/** Reference-chunk reference (hex) — the caller's "synced reference". */
|
|
275
|
+
referenceHex: string;
|
|
276
|
+
/**
|
|
277
|
+
* Buckets occupied by the current state chunks (retained + re-uploaded
|
|
278
|
+
* counter chunks + the reference chunk). The caller hands these to the
|
|
279
|
+
* stamper so subsequent utilisation saves avoid overstamping (evicting) the
|
|
280
|
+
* publish.
|
|
281
|
+
*/
|
|
282
|
+
stateBuckets: number[];
|
|
283
|
+
/** The full ref set after this publish, for the caller to cache. */
|
|
284
|
+
references: Uint8Array[];
|
|
285
|
+
/** Snapshot of the counter this publish wrote (pass as `previousCounter` next). */
|
|
286
|
+
publishedCounter: Uint32Array;
|
|
287
|
+
}>;
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Whether a session restored from storage may still write.
|
|
3
|
+
*
|
|
4
|
+
* A saved session outlives the page that made it, and a revoke made while no
|
|
5
|
+
* page of this site was open is written only to Swarm: the account's published
|
|
6
|
+
* state is the one place a Remove or a Disconnect from another device can be
|
|
7
|
+
* read. So the frame asks it before a restored session's first write, and an
|
|
8
|
+
* answer that never arrived is `unconfirmed`, never "not revoked".
|
|
9
|
+
*/
|
|
10
|
+
import { Bee } from "@ethersphere/bee-js";
|
|
11
|
+
/**
|
|
12
|
+
* - `allowed`: the published account still has this site connected, or the
|
|
13
|
+
* account has never published anything (it has no drive, so it cannot have
|
|
14
|
+
* published a revoke either).
|
|
15
|
+
* - `ended`: the published account removed the site, disconnected it after
|
|
16
|
+
* this session connected, or no longer lists it.
|
|
17
|
+
* - `unconfirmed`: the answer could not be read.
|
|
18
|
+
*/
|
|
19
|
+
export type SessionVerdict = "allowed" | "ended" | "unconfirmed";
|
|
20
|
+
export declare function checkRestoredSession(opts: {
|
|
21
|
+
bee: Bee;
|
|
22
|
+
derivationKey: string;
|
|
23
|
+
accountId: string;
|
|
24
|
+
appOrigin: string;
|
|
25
|
+
/** When this session's connection was made. Missing reads as 0, so a
|
|
26
|
+
* disconnect stands rather than being explained away by a clock that does
|
|
27
|
+
* not exist. */
|
|
28
|
+
connectedAt: number | undefined;
|
|
29
|
+
}): Promise<SessionVerdict>;
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Store Interfaces for Sync Coordinator
|
|
3
|
+
*
|
|
4
|
+
* These interfaces define the minimal contract that stores must implement
|
|
5
|
+
* to be used with the sync coordinator. This allows the coordinator to be
|
|
6
|
+
* used with different store implementations (Svelte stores, plain objects, etc.)
|
|
7
|
+
*/
|
|
8
|
+
import type { EthAddress, BatchId, Stamper } from "@ethersphere/bee-js";
|
|
9
|
+
import type { SyncedAccount, PostageStamp } from "../schemas";
|
|
10
|
+
/**
|
|
11
|
+
* Options for creating a stamper with utilization tracking
|
|
12
|
+
*/
|
|
13
|
+
export interface StamperOptions {
|
|
14
|
+
owner: EthAddress;
|
|
15
|
+
encryptionKey: Uint8Array;
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* Extended stamper interface with optional flush capability
|
|
19
|
+
*
|
|
20
|
+
* Some stampers (like UtilizationAwareStamper) support flushing
|
|
21
|
+
* dirty bucket state to cache and binding to a multi-device partition.
|
|
22
|
+
*/
|
|
23
|
+
export interface FlushableStamper extends Stamper {
|
|
24
|
+
flush?(): Promise<void>;
|
|
25
|
+
bindPartition?(opts: {
|
|
26
|
+
partition: number;
|
|
27
|
+
partitionCount: number;
|
|
28
|
+
localCounter: Uint32Array;
|
|
29
|
+
}): void;
|
|
30
|
+
buildLeaseLocalCounter?(): Uint32Array;
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Interface for accessing account data. The account is the aggregate root and
|
|
34
|
+
* owns its connected apps and postage stamps inline. The sync coordinator
|
|
35
|
+
* publishes to Swarm, so it sees only the `SyncedAccount` projection — the
|
|
36
|
+
* device-local vault never enters this seam.
|
|
37
|
+
*/
|
|
38
|
+
export interface AccountsStoreInterface {
|
|
39
|
+
getAccount(id: EthAddress): SyncedAccount | undefined;
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* Interface for accessing and managing postage stamp data
|
|
43
|
+
*/
|
|
44
|
+
export interface PostageStampsStoreInterface {
|
|
45
|
+
getStamp(batchID: BatchId): PostageStamp | undefined;
|
|
46
|
+
getStamper(batchID: BatchId, options?: StamperOptions): Promise<FlushableStamper | undefined>;
|
|
47
|
+
updateStampUtilization(batchID: BatchId, utilization: number): void;
|
|
48
|
+
}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Sync Account
|
|
3
|
+
*
|
|
4
|
+
* Factory function that creates a sync function for syncing account state
|
|
5
|
+
* to Swarm. This integrates store access, key derivation, and utilization
|
|
6
|
+
* tracking.
|
|
7
|
+
*/
|
|
8
|
+
import { Bee } from "@ethersphere/bee-js";
|
|
9
|
+
import type { UtilizationStoreDB } from "../storage/utilization-store";
|
|
10
|
+
import type { DebouncedUtilizationUploader } from "../storage/debounced-uploader";
|
|
11
|
+
import type { AccountsStoreInterface, PostageStampsStoreInterface } from "./store-interfaces";
|
|
12
|
+
import type { SyncResult } from "./types";
|
|
13
|
+
/**
|
|
14
|
+
* Options for creating a sync account function
|
|
15
|
+
*/
|
|
16
|
+
export interface SyncAccountOptions {
|
|
17
|
+
/** Bee client for Swarm operations */
|
|
18
|
+
bee: Bee;
|
|
19
|
+
/** Store providing account data (the account owns its apps + stamps) */
|
|
20
|
+
accountsStore: AccountsStoreInterface;
|
|
21
|
+
/** Store providing postage stamp runtime access (stamper/utilization) */
|
|
22
|
+
postageStampsStore: PostageStampsStoreInterface;
|
|
23
|
+
/** Utilization store for browser-based utilization tracking */
|
|
24
|
+
utilizationStore: UtilizationStoreDB;
|
|
25
|
+
/** Debounced uploader for batch utilization state */
|
|
26
|
+
utilizationUploader: DebouncedUtilizationUploader;
|
|
27
|
+
/**
|
|
28
|
+
* Devices heard live on the account bus (`PresenceTracker`), unioned into
|
|
29
|
+
* the partition rival set so this one-shot coordinator and the proxy's agree
|
|
30
|
+
* about who the rivals are. Optional: a caller with no bus has only the
|
|
31
|
+
* registry's recent sign-ins to go on.
|
|
32
|
+
*/
|
|
33
|
+
liveDeviceIds?: () => string[];
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* Sync account function type
|
|
37
|
+
*/
|
|
38
|
+
export type SyncAccountFunction = (accountId: string) => Promise<SyncResult | undefined>;
|
|
39
|
+
/**
|
|
40
|
+
* Create a sync account function with dependency-injected stores
|
|
41
|
+
*
|
|
42
|
+
* @param options - Configuration options including stores and optional utilization tracking
|
|
43
|
+
* @returns Function that syncs an account to Swarm
|
|
44
|
+
*/
|
|
45
|
+
export declare function createSyncAccount(options: SyncAccountOptions): SyncAccountFunction;
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Cross-module timing constants for the multi-device sync path.
|
|
3
|
+
*
|
|
4
|
+
* This module has NO imports on purpose: it is the single source of truth for
|
|
5
|
+
* values several sync modules share, so it can be imported by any of them
|
|
6
|
+
* (including `utils/batch-utilization`) without a circular import. Keeping the
|
|
7
|
+
* values here removes the drift risk of the same literal defined independently
|
|
8
|
+
* in each module.
|
|
9
|
+
*/
|
|
10
|
+
/**
|
|
11
|
+
* Client-side timeout for a single cross-device SOC/feed read (roster entry,
|
|
12
|
+
* partition-intent, lock SOC, state-pointer). Bee has no fast authoritative
|
|
13
|
+
* "not found" — a retrieval of an absent chunk fails only after exhausting
|
|
14
|
+
* peers — so an absent target would otherwise block for tens of seconds. A
|
|
15
|
+
* present chunk in its neighborhood retrieves well under this; a timed-out read
|
|
16
|
+
* is treated as inconclusive (see each caller's sentinel handling).
|
|
17
|
+
*/
|
|
18
|
+
export declare const SYNC_READ_TIMEOUT_MS = 2500;
|
|
19
|
+
/**
|
|
20
|
+
* Partition-lease lifetime. A device holds its partition for this long before
|
|
21
|
+
* the lease must be refreshed; if it crashes without refreshing, peers can
|
|
22
|
+
* reclaim the partition.
|
|
23
|
+
*/
|
|
24
|
+
export declare const LEASE_TTL_MS: number;
|
|
25
|
+
/**
|
|
26
|
+
* Epoch length for the rotating intent/occupancy/state-pointer addresses.
|
|
27
|
+
* TTL-sized (`= LEASE_TTL_MS`) so a contention round and its immediate retries
|
|
28
|
+
* share a bucket (and the previous bucket covers the boundary), while every
|
|
29
|
+
* fresh round rotates to an address no node has cached. The two genuinely track
|
|
30
|
+
* each other — sharing this source keeps them from drifting.
|
|
31
|
+
*/
|
|
32
|
+
export declare const INTENT_EPOCH_MS: number;
|