@optimystic/db-core 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/dist/src/blocks/helpers.d.ts +5 -0
- package/dist/src/blocks/helpers.d.ts.map +1 -1
- package/dist/src/blocks/helpers.js +12 -0
- package/dist/src/blocks/helpers.js.map +1 -1
- package/dist/src/cluster/membership.d.ts +7 -0
- package/dist/src/cluster/membership.d.ts.map +1 -1
- package/dist/src/cluster/membership.js +12 -8
- package/dist/src/cluster/membership.js.map +1 -1
- package/dist/src/cluster/structs.d.ts +27 -1
- package/dist/src/cluster/structs.d.ts.map +1 -1
- package/dist/src/cluster/structs.js.map +1 -1
- package/dist/src/collection/action.d.ts +14 -0
- package/dist/src/collection/action.d.ts.map +1 -1
- package/dist/src/collection/action.js +16 -1
- package/dist/src/collection/action.js.map +1 -1
- package/dist/src/collection/collection.d.ts +273 -4
- package/dist/src/collection/collection.d.ts.map +1 -1
- package/dist/src/collection/collection.js +427 -30
- package/dist/src/collection/collection.js.map +1 -1
- package/dist/src/collections/tree/tree.d.ts +16 -1
- package/dist/src/collections/tree/tree.d.ts.map +1 -1
- package/dist/src/collections/tree/tree.js +19 -0
- package/dist/src/collections/tree/tree.js.map +1 -1
- package/dist/src/index.d.ts +1 -0
- package/dist/src/index.d.ts.map +1 -1
- package/dist/src/index.js +1 -0
- package/dist/src/index.js.map +1 -1
- package/dist/src/network/i-repo.d.ts +11 -1
- package/dist/src/network/i-repo.d.ts.map +1 -1
- package/dist/src/network/stale-failure.d.ts +21 -0
- package/dist/src/network/stale-failure.d.ts.map +1 -1
- package/dist/src/network/stale-failure.js +22 -0
- package/dist/src/network/stale-failure.js.map +1 -1
- package/dist/src/network/struct.d.ts +66 -21
- package/dist/src/network/struct.d.ts.map +1 -1
- package/dist/src/network/struct.js.map +1 -1
- package/dist/src/testing/test-transactor.d.ts +22 -0
- package/dist/src/testing/test-transactor.d.ts.map +1 -1
- package/dist/src/testing/test-transactor.js +44 -5
- package/dist/src/testing/test-transactor.js.map +1 -1
- package/dist/src/transaction/coordinator.d.ts +20 -0
- package/dist/src/transaction/coordinator.d.ts.map +1 -1
- package/dist/src/transaction/coordinator.js +256 -113
- package/dist/src/transaction/coordinator.js.map +1 -1
- package/dist/src/transaction/operations-hash.d.ts +1 -1
- package/dist/src/transaction/operations-hash.js +1 -1
- package/dist/src/transaction/transaction.d.ts +4 -2
- package/dist/src/transaction/transaction.d.ts.map +1 -1
- package/dist/src/transactor/network-transactor.d.ts +21 -1
- package/dist/src/transactor/network-transactor.d.ts.map +1 -1
- package/dist/src/transactor/network-transactor.js +122 -29
- package/dist/src/transactor/network-transactor.js.map +1 -1
- package/dist/src/transactor/transactor-source.d.ts +11 -5
- package/dist/src/transactor/transactor-source.d.ts.map +1 -1
- package/dist/src/transactor/transactor-source.js +16 -8
- package/dist/src/transactor/transactor-source.js.map +1 -1
- package/dist/src/transform/cache-source.d.ts +13 -0
- package/dist/src/transform/cache-source.d.ts.map +1 -1
- package/dist/src/transform/cache-source.js +18 -0
- package/dist/src/transform/cache-source.js.map +1 -1
- package/dist/src/transform/digest.d.ts +18 -0
- package/dist/src/transform/digest.d.ts.map +1 -0
- package/dist/src/transform/digest.js +65 -0
- package/dist/src/transform/digest.js.map +1 -0
- package/dist/src/transform/index.d.ts +1 -0
- package/dist/src/transform/index.d.ts.map +1 -1
- package/dist/src/transform/index.js +1 -0
- package/dist/src/transform/index.js.map +1 -1
- package/dist/src/transform/tracker.d.ts +16 -0
- package/dist/src/transform/tracker.d.ts.map +1 -1
- package/dist/src/transform/tracker.js +40 -1
- package/dist/src/transform/tracker.js.map +1 -1
- package/dist/src/utility/canonical-json.d.ts +11 -0
- package/dist/src/utility/canonical-json.d.ts.map +1 -0
- package/dist/src/utility/canonical-json.js +15 -0
- package/dist/src/utility/canonical-json.js.map +1 -0
- package/dist/src/utility/lru-map.d.ts +2 -0
- package/dist/src/utility/lru-map.d.ts.map +1 -1
- package/dist/src/utility/lru-map.js +4 -0
- package/dist/src/utility/lru-map.js.map +1 -1
- package/package.json +2 -1
- package/src/blocks/helpers.ts +26 -13
- package/src/cluster/membership.ts +87 -85
- package/src/cluster/structs.ts +28 -1
- package/src/collection/action.ts +17 -0
- package/src/collection/collection.ts +1129 -688
- package/src/collections/tree/tree.ts +341 -320
- package/src/index.ts +24 -23
- package/src/network/i-repo.ts +59 -46
- package/src/network/stale-failure.ts +67 -43
- package/src/network/struct.ts +332 -270
- package/src/testing/test-transactor.ts +680 -638
- package/src/transaction/coordinator.ts +1266 -1110
- package/src/transaction/operations-hash.ts +1 -1
- package/src/transaction/transaction.ts +4 -2
- package/src/transactor/network-transactor.ts +123 -32
- package/src/transactor/transactor-source.ts +17 -9
- package/src/transform/cache-source.ts +20 -0
- package/src/transform/digest.ts +72 -0
- package/src/transform/index.ts +1 -0
- package/src/transform/tracker.ts +42 -1
- package/src/utility/canonical-json.ts +16 -0
- package/src/utility/lru-map.ts +5 -0
package/src/network/struct.ts
CHANGED
|
@@ -1,270 +1,332 @@
|
|
|
1
|
-
import type { CollectionId, BlockId, IBlock, ActionId, Transform, Transforms } from "../index.js";
|
|
2
|
-
import type { ActionContext, ActionRev } from "../collection/action.js";
|
|
3
|
-
import type { Transaction } from "../transaction/transaction.js";
|
|
4
|
-
import type { DisputeResolutionProof } from "../log/struct.js";
|
|
5
|
-
import type { PeerId } from "./types.js";
|
|
6
|
-
|
|
7
|
-
export type ActionBlocks = {
|
|
8
|
-
blockIds: BlockId[];
|
|
9
|
-
actionId: ActionId;
|
|
10
|
-
};
|
|
11
|
-
|
|
12
|
-
export type ActionTransforms = {
|
|
13
|
-
actionId: ActionId;
|
|
14
|
-
rev?: number;
|
|
15
|
-
transforms: Transforms;
|
|
16
|
-
};
|
|
17
|
-
|
|
18
|
-
export type ActionTransform = {
|
|
19
|
-
actionId: ActionId;
|
|
20
|
-
rev?: number;
|
|
21
|
-
transform: Transform;
|
|
22
|
-
};
|
|
23
|
-
|
|
24
|
-
export type ActionPending = {
|
|
25
|
-
blockId: BlockId;
|
|
26
|
-
actionId: ActionId;
|
|
27
|
-
transform?: Transform;
|
|
28
|
-
};
|
|
29
|
-
|
|
30
|
-
export type PendRequest = ActionTransforms & {
|
|
31
|
-
/** What to do if there are any pending actions.
|
|
32
|
-
* 'c' is continue normally,
|
|
33
|
-
* 'f' is fail, returning the pending ActionIds,
|
|
34
|
-
* 'r' is return, which fails but returns the pending ActionIds and their transforms */
|
|
35
|
-
policy: 'c' | 'f' | 'r';
|
|
36
|
-
/**
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
*
|
|
44
|
-
*
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
};
|
|
65
|
-
|
|
66
|
-
export type
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
/** List of actions that
|
|
73
|
-
pending
|
|
74
|
-
/**
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
/**
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
*
|
|
88
|
-
*
|
|
89
|
-
*
|
|
90
|
-
*
|
|
91
|
-
*
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
};
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
*
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
/**
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
*/
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
/**
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
1
|
+
import type { CollectionId, BlockId, IBlock, ActionId, Transform, Transforms } from "../index.js";
|
|
2
|
+
import type { ActionContext, ActionRev } from "../collection/action.js";
|
|
3
|
+
import type { Transaction } from "../transaction/transaction.js";
|
|
4
|
+
import type { DisputeResolutionProof } from "../log/struct.js";
|
|
5
|
+
import type { PeerId } from "./types.js";
|
|
6
|
+
|
|
7
|
+
export type ActionBlocks = {
|
|
8
|
+
blockIds: BlockId[];
|
|
9
|
+
actionId: ActionId;
|
|
10
|
+
};
|
|
11
|
+
|
|
12
|
+
export type ActionTransforms = {
|
|
13
|
+
actionId: ActionId;
|
|
14
|
+
rev?: number;
|
|
15
|
+
transforms: Transforms;
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
export type ActionTransform = {
|
|
19
|
+
actionId: ActionId;
|
|
20
|
+
rev?: number;
|
|
21
|
+
transform: Transform;
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
export type ActionPending = {
|
|
25
|
+
blockId: BlockId;
|
|
26
|
+
actionId: ActionId;
|
|
27
|
+
transform?: Transform;
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
export type PendRequest = ActionTransforms & {
|
|
31
|
+
/** What to do if there are any pending actions.
|
|
32
|
+
* 'c' is continue normally,
|
|
33
|
+
* 'f' is fail, returning the pending ActionIds,
|
|
34
|
+
* 'r' is return, which fails but returns the pending ActionIds and their transforms */
|
|
35
|
+
policy: 'c' | 'f' | 'r';
|
|
36
|
+
/**
|
|
37
|
+
* Present only on the multi-collection path (`TransactionCoordinator.pendCollection`): the
|
|
38
|
+
* transaction to re-execute plus the hash of ALL operations across all blocks it must produce.
|
|
39
|
+
* Absent on the single-collection `Collection.sync` path, which carries bare transforms and is
|
|
40
|
+
* therefore not re-checkable — see `ClusterConsensusConfig.unvalidatablePendPolicy` for what a
|
|
41
|
+
* validating receiver does with that shape. ONE optional pair, deliberately: "transaction
|
|
42
|
+
* without its hash" (or the reverse) was a state the old two independent optional fields
|
|
43
|
+
* permitted and no producer ever created — and a receiver whose guard required both could be
|
|
44
|
+
* talked out of validating by a sender that omitted one.
|
|
45
|
+
*/
|
|
46
|
+
validation?: {
|
|
47
|
+
/** The full transaction for replay/validation. */
|
|
48
|
+
transaction: Transaction;
|
|
49
|
+
/** Hash of ALL operations across all blocks the re-executed transaction must produce. */
|
|
50
|
+
operationsHash: string;
|
|
51
|
+
};
|
|
52
|
+
/** For multi-collection transactions: supercluster nominees for consensus */
|
|
53
|
+
superclusterNominees?: PeerId[];
|
|
54
|
+
/**
|
|
55
|
+
* Aged, advisory retry priority for the *single-collection* pend path (default 0 when absent).
|
|
56
|
+
* The multi-collection path instead carries priority on the transaction inside
|
|
57
|
+
* {@link PendRequest.validation} ({@link Transaction.priority}); this top-level field is the
|
|
58
|
+
* carrier for a `Collection.sync` pend, which has no transaction. A cluster member reads whichever is present as the first
|
|
59
|
+
* `resolveRace` tiebreak. FAIRNESS-ONLY: it rides inside the signed cluster `message` (so it is
|
|
60
|
+
* integrity-protected in transit) but MUST NOT affect the operations hash, stale-read checks, or
|
|
61
|
+
* validity — a stale pend is still rejected regardless of priority.
|
|
62
|
+
*/
|
|
63
|
+
priority?: number;
|
|
64
|
+
};
|
|
65
|
+
|
|
66
|
+
export type BlockActionStatus = ActionBlocks & {
|
|
67
|
+
statuses: ('pending' | 'committed' | 'checkpointed' | 'aborted' | 'committed-invalidated')[];
|
|
68
|
+
};
|
|
69
|
+
|
|
70
|
+
export type PendSuccess = {
|
|
71
|
+
success: true;
|
|
72
|
+
/** List of already pending actions that were found on blocks touched by this pend */
|
|
73
|
+
pending: ActionPending[];
|
|
74
|
+
/** The affected blocks */
|
|
75
|
+
blockIds: BlockId[];
|
|
76
|
+
};
|
|
77
|
+
|
|
78
|
+
export type StaleFailure = {
|
|
79
|
+
success: false;
|
|
80
|
+
/** The reason for the failure */
|
|
81
|
+
reason?: string;
|
|
82
|
+
/** List of actions that have already been committed and are newer than our known revision */
|
|
83
|
+
missing?: ActionTransforms[];
|
|
84
|
+
/** List of actions that are pending on the blocks touched by this pend */
|
|
85
|
+
pending?: ActionPending[];
|
|
86
|
+
/**
|
|
87
|
+
* Explicit retryability. True when this failure is an optimistic-concurrency loss — the
|
|
88
|
+
* requested revision was taken, or a rival pend holds the blocks — so a re-read, rebase and
|
|
89
|
+
* re-pend can win. Set it only when the producer genuinely classified the failure; leave it
|
|
90
|
+
* absent otherwise, and consumers fall back to inferring from `missing`/`pending`.
|
|
91
|
+
* Read it through `isConflictFailure` rather than testing it directly.
|
|
92
|
+
*/
|
|
93
|
+
conflict?: boolean;
|
|
94
|
+
/**
|
|
95
|
+
* The block that already occupies (or is past) the requested revision, and the revision the
|
|
96
|
+
* responder holds for it.
|
|
97
|
+
*
|
|
98
|
+
* CONFIRMED-ONLY: set this only when the producer read the revision out of its own storage.
|
|
99
|
+
* A producer that merely suspects staleness — or that learned of it from another peer's
|
|
100
|
+
* free-form reject text — must leave it absent. Absent means "no confirmed number", never
|
|
101
|
+
* "not stale".
|
|
102
|
+
*
|
|
103
|
+
* DIAGNOSTIC, NOT A RETRYABILITY SIGNAL: `conflict` (read via `isConflictFailure`) remains the
|
|
104
|
+
* single source of truth for "can a re-read and re-pend win?". Never branch retry decisions on
|
|
105
|
+
* the presence of this field.
|
|
106
|
+
*/
|
|
107
|
+
staleAt?: { blockId: BlockId; rev: number };
|
|
108
|
+
};
|
|
109
|
+
|
|
110
|
+
export type PendResult = PendSuccess | StaleFailure;
|
|
111
|
+
|
|
112
|
+
/** What one block will materialize to at the committing revision, declared by the client that
|
|
113
|
+
* authored the transforms. */
|
|
114
|
+
export type BlockContentDigest = {
|
|
115
|
+
/** base64url SHA-256 of canonicalJson(block) - see canonicalBlockHash. */
|
|
116
|
+
digest: string;
|
|
117
|
+
/** Committed revision of the base the digest was computed from. ABSENT when the block's
|
|
118
|
+
* transform carries an insert, which makes the result base-independent and therefore
|
|
119
|
+
* checkable by every member regardless of how far behind it is. */
|
|
120
|
+
baseRev?: number;
|
|
121
|
+
};
|
|
122
|
+
|
|
123
|
+
/** Per-block content declarations riding on a commit. Optional per id: a block the client cannot
|
|
124
|
+
* digest without a network read is simply omitted, and falls back to corroboration downstream. */
|
|
125
|
+
export type BlockContentDigests = Record<BlockId, BlockContentDigest>;
|
|
126
|
+
|
|
127
|
+
export type CommitRequest = ActionBlocks & {
|
|
128
|
+
/** The header block of the collection, if this is a new collection. Nominally "commit first" —
|
|
129
|
+
* but see the NOTE in `NetworkTransactor.commit`: the header-first step is unreachable from the
|
|
130
|
+
* only production producer of this field, so today it only affects hand-built requests. */
|
|
131
|
+
headerId?: BlockId;
|
|
132
|
+
/** The tail block of the log. Nominally "commit next"; since the header-first step above never
|
|
133
|
+
* fires in production, this is in practice the FIRST block committed, which is the ordering
|
|
134
|
+
* `Collection.bootstrapContext` relies on. */
|
|
135
|
+
tailId: BlockId;
|
|
136
|
+
/** The new revision for the committed action */
|
|
137
|
+
rev: number;
|
|
138
|
+
/** Per-block content declarations for the committing action — see {@link BlockContentDigests}.
|
|
139
|
+
* Rides inside the consensus message, so the generic cluster message hash folds it into every
|
|
140
|
+
* cohort signature with no change to the hash helpers. Action-wide here; the transactor narrows
|
|
141
|
+
* it to each per-coordinator batch's own block ids before sending (`RepoCommitRequest`). */
|
|
142
|
+
// NOTE: accepted tradeoff — this field stays OPTIONAL even though an omitted declaration has a real
|
|
143
|
+
// cost: `StorageRepo.persistProofIfContentMatches` retains no `BlockCommitProof` for a block that
|
|
144
|
+
// declared no digest (`commit:proof-undeclared`), and a block with no proof is refused by any
|
|
145
|
+
// receiver running the default `requirePushCertificate: true` (`push:reject-uncertified
|
|
146
|
+
// reason=no-proof`). Such a block stays readable and pullable (`handlePull` is not certificate-gated)
|
|
147
|
+
// and still repairs by corroboration while two or more holders remain, but it can never GAIN a
|
|
148
|
+
// holder by push — so spread-on-churn and cohort-growth healing silently stop maintaining its
|
|
149
|
+
// replication factor. Kept optional anyway because: (a) required-but-nullable is the strongest form
|
|
150
|
+
// available and still permits `undefined`, so it does not make the bad state unrepresentable, only
|
|
151
|
+
// typed out loud; (b) some commits legitimately declare nothing — delete-only/tombstone commits
|
|
152
|
+
// materialize no content, and a member on a lagging base abstains; (c) measured migration cost of
|
|
153
|
+
// required-but-nullable here and on `RepoCommitRequest` is >=194 `tsc --noEmit` errors across >=39
|
|
154
|
+
// files (39/9 in db-core, 155/30 in db-p2p, quereus-plugin-optimystic unmeasured), nearly all fixed
|
|
155
|
+
// by literally writing `undefined`. Revisit if undeclared commits ever become common enough to show
|
|
156
|
+
// up as replication-factor decay.
|
|
157
|
+
blockDigests?: BlockContentDigests;
|
|
158
|
+
};
|
|
159
|
+
|
|
160
|
+
/**
|
|
161
|
+
* Originates a compensating invalidation through the same critical-cluster consensus as any
|
|
162
|
+
* transaction: it takes a revision slot and serializes against concurrent commits. Each member
|
|
163
|
+
* applies it deterministically — verifying {@link resolution} as an invalidation certificate, then
|
|
164
|
+
* writing the per-block compensating revisions and appending the durable invalidation log entry.
|
|
165
|
+
*
|
|
166
|
+
* Carries everything a member needs to apply the reversal without trusting the originator: the
|
|
167
|
+
* target action, the blocks it wrote, the owning collection's log, and the signed proof.
|
|
168
|
+
*/
|
|
169
|
+
export type InvalidateRequest = {
|
|
170
|
+
/** actionId of the committed action being reversed. */
|
|
171
|
+
invalidatedActionId: ActionId;
|
|
172
|
+
/** rev of the invalidated entry — pins which block revisions to roll back. */
|
|
173
|
+
invalidatedRev: number;
|
|
174
|
+
/** Blocks the invalidated action wrote (its commit's blockIds). */
|
|
175
|
+
blockIds: BlockId[];
|
|
176
|
+
/** The collection (log) the invalidated action belongs to — where the compensating entry lands. */
|
|
177
|
+
collectionId: CollectionId;
|
|
178
|
+
/** The invalidation certificate (challenger-wins + signed 2/3 decisive arbitrator votes). */
|
|
179
|
+
resolution: DisputeResolutionProof;
|
|
180
|
+
};
|
|
181
|
+
|
|
182
|
+
export type CommitResult = CommitSuccess | StaleFailure;
|
|
183
|
+
|
|
184
|
+
export type CommitSuccess = {
|
|
185
|
+
success: true;
|
|
186
|
+
/** If present, the identified collection acts as the coordinator for the multi-collection transaction */
|
|
187
|
+
coordinatorId?: CollectionId;
|
|
188
|
+
};
|
|
189
|
+
|
|
190
|
+
export type BlockActionState = {
|
|
191
|
+
/** The latest action that has been committed */
|
|
192
|
+
latest?: ActionRev;
|
|
193
|
+
/** If present, the specified actions are pending */
|
|
194
|
+
pendings?: ActionId[];
|
|
195
|
+
};
|
|
196
|
+
|
|
197
|
+
export type BlockGets = {
|
|
198
|
+
blockIds: BlockId[];
|
|
199
|
+
context?: ActionContext; // Latest if this is omitted
|
|
200
|
+
};
|
|
201
|
+
|
|
202
|
+
/** Why a repo could not establish whether a block exists. Present ONLY when the repo
|
|
203
|
+
* knows its own answer is a guess; an absent field is an authoritative answer. */
|
|
204
|
+
export type BlockUnavailableReason =
|
|
205
|
+
/** Records for this block exist here but it cannot be reconstructed locally — a
|
|
206
|
+
* revision was received with no base to apply it to, or its history is truncated. */
|
|
207
|
+
| 'unmaterializable'
|
|
208
|
+
/** Nothing is held locally; PART of the cohort answered and part could not be asked.
|
|
209
|
+
* A silent peer could be the sole holder, so the absence is a guess — but other
|
|
210
|
+
* coordinators are reachable, so asking one of them can still settle it. Also the
|
|
211
|
+
* fallback when the consult could not run at all (the cohort lookup itself failed):
|
|
212
|
+
* a routing failure says nothing about how many cohort members were reachable. */
|
|
213
|
+
| 'peers-unreachable'
|
|
214
|
+
/** Nothing is held locally and NO cohort member outside the answering node could be
|
|
215
|
+
* asked at all. Distinct from `peers-unreachable` in exactly the way that matters to
|
|
216
|
+
* a caller: there is no better-connected coordinator to re-ask, so the answer will
|
|
217
|
+
* not improve until that node's connectivity does. Its local view is all there is. */
|
|
218
|
+
| 'cohort-unreachable'
|
|
219
|
+
/** Nothing is held locally, but a cohort peer positively CLAIMED a revision of this
|
|
220
|
+
* block, and the answering node could neither corroborate that claim to a quorum nor
|
|
221
|
+
* acquire the content. The block is known to exist somewhere; reporting it absent
|
|
222
|
+
* would be a lie regardless of whether anyone was silent. */
|
|
223
|
+
| 'claimed-elsewhere';
|
|
224
|
+
|
|
225
|
+
export type GetBlockResult = {
|
|
226
|
+
/** The retrieved block - undefined if the block was deleted */
|
|
227
|
+
block?: IBlock;
|
|
228
|
+
/** The latest and pending states of the repo that retrieved the block */
|
|
229
|
+
state: BlockActionState;
|
|
230
|
+
/** The revision the returned `block` actually IS — the `(rev, actionId)` of the highest
|
|
231
|
+
* committed revision of THIS block at or below the caller's {@link BlockGets.context}`.rev`.
|
|
232
|
+
* Differs from `state.latest` only for a revision-pinned read of a block that has committed
|
|
233
|
+
* further since the pin; for an unpinned read the two agree.
|
|
234
|
+
*
|
|
235
|
+
* This — not `state.latest` — is what a read observed, so it is what a read dependency must
|
|
236
|
+
* record (recording `latest` would claim the reader saw content it never read, and the
|
|
237
|
+
* validator's stale-read check would wrongly pass), and it is the only correct label for the
|
|
238
|
+
* content when it is passed on (a block-repair archive, a replica push). `state.latest` keeps
|
|
239
|
+
* its own meaning: the newest revision the answering repo holds for the block.
|
|
240
|
+
*
|
|
241
|
+
* The revision and its action id are ONE field, deliberately: a site that must label content
|
|
242
|
+
* it is holding needs both, and two independently-optional fields could disagree — which is
|
|
243
|
+
* exactly the mislabel this exists to make unrepresentable (old bytes served under a newer
|
|
244
|
+
* revision's number and action id, which a receiver keyed by action id then writes over its
|
|
245
|
+
* own good copy; see `serveBlockArchive`).
|
|
246
|
+
*
|
|
247
|
+
* Optional: a producer that does not know what it materialized leaves it absent rather than
|
|
248
|
+
* guessing, and consumers fall back to `state.latest` (read dependencies record
|
|
249
|
+
* `state.latest?.rev ?? 0`; a labelling site refuses to label a pinned read). */
|
|
250
|
+
materialized?: ActionRev;
|
|
251
|
+
/** Set when this repo could not determine whether the block exists — its answer is a
|
|
252
|
+
* guess, not an authoritative absent. Every producer that omits it (including
|
|
253
|
+
* TestTransactor) keeps meaning "authoritative". */
|
|
254
|
+
unavailable?: BlockUnavailableReason;
|
|
255
|
+
/** Set when this repo served committed content it could NOT confirm is current: its
|
|
256
|
+
* freshness consult did not converge AND a cohort peer claimed a strictly higher
|
|
257
|
+
* revision than the one served, within the view the caller asked for (unpinned, or
|
|
258
|
+
* pinned at or above the claim). Carries that claimed revision. The claim did not
|
|
259
|
+
* drive a successful repair — it failed the read-repair corroboration quorum, or was
|
|
260
|
+
* corroborated but the content could not be acquired — so it is evidence of DOUBT,
|
|
261
|
+
* never a revision to adopt. Distinct from `unavailable`, which is about EXISTENCE:
|
|
262
|
+
* the content here is real, it may just be behind. Absent = confirmed, so every
|
|
263
|
+
* producer that omits it keeps its meaning. */
|
|
264
|
+
unconfirmedAheadRev?: number;
|
|
265
|
+
};
|
|
266
|
+
|
|
267
|
+
/**
|
|
268
|
+
* Thrown by a block read when the responsible repo could not determine whether the
|
|
269
|
+
* block exists. Distinct from "the block is absent" (undefined) and from a transport
|
|
270
|
+
* failure — this node's data is genuinely indeterminate and the caller must not treat
|
|
271
|
+
* it as empty. Not a StaleFailure: `Collection.sync` does not retry it.
|
|
272
|
+
*/
|
|
273
|
+
export class BlockUnavailableError extends Error {
|
|
274
|
+
constructor(readonly blockId: BlockId, readonly reason: BlockUnavailableReason) {
|
|
275
|
+
super(`Block ${blockId} is unavailable (${reason}): the repo could not determine whether it exists`);
|
|
276
|
+
this.name = 'BlockUnavailableError';
|
|
277
|
+
}
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
/**
|
|
281
|
+
* Thrown by an unpinned ("give me latest") block read whose surviving answer carries
|
|
282
|
+
* {@link GetBlockResult.unconfirmedAheadRev}: every reachable coordinator served content
|
|
283
|
+
* it could not confirm is current, while a cohort peer claimed a strictly higher revision
|
|
284
|
+
* nothing could corroborate or refute. Sibling of {@link BlockUnavailableError} — that one
|
|
285
|
+
* is about EXISTENCE (blockless answer, could not find out), this one about CURRENCY (real
|
|
286
|
+
* content, possibly behind). Not a StaleFailure: `Collection.sync` does not retry it.
|
|
287
|
+
*/
|
|
288
|
+
export class BlockPossiblyStaleError extends Error {
|
|
289
|
+
constructor(readonly blockId: BlockId, readonly claimedRev: number) {
|
|
290
|
+
super(`Block ${blockId} may be stale: a cohort peer claimed rev ${claimedRev} that no reachable coordinator could confirm or refute`);
|
|
291
|
+
this.name = 'BlockPossiblyStaleError';
|
|
292
|
+
}
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
export type GetBlockResults = Record<BlockId, GetBlockResult>;
|
|
296
|
+
|
|
297
|
+
/**
|
|
298
|
+
* Result of validating a transaction in a PendRequest.
|
|
299
|
+
*/
|
|
300
|
+
export type PendValidationResult = {
|
|
301
|
+
/** Whether validation passed */
|
|
302
|
+
valid: boolean;
|
|
303
|
+
/** Reason for validation failure (if valid=false) */
|
|
304
|
+
reason?: string;
|
|
305
|
+
};
|
|
306
|
+
|
|
307
|
+
/**
|
|
308
|
+
* Hook for validating transactions in PendRequests.
|
|
309
|
+
*
|
|
310
|
+
* This hook is called by the storage layer when receiving a PendRequest
|
|
311
|
+
* that carries a `validation` payload (the transaction plus its operations
|
|
312
|
+
* hash). If validation fails — or the hook throws — the pend operation is
|
|
313
|
+
* rejected. What a hook-configured node does with a pend carrying NO
|
|
314
|
+
* `validation` payload is a policy decision
|
|
315
|
+
* (`StorageRepoOptions.unvalidatablePendPolicy` in db-p2p).
|
|
316
|
+
*
|
|
317
|
+
* If the hook is not provided, validation is skipped (storage-only nodes).
|
|
318
|
+
*/
|
|
319
|
+
export type PendValidationHook = (
|
|
320
|
+
transaction: Transaction,
|
|
321
|
+
operationsHash: string
|
|
322
|
+
) => Promise<PendValidationResult>;
|
|
323
|
+
|
|
324
|
+
// Backward compatibility aliases (deprecated - use Action* names)
|
|
325
|
+
/** @deprecated Use ActionBlocks instead */
|
|
326
|
+
export type TrxBlocks = ActionBlocks;
|
|
327
|
+
/** @deprecated Use ActionTransforms instead */
|
|
328
|
+
export type TrxTransforms = ActionTransforms;
|
|
329
|
+
/** @deprecated Use ActionTransform instead */
|
|
330
|
+
export type TrxTransform = ActionTransform;
|
|
331
|
+
/** @deprecated Use ActionPending instead */
|
|
332
|
+
export type TrxPending = ActionPending;
|