@optimystic/db-p2p 0.25.1 → 0.27.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (107) hide show
  1. package/dist/src/cluster/certified-claims.d.ts +17 -3
  2. package/dist/src/cluster/certified-claims.d.ts.map +1 -1
  3. package/dist/src/cluster/certified-claims.js +5 -3
  4. package/dist/src/cluster/certified-claims.js.map +1 -1
  5. package/dist/src/cluster/cluster-repo.d.ts +57 -92
  6. package/dist/src/cluster/cluster-repo.d.ts.map +1 -1
  7. package/dist/src/cluster/cluster-repo.js +149 -201
  8. package/dist/src/cluster/cluster-repo.js.map +1 -1
  9. package/dist/src/cluster/commit-proof.d.ts +16 -0
  10. package/dist/src/cluster/commit-proof.d.ts.map +1 -1
  11. package/dist/src/cluster/commit-proof.js +32 -1
  12. package/dist/src/cluster/commit-proof.js.map +1 -1
  13. package/dist/src/cluster/quorum-restore.d.ts +81 -28
  14. package/dist/src/cluster/quorum-restore.d.ts.map +1 -1
  15. package/dist/src/cluster/quorum-restore.js +148 -51
  16. package/dist/src/cluster/quorum-restore.js.map +1 -1
  17. package/dist/src/cluster/race-resolution.d.ts +89 -0
  18. package/dist/src/cluster/race-resolution.d.ts.map +1 -0
  19. package/dist/src/cluster/race-resolution.js +140 -0
  20. package/dist/src/cluster/race-resolution.js.map +1 -0
  21. package/dist/src/cluster/reconcile-block.d.ts +9 -4
  22. package/dist/src/cluster/reconcile-block.d.ts.map +1 -1
  23. package/dist/src/cluster/reconcile-block.js +28 -11
  24. package/dist/src/cluster/reconcile-block.js.map +1 -1
  25. package/dist/src/cluster/record-operations.d.ts +25 -0
  26. package/dist/src/cluster/record-operations.d.ts.map +1 -0
  27. package/dist/src/cluster/record-operations.js +56 -0
  28. package/dist/src/cluster/record-operations.js.map +1 -0
  29. package/dist/src/cohort-topic/host.d.ts +133 -51
  30. package/dist/src/cohort-topic/host.d.ts.map +1 -1
  31. package/dist/src/cohort-topic/host.js +290 -83
  32. package/dist/src/cohort-topic/host.js.map +1 -1
  33. package/dist/src/libp2p-node-base.d.ts +13 -4
  34. package/dist/src/libp2p-node-base.d.ts.map +1 -1
  35. package/dist/src/libp2p-node-base.js +5 -5
  36. package/dist/src/libp2p-node-base.js.map +1 -1
  37. package/dist/src/repo/cluster-coordinator.d.ts +9 -0
  38. package/dist/src/repo/cluster-coordinator.d.ts.map +1 -1
  39. package/dist/src/repo/cluster-coordinator.js +13 -2
  40. package/dist/src/repo/cluster-coordinator.js.map +1 -1
  41. package/dist/src/repo/coordinator-repo.d.ts +34 -2
  42. package/dist/src/repo/coordinator-repo.d.ts.map +1 -1
  43. package/dist/src/repo/coordinator-repo.js +57 -3
  44. package/dist/src/repo/coordinator-repo.js.map +1 -1
  45. package/dist/src/storage/block-latch.d.ts +5 -4
  46. package/dist/src/storage/block-latch.d.ts.map +1 -1
  47. package/dist/src/storage/block-latch.js +5 -4
  48. package/dist/src/storage/block-latch.js.map +1 -1
  49. package/dist/src/storage/block-storage.d.ts +1 -1
  50. package/dist/src/storage/block-storage.d.ts.map +1 -1
  51. package/dist/src/storage/block-storage.js +11 -3
  52. package/dist/src/storage/block-storage.js.map +1 -1
  53. package/dist/src/storage/cached-raw-storage.d.ts +17 -1
  54. package/dist/src/storage/cached-raw-storage.d.ts.map +1 -1
  55. package/dist/src/storage/cached-raw-storage.js +8 -1
  56. package/dist/src/storage/cached-raw-storage.js.map +1 -1
  57. package/dist/src/storage/cached-store-driver.d.ts +7 -0
  58. package/dist/src/storage/cached-store-driver.d.ts.map +1 -1
  59. package/dist/src/storage/cached-store-driver.js +20 -0
  60. package/dist/src/storage/cached-store-driver.js.map +1 -1
  61. package/dist/src/storage/i-block-storage.d.ts +45 -2
  62. package/dist/src/storage/i-block-storage.d.ts.map +1 -1
  63. package/dist/src/storage/i-block-storage.js +29 -0
  64. package/dist/src/storage/i-block-storage.js.map +1 -1
  65. package/dist/src/storage/i-raw-storage.d.ts +16 -0
  66. package/dist/src/storage/i-raw-storage.d.ts.map +1 -1
  67. package/dist/src/storage/kv-raw-storage.d.ts +5 -1
  68. package/dist/src/storage/kv-raw-storage.d.ts.map +1 -1
  69. package/dist/src/storage/kv-raw-storage.js +8 -1
  70. package/dist/src/storage/kv-raw-storage.js.map +1 -1
  71. package/dist/src/storage/raw-store-driver.d.ts +15 -0
  72. package/dist/src/storage/raw-store-driver.d.ts.map +1 -1
  73. package/dist/src/storage/shared-cache-pool.d.ts +5 -0
  74. package/dist/src/storage/shared-cache-pool.d.ts.map +1 -1
  75. package/dist/src/storage/shared-cache-pool.js +6 -1
  76. package/dist/src/storage/shared-cache-pool.js.map +1 -1
  77. package/dist/src/storage/storage-repo.d.ts.map +1 -1
  78. package/dist/src/storage/storage-repo.js +141 -99
  79. package/dist/src/storage/storage-repo.js.map +1 -1
  80. package/dist/src/storage/with-read-cache.d.ts +9 -5
  81. package/dist/src/storage/with-read-cache.d.ts.map +1 -1
  82. package/dist/src/storage/with-read-cache.js +16 -6
  83. package/dist/src/storage/with-read-cache.js.map +1 -1
  84. package/package.json +2 -2
  85. package/{README.md → readme.md} +2 -2
  86. package/src/cluster/certified-claims.ts +22 -9
  87. package/src/cluster/cluster-repo.ts +199 -212
  88. package/src/cluster/commit-proof.ts +38 -2
  89. package/src/cluster/quorum-restore.ts +183 -56
  90. package/src/cluster/race-resolution.ts +158 -0
  91. package/src/cluster/reconcile-block.ts +34 -11
  92. package/src/cluster/record-operations.ts +59 -0
  93. package/src/cohort-topic/host.ts +423 -118
  94. package/src/libp2p-node-base.ts +18 -9
  95. package/src/repo/cluster-coordinator.ts +1039 -1027
  96. package/src/repo/coordinator-repo.ts +1937 -1855
  97. package/src/storage/block-latch.ts +5 -4
  98. package/src/storage/block-storage.ts +11 -3
  99. package/src/storage/cached-raw-storage.ts +21 -1
  100. package/src/storage/cached-store-driver.ts +23 -0
  101. package/src/storage/i-block-storage.ts +47 -2
  102. package/src/storage/i-raw-storage.ts +17 -0
  103. package/src/storage/kv-raw-storage.ts +8 -1
  104. package/src/storage/raw-store-driver.ts +26 -0
  105. package/src/storage/shared-cache-pool.ts +6 -1
  106. package/src/storage/storage-repo.ts +143 -103
  107. package/src/storage/with-read-cache.ts +16 -6
@@ -70,18 +70,26 @@ export function corroboratorCapacity(cohortPeerCount, repairCorroborationCluster
70
70
  *
71
71
  * **Certified claims** (`certified === true`, injected by a caller that verified the claim's
72
72
  * cohort commit proof — see `cluster/certified-claims.ts`) short-circuit the distinct-peer rule,
73
- * because the proof's signature set IS the corroboration:
73
+ * because the proof's signature set IS the corroboration — weighed by how many signers that set
74
+ * actually holds ({@link RevClaim.certifiedSignerCount}):
74
75
  *
75
76
  * - No certified claims → today's corroboration result, unchanged.
76
77
  * - A corroborated pair at a HIGHER rev than every certified claim wins — corroboration stays a
77
78
  * legitimate weaker path, so a legacy uncertified tail written after the last proven rev
78
79
  * remains readable.
79
- * - Otherwise the highest certified rev wins: this covers a certified rev beaten in raw rev only
80
- * by an *uncorroborated* claim (which failed quorum and is no evidence), and the equal-rev
81
- * tie, where the proof outweighs votes.
82
- * - Except: two distinct `actionId`s certified at that top rev is equivocation the cohort
83
- * provably signed two different actions into one revision and the whole selection declines
84
- * (`undefined`). Callers distinguish this decline from a plain no-quorum via
80
+ * - A MULTI-SIGNER certified claim (two-plus signers) at the top certified rev wins over an
81
+ * equal-rev corroborated pair and over any uncorroborated claim: the proof outweighs votes.
82
+ * Single-signer certified claims at that rev neither outrank it nor equivocate against it — a
83
+ * solo cohort's self-signed receipt is one machine's word, not the cohort's second signature.
84
+ * - A SINGLE-SIGNER-only certified rev still wins where nothing multi-peer contests it (a lone
85
+ * holder with a solo proof stays repairable), but it never beats corroboration by
86
+ * {@link CORROBORATION_FLOOR}-plus distinct peers at the SAME revision — one machine that was
87
+ * briefly alone must not outrank the cohort that stayed together. A corroborated pair that
88
+ * AGREES with the sole certified action is convergence, not a contest; the certified verdict
89
+ * is kept.
90
+ * - Two distinct `actionId`s still CONTENDING at the top certified rev is equivocation — the
91
+ * same keys provably signed two different actions into one revision — and the whole selection
92
+ * declines (`undefined`). Callers distinguish this decline from a plain no-quorum via
85
93
  * {@link certifiedEquivocation}.
86
94
  */
87
95
  export function selectQuorumRev(claims, simpleMajorityThreshold, corroboratorCapacity) {
@@ -124,15 +132,30 @@ export function selectQuorumRev(claims, simpleMajorityThreshold, corroboratorCap
124
132
  // `corroborated`), so it cannot outrank a proof.
125
133
  if (corroborated && corroborated.rev > certified.rev)
126
134
  return corroborated;
127
- // Two actions provably signed into the same top revision: decline the whole selection rather
128
- // than pick a side. Callers log via certifiedEquivocation.
129
- if (certified.byAction.size !== 1)
135
+ const contenders = certifiedContenders(certified.byAction);
136
+ // A single-signer-only certified rev loses the equal-rev tie to genuinely multi-peer
137
+ // corroboration — unless the corroborated pair AGREES with the sole certified action, which is
138
+ // convergence rather than a contest and keeps the certified verdict (and its logging/penalty
139
+ // exemptions) intact.
140
+ // NOTE: the weighing reaches EQUAL revisions only. A solo fork that committed twice while its
141
+ // cohort committed once claims the higher rev and still wins above. That is not an oversight of
142
+ // this rule: a claim carries no lineage, so "one peer ahead of two" is identical whether it
143
+ // forked or the two are lagging, and refusing it would break ordinary lagging-cohort repair.
144
+ // Pinned by 'does NOT reach a solo fork one revision AHEAD' in quorum-restore-certified.spec.ts;
145
+ // closing it needs new evidence — debt-repair-cannot-tell-a-fork-from-a-lagging-cohort.
146
+ if (!contenders.multiSigner && corroborated && corroborated.rev === certified.rev
147
+ && corroborated.supporters.length >= CORROBORATION_FLOOR
148
+ && !(certified.byAction.size === 1 && certified.byAction.has(corroborated.actionId))) {
149
+ return corroborated;
150
+ }
151
+ // Two actions provably signed into the same top revision by contending proofs: decline the
152
+ // whole selection rather than pick a side. Callers log via certifiedEquivocation.
153
+ if (contenders.entries.length !== 1)
130
154
  return undefined;
131
- const [entry] = certified.byAction;
132
- const [actionId, supporters] = entry; // size === 1 checked above
133
- return { rev: certified.rev, actionId, supporters: [...supporters], certified: true };
155
+ const [actionId, group] = contenders.entries[0];
156
+ return { rev: certified.rev, actionId, supporters: [...group.supporters], certified: true };
134
157
  }
135
- /** Certified claims at the top certified rev, keyed by actionId → distinct certified claimants. */
158
+ /** Certified claims at the top certified rev, keyed by actionId → the claimants behind each. */
136
159
  function certifiedGroups(claims) {
137
160
  let top;
138
161
  for (const c of claims) {
@@ -145,24 +168,43 @@ function certifiedGroups(claims) {
145
168
  for (const c of claims) {
146
169
  if (c.certified !== true || c.rev !== top)
147
170
  continue;
148
- let s = byAction.get(c.actionId);
149
- if (!s) {
150
- s = new Set();
151
- byAction.set(c.actionId, s);
171
+ let g = byAction.get(c.actionId);
172
+ if (!g) {
173
+ g = { supporters: new Set(), multiSigner: false };
174
+ byAction.set(c.actionId, g);
152
175
  }
153
- s.add(c.peerId);
176
+ g.supporters.add(c.peerId);
177
+ // Absent or sub-two count weighs as single-signer — see RevClaim.certifiedSignerCount.
178
+ if ((c.certifiedSignerCount ?? 1) >= 2)
179
+ g.multiSigner = true;
154
180
  }
155
181
  return { rev: top, byAction };
156
182
  }
183
+ /**
184
+ * Which certified actions at the top rev actually CONTEND for selection. A multi-signer proof is
185
+ * the cohort's own signature set; a single-signer proof is one machine's word about its own solo
186
+ * commit — so when any action is multi-signer-backed, only the multi-signer-backed ones contend
187
+ * (a solo receipt can neither outrank nor equivocate against a cohort proof), and only with no
188
+ * multi-signer action anywhere do the single-signer ones contend among themselves. One helper so
189
+ * {@link selectQuorumRev} and {@link certifiedEquivocation} cannot drift on the rule.
190
+ */
191
+ function certifiedContenders(byAction) {
192
+ const multi = [...byAction].filter(([, g]) => g.multiSigner);
193
+ return multi.length > 0
194
+ ? { entries: multi, multiSigner: true }
195
+ : { entries: [...byAction], multiSigner: false };
196
+ }
157
197
  /**
158
198
  * The conflicting certified set at the top certified rev, when there is one: two-plus distinct
159
- * `actionId`s each carrying a verified cohort commit proof for the SAME revision. It deserves a
160
- * distinct log line from a plain no-quorum the cohort (or whoever holds its keys) provably
161
- * signed both sides. Selection stays pure, so callers do the logging with what this reports.
199
+ * `actionId`s that CONTEND there ({@link certifiedContenders}) each carrying a verified cohort
200
+ * commit proof for the SAME revision, at comparable weight. It deserves a distinct log line from a
201
+ * plain no-quorum whoever holds the signing keys provably signed both sides. Selection stays
202
+ * pure, so callers do the logging with what this reports.
162
203
  *
163
- * `undefined` when no certified claim exists or the top certified rev names a single action
164
- * conflicts at LOWER certified revs are history already superseded, not equivocation worth
165
- * declining over.
204
+ * `undefined` when no certified claim exists or a single action contends at the top certified rev —
205
+ * conflicts at LOWER certified revs are history already superseded, and a single-signer proof
206
+ * disagreeing with a multi-signer one at the same rev is a solo machine's fork losing to the
207
+ * cohort, not equivocation worth declining over.
166
208
  *
167
209
  * **Ask this only on a decline.** A non-`undefined` result does NOT imply {@link selectQuorumRev}
168
210
  * returned `undefined`: a corroborated pair STRICTLY above the top certified rev still wins, and
@@ -171,9 +213,12 @@ function certifiedGroups(claims) {
171
213
  */
172
214
  export function certifiedEquivocation(claims) {
173
215
  const groups = certifiedGroups(claims);
174
- if (!groups || groups.byAction.size < 2)
216
+ if (!groups)
175
217
  return undefined;
176
- return { rev: groups.rev, actionIds: [...groups.byAction.keys()] };
218
+ const contenders = certifiedContenders(groups.byAction);
219
+ if (contenders.entries.length < 2)
220
+ return undefined;
221
+ return { rev: groups.rev, actionIds: contenders.entries.map(([actionId]) => actionId) };
177
222
  }
178
223
  /**
179
224
  * Among candidates already known to corroborate the target `(rev, actionId)`,
@@ -196,22 +241,34 @@ export function certifiedEquivocation(claims) {
196
241
  * *genuinely* that small reaches this branch.
197
242
  *
198
243
  * **Certified candidates** (`certified === true` — a caller verified a cohort commit proof whose
199
- * declared digest matches these exact bytes) short-circuit the hash quorum: exactly one distinct
200
- * certified hash → that block wins outright, however many peers served it. Two-plus distinct
201
- * certified hashes is certified content equivocation → decline (`undefined`), mirroring the
202
- * existing unique-hash-tie decline; callers name that decline via
203
- * {@link certifiedContentEquivocation}. No certified candidate the hash quorum below, unchanged.
244
+ * declared digest matches these exact bytes) are weighed by the proof's signer count, mirroring
245
+ * {@link selectQuorumRev}:
246
+ *
247
+ * - Exactly one distinct MULTI-SIGNER certified hash that block wins outright, however many
248
+ * peers served anything else: the cohort's own signatures over the digest outweigh other
249
+ * carriers' bytes. Two-plus distinct multi-signer certified hashes is certified content
250
+ * equivocation → decline (`undefined`); a single-signer certified hash alongside a
251
+ * multi-signer one neither wins nor forces that decline (a solo receipt cannot equivocate
252
+ * against the cohort's proof).
253
+ * - SINGLE-SIGNER-only certified hashes no longer short-circuit: a hash group carried by
254
+ * {@link CORROBORATION_FLOOR}-plus distinct peers that meets the ordinary quorum displaces
255
+ * them (two-plus such groups is a genuine content disagreement → decline, as ever). With no
256
+ * multi-peer group to defer to, a sole single-signer certified hash still wins on its proof —
257
+ * a lone certified holder stays repairable — and two-plus distinct ones decline.
258
+ * - No certified candidate → the hash quorum below, unchanged.
259
+ *
260
+ * Callers name the certified declines via {@link certifiedContentEquivocation}.
204
261
  */
205
262
  export function selectQuorumBlock(candidates, simpleMajorityThreshold, corroboratorCapacity) {
206
263
  if (candidates.length === 0)
207
264
  return undefined;
208
265
  const certifiedByHash = certifiedHashes(candidates);
209
- if (certifiedByHash.size === 1) {
210
- const [entry] = certifiedByHash;
211
- const [hash, block] = entry;
266
+ const multiSignerHashes = [...certifiedByHash].filter(([, v]) => v.multiSigner);
267
+ if (multiSignerHashes.length === 1) {
268
+ const [hash, { block }] = multiSignerHashes[0];
212
269
  return { block, hash };
213
270
  }
214
- if (certifiedByHash.size > 1)
271
+ if (multiSignerHashes.length > 1)
215
272
  return undefined; // certified content equivocation — decline
216
273
  // One vote per distinct peer per hash group, matching selectQuorumRev — a peer appearing twice
217
274
  // must not be able to second itself into a content quorum.
@@ -228,34 +285,74 @@ export function selectQuorumBlock(candidates, simpleMajorityThreshold, corrobora
228
285
  }
229
286
  const quorum = quorumSize(voters.size, simpleMajorityThreshold, corroboratorCapacity);
230
287
  const meeting = [...groups.entries()].filter(([, g]) => g.supporters.size >= quorum);
231
- // Exactly one hash may meet quorum; a tie is a genuine content disagreement → decline.
232
- if (meeting.length !== 1)
288
+ if (certifiedByHash.size === 0) {
289
+ // Exactly one hash may meet quorum; a tie is a genuine content disagreement → decline.
290
+ if (meeting.length !== 1)
291
+ return undefined;
292
+ const [hash, group] = meeting[0];
293
+ return { block: group.block, hash };
294
+ }
295
+ // Only single-signer certified hashes remain. Genuinely multi-peer agreement — a quorum-meeting
296
+ // group of CORROBORATION_FLOOR-plus distinct carriers — outranks a solo proof, with the usual
297
+ // uniqueness rule among such groups. (A quorum-meeting group of ONE carrier, possible only on a
298
+ // capacity-one cohort, is a bare assertion and does not displace a verified proof.)
299
+ const multiPeer = meeting.filter(([, g]) => g.supporters.size >= CORROBORATION_FLOOR);
300
+ if (multiPeer.length > 1)
233
301
  return undefined;
234
- const [hash, group] = meeting[0];
235
- return { block: group.block, hash };
302
+ if (multiPeer.length === 1) {
303
+ const [hash, group] = multiPeer[0];
304
+ return { block: group.block, hash };
305
+ }
306
+ // No multi-peer agreement to defer to: a sole single-signer certified hash wins on its proof;
307
+ // two-plus distinct ones are solo-vs-solo equivocation → decline.
308
+ if (certifiedByHash.size > 1)
309
+ return undefined;
310
+ const [entry] = certifiedByHash;
311
+ const [hash, { block }] = entry;
312
+ return { block, hash };
236
313
  }
237
- /** Distinct hashes carried by certified candidates → the first block instance serving each. */
314
+ /** Distinct hashes carried by certified candidates → the first block instance serving each, and
315
+ * whether any certified candidate at that hash carried a two-plus-signer proof. */
238
316
  function certifiedHashes(candidates) {
239
317
  const byHash = new Map();
240
318
  for (const c of candidates) {
241
- if (c.certified === true && !byHash.has(c.hash))
242
- byHash.set(c.hash, c.block);
319
+ if (c.certified !== true)
320
+ continue;
321
+ let entry = byHash.get(c.hash);
322
+ if (!entry) {
323
+ entry = { block: c.block, multiSigner: false };
324
+ byHash.set(c.hash, entry);
325
+ }
326
+ // Absent or sub-two count weighs as single-signer — see BlockHashCandidate.certifiedSignerCount.
327
+ if ((c.certifiedSignerCount ?? 1) >= 2)
328
+ entry.multiSigner = true;
243
329
  }
244
330
  return byHash;
245
331
  }
246
332
  /**
247
- * The conflicting certified hashes when there are two or more — the content-side sibling of
248
- * {@link certifiedEquivocation}, and the reason {@link selectQuorumBlock} declines outright.
249
- * Candidates reaching that selector all carry the SAME `(rev, actionId)`, so two certified hashes
250
- * mean the cohort's keys signed two different digests into one revision: a provable compromise an
251
- * operator must be able to tell apart from the routine "not enough carriers agreed" decline. Both
252
- * declines return `undefined`, so without this they log identically.
333
+ * The conflicting certified hashes when there are two or more CONTENDING — the content-side
334
+ * sibling of {@link certifiedEquivocation}, and the reason {@link selectQuorumBlock} declines
335
+ * outright. Candidates reaching that selector all carry the SAME `(rev, actionId)`, so two
336
+ * contending certified hashes mean the same keys signed two different digests into one revision: a
337
+ * provable compromise an operator must be able to tell apart from the routine "not enough carriers
338
+ * agreed" decline. Both declines return `undefined`, so without this they log identically.
339
+ * Contention mirrors the selector: multi-signer certified hashes when any exist, else the
340
+ * single-signer ones among themselves — a solo receipt disagreeing with a cohort proof is a fork
341
+ * losing, not equivocation.
253
342
  *
254
- * `undefined` when fewer than two distinct certified hashes exist — including the ordinary
343
+ * `undefined` when fewer than two certified hashes contend — including the ordinary
255
344
  * no-certified-candidate case, where a decline really is a plain content-quorum shortfall.
345
+ *
346
+ * **Ask this only on a decline**, exactly as with {@link certifiedEquivocation}: a non-`undefined`
347
+ * result does NOT imply {@link selectQuorumBlock} returned `undefined`. Two contending
348
+ * single-signer certified hashes yield to a {@link CORROBORATION_FLOOR}-plus-carrier quorum group,
349
+ * so selection can succeed while the solo-versus-solo conflict is still reported here. Call it when
350
+ * selection declined, to say WHY.
256
351
  */
257
352
  export function certifiedContentEquivocation(candidates) {
258
353
  const byHash = certifiedHashes(candidates);
259
- return byHash.size < 2 ? undefined : { hashes: [...byHash.keys()] };
354
+ const multi = [...byHash].filter(([, v]) => v.multiSigner);
355
+ const contenders = multi.length > 0 ? multi : [...byHash];
356
+ return contenders.length < 2 ? undefined : { hashes: contenders.map(([hash]) => hash) };
260
357
  }
261
358
  //# sourceMappingURL=quorum-restore.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"quorum-restore.js","sourceRoot":"","sources":["../../../src/cluster/quorum-restore.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"quorum-restore.js","sourceRoot":"","sources":["../../../src/cluster/quorum-restore.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,89 @@
1
+ /**
2
+ * The deterministic arbiter between two conflicting cluster transactions: which of two writes to a
3
+ * shared block wins. Every function here is a total function of its arguments, with no member state
4
+ * and no effect beyond a debug log — `resolveRace` runs on the vote path, where a throw would cost
5
+ * the member its vote entirely — so the ordering rule can be read and tested on its own (see
6
+ * `test/race-resolution.spec.ts`). The stateful scan that consults them (`findConflict`, which sweeps stale
7
+ * reservations and clears a losing transaction) stays on `ClusterMember` in `cluster-repo.ts`.
8
+ */
9
+ import type { ClusterRecord, RepoMessage } from "@optimystic/db-core";
10
+ /** Number of *approve* promise votes on a record — the count the commit rule uses. */
11
+ export declare function approvalCount(record: ClusterRecord): number;
12
+ /**
13
+ * Resolve a race between two conflicting transactions. Total and deterministic, so every honest
14
+ * member computes the identical winner (the Theorem 1 Case-2 premise). Order:
15
+ * 1. more *approve* promise signatures wins (progress monotonicity — see safety note below);
16
+ * 2. equal approval counts → higher aged priority wins (fairness — see {@link recordPriority});
17
+ * 3. still tied → higher message hash wins.
18
+ *
19
+ * The count is APPROVALS, not `promises` keys. `promises` is the vote map — a reject occupies a key
20
+ * there exactly as an approve does — so counting keys would treat a rejection as progress, letting a
21
+ * record that can never commit outrank (and therefore block, via the reservation scan `findConflict`
22
+ * in `cluster-repo.ts`) a fresh rival for the whole staleness window. Approvals is also the count
23
+ * the invariant below actually needs: the commit rule is `approvedPromises >= superMajority`,
24
+ * which never looks at rejections.
25
+ *
26
+ * Approval count is FIRST so this comparison never displaces a transaction that is further along.
27
+ * That restores the pre-priority safety invariant: a member commits purely on promise supermajority
28
+ * (`handleCommitNeeded` signs whenever `approvedPromises >= superMajority`; the commit path has NO
29
+ * conflict re-check), so `resolveRace` is the ONLY arbiter among concurrently-pending conflicts.
30
+ * With approvals-first, once transaction X holds a promise supermajority every conflicting rival Y has
31
+ * strictly fewer approvals — Y can only match X's count by getting the intersecting quorum member to
32
+ * approve it, but that member already holds X at supermajority and `resolveRace(X, Y)` returns
33
+ * `keep-existing` on X's higher count, so it never does. By quorum intersection any Y-supermajority
34
+ * overlaps X's in ≥1 honest member, and that member rejects Y. One winner (docs/correctness.md
35
+ * Theorem 9). Priority-first would break this: it could displace an already-quorum-reached X for a
36
+ * higher-priority Y with fewer approvals, letting BOTH commit (split brain) — the regression fixed by
37
+ * ticket occ-priority-first-breaks-promise-monotonicity.
38
+ *
39
+ * Priority is now a tie-break that runs only at EQUAL approval counts, which is exactly the
40
+ * concurrent-starvation case aging targets (two fresh rivals, 0 promises each, otherwise coin-flipping
41
+ * on the hash). Priority still breaks those ties deterministically, so aging still solves the stated
42
+ * fairness problem in its common case. It only orders two *concurrently-pending* conflicts; it does NOT
43
+ * defer a fresh pend for an absent aged transaction (that residual — sequential sub-window starvation —
44
+ * is the deferred feat-occ-priority-reservation).
45
+ *
46
+ * NOTE: residual-fairness tripwire. Under approvals-first an aged transaction can still lose to a fresh
47
+ * rival that has *legitimately* gathered even one more approval — that is not the pure-coin-flip
48
+ * starvation aging targets (equal counts, priority wins), it is the monotonicity behaviour we WANT (a
49
+ * more-progressed rival is never displaced). If deeper fairness against a genuinely-more-progressed
50
+ * rival is ever needed, it belongs to feat-occ-priority-reservation (reserve/defer at pend time), NOT
51
+ * to this race tie-break.
52
+ *
53
+ * NOTE: Byzantine self-assert is a fairness DoS, not a safety hole. A coordinator can stamp
54
+ * priority == MaxPriority on every transaction; recordPriority clamps to the cap so it cannot
55
+ * exceed it, and priority never influences validity/operationsHash/stale-read checks — and now sits
56
+ * below the approval count, so it can only break equal-count ties it might have ~50% won anyway,
57
+ * degrading to at-worst-status-quo fairness (the same graceful-degradation class as spam under
58
+ * honest-majority). Binding priority to provable age is out of scope (feat-occ-priority-reservation).
59
+ *
60
+ * NOTE: keep priority a self-contained additive message field + this one comparison key so it
61
+ * composes with — does not block — a future HLC/crdt-sync redesign of this same path
62
+ * (design-hot-log-tail-sharding-guidance).
63
+ */
64
+ export declare function resolveRace(existing: ClusterRecord, incoming: ClusterRecord): 'keep-existing' | 'accept-incoming';
65
+ /**
66
+ * Aged advisory priority carried by a record's pend operation, clamped to [0, MaxPriority].
67
+ * The multi-collection path carries it on `pend.validation.transaction.priority`; the single-collection
68
+ * (`Collection.sync`) path carries it as top-level `pend.priority`; a record with neither — a
69
+ * legacy/unversioned coordinator's transaction, or a non-pend operation — is priority 0
70
+ * (backward compatible: such transactions simply never age). Both carriers live inside the signed
71
+ * `message`, so priority is integrity-protected in transit; clamping here bounds a self-asserted
72
+ * out-of-range value to the cap.
73
+ *
74
+ * NOTE: `message` is fixed for a transaction's whole lifecycle (promises/commits accrue in the
75
+ * separate `promises`/`commits` maps, never in `message`), so a transaction keeps its rank through
76
+ * the commit phase — there is no "priority drops to 0 at commit" asymmetry. resolveRace is only
77
+ * consulted at the promise decision (`findConflict` in `cluster-repo.ts`), i.e. between two
78
+ * still-open conflicting transactions, which is exactly the concurrent-contention case priority
79
+ * is meant to order.
80
+ */
81
+ export declare function recordPriority(record: ClusterRecord): number;
82
+ /**
83
+ * Whether two messages must serialize against each other: true when they touch a common block AND
84
+ * are not the same action. The same-action escape is what lets a commit follow its own pend — both
85
+ * name every block the action writes, so a bare overlap test would have each transaction blocking
86
+ * its own next phase. Gates whether {@link resolveRace} runs at all.
87
+ */
88
+ export declare function operationsConflict(ops1: RepoMessage['operations'], ops2: RepoMessage['operations']): boolean;
89
+ //# sourceMappingURL=race-resolution.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"race-resolution.d.ts","sourceRoot":"","sources":["../../../src/cluster/race-resolution.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AACH,OAAO,KAAK,EAAE,aAAa,EAAE,WAAW,EAAE,MAAM,qBAAqB,CAAC;AAUtE,sFAAsF;AACtF,wBAAgB,aAAa,CAAC,MAAM,EAAE,aAAa,GAAG,MAAM,CAE3D;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAmDG;AACH,wBAAgB,WAAW,CAAC,QAAQ,EAAE,aAAa,EAAE,QAAQ,EAAE,aAAa,GAAG,eAAe,GAAG,iBAAiB,CAqBjH;AAED;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,cAAc,CAAC,MAAM,EAAE,aAAa,GAAG,MAAM,CAW5D;AAED;;;;;GAKG;AACH,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,WAAW,CAAC,YAAY,CAAC,EAAE,IAAI,EAAE,WAAW,CAAC,YAAY,CAAC,GAAG,OAAO,CAwB5G"}
@@ -0,0 +1,140 @@
1
+ import { clampPriority } from "@optimystic/db-core";
2
+ import { getActionId, getAffectedBlockIds } from "./record-operations.js";
3
+ import { createLogger } from "../logger.js";
4
+ // Same sub-namespace `cluster-repo.ts` logs under: these functions moved out of `ClusterMember`, and
5
+ // their emitted tags (and the `debug` namespace they land in) must stay byte-identical, because
6
+ // several specs capture by namespace and tag substring.
7
+ const log = createLogger('cluster-member');
8
+ /** Number of *approve* promise votes on a record — the count the commit rule uses. */
9
+ export function approvalCount(record) {
10
+ return Object.values(record.promises).filter(s => s.type === 'approve').length;
11
+ }
12
+ /**
13
+ * Resolve a race between two conflicting transactions. Total and deterministic, so every honest
14
+ * member computes the identical winner (the Theorem 1 Case-2 premise). Order:
15
+ * 1. more *approve* promise signatures wins (progress monotonicity — see safety note below);
16
+ * 2. equal approval counts → higher aged priority wins (fairness — see {@link recordPriority});
17
+ * 3. still tied → higher message hash wins.
18
+ *
19
+ * The count is APPROVALS, not `promises` keys. `promises` is the vote map — a reject occupies a key
20
+ * there exactly as an approve does — so counting keys would treat a rejection as progress, letting a
21
+ * record that can never commit outrank (and therefore block, via the reservation scan `findConflict`
22
+ * in `cluster-repo.ts`) a fresh rival for the whole staleness window. Approvals is also the count
23
+ * the invariant below actually needs: the commit rule is `approvedPromises >= superMajority`,
24
+ * which never looks at rejections.
25
+ *
26
+ * Approval count is FIRST so this comparison never displaces a transaction that is further along.
27
+ * That restores the pre-priority safety invariant: a member commits purely on promise supermajority
28
+ * (`handleCommitNeeded` signs whenever `approvedPromises >= superMajority`; the commit path has NO
29
+ * conflict re-check), so `resolveRace` is the ONLY arbiter among concurrently-pending conflicts.
30
+ * With approvals-first, once transaction X holds a promise supermajority every conflicting rival Y has
31
+ * strictly fewer approvals — Y can only match X's count by getting the intersecting quorum member to
32
+ * approve it, but that member already holds X at supermajority and `resolveRace(X, Y)` returns
33
+ * `keep-existing` on X's higher count, so it never does. By quorum intersection any Y-supermajority
34
+ * overlaps X's in ≥1 honest member, and that member rejects Y. One winner (docs/correctness.md
35
+ * Theorem 9). Priority-first would break this: it could displace an already-quorum-reached X for a
36
+ * higher-priority Y with fewer approvals, letting BOTH commit (split brain) — the regression fixed by
37
+ * ticket occ-priority-first-breaks-promise-monotonicity.
38
+ *
39
+ * Priority is now a tie-break that runs only at EQUAL approval counts, which is exactly the
40
+ * concurrent-starvation case aging targets (two fresh rivals, 0 promises each, otherwise coin-flipping
41
+ * on the hash). Priority still breaks those ties deterministically, so aging still solves the stated
42
+ * fairness problem in its common case. It only orders two *concurrently-pending* conflicts; it does NOT
43
+ * defer a fresh pend for an absent aged transaction (that residual — sequential sub-window starvation —
44
+ * is the deferred feat-occ-priority-reservation).
45
+ *
46
+ * NOTE: residual-fairness tripwire. Under approvals-first an aged transaction can still lose to a fresh
47
+ * rival that has *legitimately* gathered even one more approval — that is not the pure-coin-flip
48
+ * starvation aging targets (equal counts, priority wins), it is the monotonicity behaviour we WANT (a
49
+ * more-progressed rival is never displaced). If deeper fairness against a genuinely-more-progressed
50
+ * rival is ever needed, it belongs to feat-occ-priority-reservation (reserve/defer at pend time), NOT
51
+ * to this race tie-break.
52
+ *
53
+ * NOTE: Byzantine self-assert is a fairness DoS, not a safety hole. A coordinator can stamp
54
+ * priority == MaxPriority on every transaction; recordPriority clamps to the cap so it cannot
55
+ * exceed it, and priority never influences validity/operationsHash/stale-read checks — and now sits
56
+ * below the approval count, so it can only break equal-count ties it might have ~50% won anyway,
57
+ * degrading to at-worst-status-quo fairness (the same graceful-degradation class as spam under
58
+ * honest-majority). Binding priority to provable age is out of scope (feat-occ-priority-reservation).
59
+ *
60
+ * NOTE: keep priority a self-contained additive message field + this one comparison key so it
61
+ * composes with — does not block — a future HLC/crdt-sync redesign of this same path
62
+ * (design-hot-log-tail-sharding-guidance).
63
+ */
64
+ export function resolveRace(existing, incoming) {
65
+ // 1. Transaction with more APPROVALS wins — never displace a more-progressed rival (safety, see
66
+ // above). Counting `promises` keys instead would count reject votes as progress: a record holding
67
+ // one rejection would outrank an untouched rival and reserve its blocks for the whole staleness
68
+ // window, and the commit rule this ordering protects (`approvedPromises >= superMajority`) never
69
+ // looks at rejections anyway.
70
+ const existingCount = approvalCount(existing);
71
+ const incomingCount = approvalCount(incoming);
72
+ if (existingCount !== incomingCount) {
73
+ return existingCount > incomingCount ? 'keep-existing' : 'accept-incoming';
74
+ }
75
+ // 2. Equal approval counts → higher aged priority wins (fairness tie-break).
76
+ const existingPriority = recordPriority(existing);
77
+ const incomingPriority = recordPriority(incoming);
78
+ if (existingPriority !== incomingPriority) {
79
+ return existingPriority > incomingPriority ? 'keep-existing' : 'accept-incoming';
80
+ }
81
+ // 3. Tie-breaker: higher message hash wins (deterministic).
82
+ return existing.messageHash > incoming.messageHash ? 'keep-existing' : 'accept-incoming';
83
+ }
84
+ /**
85
+ * Aged advisory priority carried by a record's pend operation, clamped to [0, MaxPriority].
86
+ * The multi-collection path carries it on `pend.validation.transaction.priority`; the single-collection
87
+ * (`Collection.sync`) path carries it as top-level `pend.priority`; a record with neither — a
88
+ * legacy/unversioned coordinator's transaction, or a non-pend operation — is priority 0
89
+ * (backward compatible: such transactions simply never age). Both carriers live inside the signed
90
+ * `message`, so priority is integrity-protected in transit; clamping here bounds a self-asserted
91
+ * out-of-range value to the cap.
92
+ *
93
+ * NOTE: `message` is fixed for a transaction's whole lifecycle (promises/commits accrue in the
94
+ * separate `promises`/`commits` maps, never in `message`), so a transaction keeps its rank through
95
+ * the commit phase — there is no "priority drops to 0 at commit" asymmetry. resolveRace is only
96
+ * consulted at the promise decision (`findConflict` in `cluster-repo.ts`), i.e. between two
97
+ * still-open conflicting transactions, which is exactly the concurrent-contention case priority
98
+ * is meant to order.
99
+ */
100
+ export function recordPriority(record) {
101
+ for (const op of record.message.operations) {
102
+ if ('pend' in op) {
103
+ // Every hop optional: `validation` arrives off the wire inside a signed message whose
104
+ // hash binds its bytes, not its shape, so a malformed pair must yield priority 0 (what
105
+ // clampPriority already does for a missing or Byzantine number) rather than throw out
106
+ // of the vote path — the lost vote this fail-closed pass exists to prevent.
107
+ return clampPriority(op.pend.validation?.transaction?.priority ?? op.pend.priority);
108
+ }
109
+ }
110
+ return 0;
111
+ }
112
+ /**
113
+ * Whether two messages must serialize against each other: true when they touch a common block AND
114
+ * are not the same action. The same-action escape is what lets a commit follow its own pend — both
115
+ * name every block the action writes, so a bare overlap test would have each transaction blocking
116
+ * its own next phase. Gates whether {@link resolveRace} runs at all.
117
+ */
118
+ export function operationsConflict(ops1, ops2) {
119
+ // Check if one is a commit for the same action as a pend - these don't conflict
120
+ const actionId1 = getActionId(ops1);
121
+ const actionId2 = getActionId(ops2);
122
+ if (actionId1 && actionId2 && actionId1 === actionId2) {
123
+ // Same action - commit is resolving the pend, not conflicting
124
+ return false;
125
+ }
126
+ const blocks1 = new Set(getAffectedBlockIds(ops1));
127
+ const blocks2 = new Set(getAffectedBlockIds(ops2));
128
+ for (const block of Array.from(blocks1)) {
129
+ if (blocks2.has(block)) {
130
+ log('cluster-member:conflict-detected', {
131
+ blocks1: Array.from(blocks1),
132
+ blocks2: Array.from(blocks2),
133
+ conflictingBlock: block
134
+ });
135
+ return true;
136
+ }
137
+ }
138
+ return false;
139
+ }
140
+ //# sourceMappingURL=race-resolution.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"race-resolution.js","sourceRoot":"","sources":["../../../src/cluster/race-resolution.ts"],"names":[],"mappings":"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"}
@@ -52,8 +52,11 @@ export interface ReconcileBlockDeps {
52
52
  *
53
53
  * Peer-attached cohort commit proofs are verified first ({@link certifyCandidates}) and both gates
54
54
  * weigh the verdicts: a claim — and, separately, the bytes — that a verified proof certifies is
55
- * accepted with no second peer at any cohort size, and the proof that certified the bytes is
56
- * persisted alongside them so the repaired replica serves it onward.
55
+ * accepted with no second peer where nothing multi-peer contests it. The proof's signer count is
56
+ * weighed too: a SINGLE-SIGNER proof (a solo cohort's self-signed receipt) never outranks several
57
+ * distinct peers agreeing at the same revision or on the same bytes — see `quorum-restore.ts`. The
58
+ * proof that certified the agreed bytes is persisted alongside them so the repaired replica serves
59
+ * it onward.
57
60
  *
58
61
  * Both quorums are capped by {@link corroboratorCapacity} for the claims that still need
59
62
  * corroboration: demanding two corroborators from a cohort that contains exactly one other peer is
@@ -69,8 +72,10 @@ export interface ReconcileBlockDeps {
69
72
  * no honest majority to appeal to in the first place. It is closed for a candidate that carries a
70
73
  * verified cohort commit proof — {@link certifyCandidates} binds the proof's declared digest to the
71
74
  * served bytes, which is a check against the *cohort's own signatures* rather than against other
72
- * peers, so a certified carrier wins the content gate outright and this exposure never applies to
73
- * it. It remains open for a proof-less candidate, and for the residual that layer 1 proves only
75
+ * peers, so a certified carrier wins the content gate with no second carrier and this exposure
76
+ * never applies to it. (Weighing still applies at larger widths: a SINGLE-SIGNER certified carrier
77
+ * does not displace a multi-peer hash quorum — see `selectQuorumBlock` — but at the capacity-one
78
+ * width this paragraph describes there is no multi-peer group to lose to.) It remains open for a proof-less candidate, and for the residual that layer 1 proves only
74
79
  * that the listed signers signed (`feat-cluster-membership-threshold-cert-anchoring`).
75
80
  *
76
81
  * Declines are cheap and retryable: nothing is persisted, nothing is marked, and the next commit
@@ -1 +1 @@
1
- {"version":3,"file":"reconcile-block.d.ts","sourceRoot":"","sources":["../../../src/cluster/reconcile-block.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAY,SAAS,EAAE,OAAO,EAAE,MAAM,EAAE,MAAM,qBAAqB,CAAC;AAEhF,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,sBAAsB,CAAC;AAEzD,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,mBAAmB,CAAC;AAChE,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,wBAAwB,CAAC;AAO9D,OAAO,EACqE,KAAK,cAAc,EAC9F,MAAM,uBAAuB,CAAC;AAC/B,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,mBAAmB,CAAC;AAK1D;;;;;GAKG;AACH,eAAO,MAAM,oBAAoB,OAAO,CAAC;AA6BzC,wGAAwG;AACxG,MAAM,WAAW,kBAAkB;IAClC,iEAAiE;IACjE,UAAU,EAAE,MAAM,CAAC;IACnB,2GAA2G;IAC3G,YAAY,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,OAAO,KAAK,OAAO,CAAC,YAAY,GAAG,SAAS,CAAC,CAAC;IACtF;;;;OAIG;IACH,mBAAmB,EAAE,CAAC,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,SAAS,EAAE,aAAa,CAAC,EAAE,gBAAgB,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IAC7H,oFAAoF;IACpF,uBAAuB,EAAE,MAAM,CAAC;IAChC,wFAAwF;IACxF,sBAAsB,EAAE,MAAM,CAAC;IAC/B,8GAA8G;IAC9G,SAAS,CAAC,EAAE,cAAc,CAAC;IAC3B;;;;;;;;OAQG;IACH,8BAA8B,EAAE,MAAM,CAAC;IACvC,iFAAiF;IACjF,UAAU,CAAC,EAAE,IAAI,CAAC,eAAe,EAAE,YAAY,CAAC,CAAC;CACjD;AA4LD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAiCG;AACH,wBAAgB,oBAAoB,CAAC,IAAI,EAAE,kBAAkB,GAAG,sBAAsB,CAgGrF"}
1
+ {"version":3,"file":"reconcile-block.d.ts","sourceRoot":"","sources":["../../../src/cluster/reconcile-block.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAY,SAAS,EAAE,OAAO,EAAE,MAAM,EAAE,MAAM,qBAAqB,CAAC;AAEhF,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,sBAAsB,CAAC;AAEzD,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,mBAAmB,CAAC;AAChE,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,wBAAwB,CAAC;AAO9D,OAAO,EACqE,KAAK,cAAc,EAC9F,MAAM,uBAAuB,CAAC;AAC/B,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,mBAAmB,CAAC;AAK1D;;;;;GAKG;AACH,eAAO,MAAM,oBAAoB,OAAO,CAAC;AAmCzC,wGAAwG;AACxG,MAAM,WAAW,kBAAkB;IAClC,iEAAiE;IACjE,UAAU,EAAE,MAAM,CAAC;IACnB,2GAA2G;IAC3G,YAAY,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,OAAO,KAAK,OAAO,CAAC,YAAY,GAAG,SAAS,CAAC,CAAC;IACtF;;;;OAIG;IACH,mBAAmB,EAAE,CAAC,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,SAAS,EAAE,aAAa,CAAC,EAAE,gBAAgB,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IAC7H,oFAAoF;IACpF,uBAAuB,EAAE,MAAM,CAAC;IAChC,wFAAwF;IACxF,sBAAsB,EAAE,MAAM,CAAC;IAC/B,8GAA8G;IAC9G,SAAS,CAAC,EAAE,cAAc,CAAC;IAC3B;;;;;;;;OAQG;IACH,8BAA8B,EAAE,MAAM,CAAC;IACvC,iFAAiF;IACjF,UAAU,CAAC,EAAE,IAAI,CAAC,eAAe,EAAE,YAAY,CAAC,CAAC;CACjD;AAqMD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAsCG;AACH,wBAAgB,oBAAoB,CAAC,IAAI,EAAE,kBAAkB,GAAG,sBAAsB,CAmGrF"}