@xyo-network/chain-sdk 5.3.0 → 5.3.2

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 (82) hide show
  1. package/dist/browser/modules/analyze/ChainHeadSelector.d.ts +47 -8
  2. package/dist/browser/modules/analyze/ChainHeadSelector.d.ts.map +1 -1
  3. package/dist/browser/modules/analyze/blockTime.d.ts +92 -0
  4. package/dist/browser/modules/analyze/blockTime.d.ts.map +1 -0
  5. package/dist/browser/modules/analyze/index.d.ts +1 -0
  6. package/dist/browser/modules/analyze/index.d.ts.map +1 -1
  7. package/dist/browser/modules/modules/diviners/index.d.ts +0 -1
  8. package/dist/browser/modules/modules/diviners/index.d.ts.map +1 -1
  9. package/dist/browser/modules/protocol/block/buildBlock.d.ts.map +1 -1
  10. package/dist/browser/modules/protocol/block/createProducerChainStakeIntentTransaction.d.ts +15 -2
  11. package/dist/browser/modules/protocol/block/createProducerChainStakeIntentTransaction.d.ts.map +1 -1
  12. package/dist/browser/modules/services/implementation/processPendingBlocks.d.ts +10 -3
  13. package/dist/browser/modules/services/implementation/processPendingBlocks.d.ts.map +1 -1
  14. package/dist/browser/modules/services/simple/block/runner/SimpleBlockRunner.d.ts +22 -1
  15. package/dist/browser/modules/services/simple/block/runner/SimpleBlockRunner.d.ts.map +1 -1
  16. package/dist/browser/modules/validation/block/validators/index.d.ts +0 -1
  17. package/dist/browser/modules/validation/block/validators/index.d.ts.map +1 -1
  18. package/dist/browser/modules/validation/elevatedPayload/payloads/validateClaimInBlock.d.ts.map +1 -1
  19. package/dist/browser/modules/wrappers/block/HydratedBlock.d.ts.map +1 -1
  20. package/dist/neutral/analyze.mjs +166 -10
  21. package/dist/neutral/analyze.mjs.map +3 -3
  22. package/dist/neutral/modules/analyze/ChainHeadSelector.d.ts +47 -8
  23. package/dist/neutral/modules/analyze/ChainHeadSelector.d.ts.map +1 -1
  24. package/dist/neutral/modules/analyze/blockTime.d.ts +92 -0
  25. package/dist/neutral/modules/analyze/blockTime.d.ts.map +1 -0
  26. package/dist/neutral/modules/analyze/index.d.ts +1 -0
  27. package/dist/neutral/modules/analyze/index.d.ts.map +1 -1
  28. package/dist/neutral/modules/modules/diviners/index.d.ts +0 -1
  29. package/dist/neutral/modules/modules/diviners/index.d.ts.map +1 -1
  30. package/dist/neutral/modules/protocol/block/buildBlock.d.ts.map +1 -1
  31. package/dist/neutral/modules/protocol/block/createProducerChainStakeIntentTransaction.d.ts +15 -2
  32. package/dist/neutral/modules/protocol/block/createProducerChainStakeIntentTransaction.d.ts.map +1 -1
  33. package/dist/neutral/modules/services/implementation/processPendingBlocks.d.ts +10 -3
  34. package/dist/neutral/modules/services/implementation/processPendingBlocks.d.ts.map +1 -1
  35. package/dist/neutral/modules/services/simple/block/runner/SimpleBlockRunner.d.ts +22 -1
  36. package/dist/neutral/modules/services/simple/block/runner/SimpleBlockRunner.d.ts.map +1 -1
  37. package/dist/neutral/modules/validation/block/validators/index.d.ts +0 -1
  38. package/dist/neutral/modules/validation/block/validators/index.d.ts.map +1 -1
  39. package/dist/neutral/modules/validation/elevatedPayload/payloads/validateClaimInBlock.d.ts.map +1 -1
  40. package/dist/neutral/modules/wrappers/block/HydratedBlock.d.ts.map +1 -1
  41. package/dist/neutral/modules.mjs +25 -100
  42. package/dist/neutral/modules.mjs.map +4 -4
  43. package/dist/neutral/protocol.mjs +1 -1
  44. package/dist/neutral/protocol.mjs.map +2 -2
  45. package/dist/neutral/services.mjs +40 -9
  46. package/dist/neutral/services.mjs.map +2 -2
  47. package/dist/neutral/validation.mjs +84 -106
  48. package/dist/neutral/validation.mjs.map +4 -4
  49. package/dist/neutral/wrappers.mjs +2 -1
  50. package/dist/neutral/wrappers.mjs.map +2 -2
  51. package/dist/node/modules/analyze/ChainHeadSelector.d.ts +47 -8
  52. package/dist/node/modules/analyze/ChainHeadSelector.d.ts.map +1 -1
  53. package/dist/node/modules/analyze/blockTime.d.ts +92 -0
  54. package/dist/node/modules/analyze/blockTime.d.ts.map +1 -0
  55. package/dist/node/modules/analyze/index.d.ts +1 -0
  56. package/dist/node/modules/analyze/index.d.ts.map +1 -1
  57. package/dist/node/modules/modules/diviners/index.d.ts +0 -1
  58. package/dist/node/modules/modules/diviners/index.d.ts.map +1 -1
  59. package/dist/node/modules/protocol/block/buildBlock.d.ts.map +1 -1
  60. package/dist/node/modules/protocol/block/createProducerChainStakeIntentTransaction.d.ts +15 -2
  61. package/dist/node/modules/protocol/block/createProducerChainStakeIntentTransaction.d.ts.map +1 -1
  62. package/dist/node/modules/services/implementation/processPendingBlocks.d.ts +10 -3
  63. package/dist/node/modules/services/implementation/processPendingBlocks.d.ts.map +1 -1
  64. package/dist/node/modules/services/simple/block/runner/SimpleBlockRunner.d.ts +22 -1
  65. package/dist/node/modules/services/simple/block/runner/SimpleBlockRunner.d.ts.map +1 -1
  66. package/dist/node/modules/validation/block/validators/index.d.ts +0 -1
  67. package/dist/node/modules/validation/block/validators/index.d.ts.map +1 -1
  68. package/dist/node/modules/validation/elevatedPayload/payloads/validateClaimInBlock.d.ts.map +1 -1
  69. package/dist/node/modules/wrappers/block/HydratedBlock.d.ts.map +1 -1
  70. package/package.json +24 -25
  71. package/dist/browser/modules/modules/diviners/BlockValidationDiviner.d.ts +0 -38
  72. package/dist/browser/modules/modules/diviners/BlockValidationDiviner.d.ts.map +0 -1
  73. package/dist/browser/modules/validation/block/validators/JsonSchema.d.ts +0 -4
  74. package/dist/browser/modules/validation/block/validators/JsonSchema.d.ts.map +0 -1
  75. package/dist/neutral/modules/modules/diviners/BlockValidationDiviner.d.ts +0 -38
  76. package/dist/neutral/modules/modules/diviners/BlockValidationDiviner.d.ts.map +0 -1
  77. package/dist/neutral/modules/validation/block/validators/JsonSchema.d.ts +0 -4
  78. package/dist/neutral/modules/validation/block/validators/JsonSchema.d.ts.map +0 -1
  79. package/dist/node/modules/modules/diviners/BlockValidationDiviner.d.ts +0 -38
  80. package/dist/node/modules/modules/diviners/BlockValidationDiviner.d.ts.map +0 -1
  81. package/dist/node/modules/validation/block/validators/JsonSchema.d.ts +0 -4
  82. package/dist/node/modules/validation/block/validators/JsonSchema.d.ts.map +0 -1
@@ -135,6 +135,80 @@ var BalanceAnalyzer = class {
135
135
  }
136
136
  };
137
137
 
138
+ // src/modules/analyze/blockTime.ts
139
+ import { isTimePayload } from "@xyo-network/xl1-sdk";
140
+ var DEFAULT_MAX_CLOCK_SKEW_MS = 3e4;
141
+ var DEFAULT_MAX_EVM_LEAD_BLOCKS = 2;
142
+ var BlockTimeRuleIds = [
143
+ "time-present",
144
+ "epoch-after-parent",
145
+ "epoch-not-future",
146
+ "anchor-monotonic",
147
+ "anchor-not-ahead"
148
+ ];
149
+ function timePayloadOfBlock(block) {
150
+ return block[1].find((payload) => isTimePayload(payload));
151
+ }
152
+ function signedEpochOfBlock(block) {
153
+ return timePayloadOfBlock(block)?.epoch;
154
+ }
155
+ function evmAnchorOfBlock(block) {
156
+ return timePayloadOfBlock(block)?.ethereum;
157
+ }
158
+ function checkEpoch(time, parentTime, bounds, options) {
159
+ if (parentTime !== void 0 && time.epoch <= parentTime.epoch) {
160
+ return ["epoch-after-parent", `epoch ${time.epoch} is not after the parent's ${parentTime.epoch}`];
161
+ }
162
+ const maxClockSkewMs = options.maxClockSkewMs ?? DEFAULT_MAX_CLOCK_SKEW_MS;
163
+ if (time.epoch > bounds.now + maxClockSkewMs) {
164
+ return [
165
+ "epoch-not-future",
166
+ `epoch ${time.epoch} is ${time.epoch - bounds.now}ms ahead of this observer's clock (${maxClockSkewMs}ms allowed)`
167
+ ];
168
+ }
169
+ return void 0;
170
+ }
171
+ function checkAnchor(time, parentTime, bounds, options) {
172
+ const anchor = time.ethereum;
173
+ if (anchor === void 0) return void 0;
174
+ const parentAnchor = parentTime?.ethereum;
175
+ if (parentAnchor !== void 0 && anchor < parentAnchor) {
176
+ return ["anchor-monotonic", `anchor went backwards, from ${parentAnchor} to ${anchor}`];
177
+ }
178
+ const maxEvmLeadBlocks = options.maxEvmLeadBlocks ?? DEFAULT_MAX_EVM_LEAD_BLOCKS;
179
+ if (bounds.evmHead !== void 0 && anchor > bounds.evmHead + maxEvmLeadBlocks) {
180
+ return [
181
+ "anchor-not-ahead",
182
+ `anchor ${anchor} is ahead of this observer's EVM head ${bounds.evmHead} (${maxEvmLeadBlocks} blocks allowed)`
183
+ ];
184
+ }
185
+ return void 0;
186
+ }
187
+ function evaluateBlockTime(candidate, parent, bounds, options = {}) {
188
+ const at = {
189
+ block: candidate[0].block,
190
+ hash: candidate[0]._hash,
191
+ producer: candidate[0].addresses[0]
192
+ };
193
+ const time = timePayloadOfBlock(candidate);
194
+ if (time === void 0) {
195
+ return {
196
+ ...at,
197
+ detail: "carries no signed time payload",
198
+ rule: "time-present"
199
+ };
200
+ }
201
+ const parentTime = parent === void 0 ? void 0 : timePayloadOfBlock(parent);
202
+ const failure = checkEpoch(time, parentTime, bounds, options) ?? checkAnchor(time, parentTime, bounds, options);
203
+ if (failure === void 0) return void 0;
204
+ const [rule, detail] = failure;
205
+ return {
206
+ ...at,
207
+ detail,
208
+ rule
209
+ };
210
+ }
211
+
138
212
  // src/modules/analyze/ChainFinalizer.ts
139
213
  import { Base } from "@ariestools/sdk";
140
214
  import { asXL1BlockRange } from "@xyo-network/xl1-sdk";
@@ -209,7 +283,7 @@ var ChainHeadSelector = class _ChainHeadSelector extends ChainFinalizer {
209
283
  const proposedBlocks = await this.admit(allProposedBlocks, allowed);
210
284
  this.windowedFinalizedChain = await getWindowedChain(this.context, this.blockViewer, this.maxWindowSize, this.windowedFinalizedChain);
211
285
  const pacedBlocks = this.filterPacedHeartbeatCandidates(proposedBlocks);
212
- const uncles = findUncles(this.context, this.windowedFinalizedChain, pacedBlocks);
286
+ const uncles = await this.filterTimeValidUncles(findUncles(this.context, this.windowedFinalizedChain, pacedBlocks));
213
287
  this.reportSilentUncleRejections(pacedBlocks, uncles.length);
214
288
  return findBestUncle(this.windowedFinalizedChain, uncles, { backoffMs: this.backoffMs, minCandidates: this.minCandidates }) ?? this.windowedFinalizedChain;
215
289
  }, this.context);
@@ -263,30 +337,50 @@ var ChainHeadSelector = class _ChainHeadSelector extends ChainFinalizer {
263
337
  }
264
338
  /**
265
339
  * Defense against unwanted heartbeat rates: a transaction-less (heartbeat) candidate qualifies
266
- * only when its signing epoch is at least heartbeatInterval after the current head's. Measured
267
- * on chain data, so every finalizer applies the same pace deterministically regardless of wall
268
- * clock. A producer heartbeating faster than the network's pace — misconfigured or hostile —
269
- * cannot drive the chain's block rate: its candidates never qualify and age out of the pool,
270
- * while transaction-carrying candidates are exempt and finalize immediately.
340
+ * only when its signed epoch is at least heartbeatInterval after the current head's. Both
341
+ * epochs come from the blocks' signed time payloads — never from client meta a producer or
342
+ * relay could rewrite after signing — so every finalizer applies the same pace deterministically
343
+ * regardless of wall clock. A producer heartbeating faster than the network's pace —
344
+ * misconfigured or hostile — cannot drive the chain's block rate: its candidates never qualify
345
+ * and age out of the pool, while transaction-carrying candidates are exempt and finalize
346
+ * immediately.
271
347
  */
272
348
  filterPacedHeartbeatCandidates(proposedBlocks) {
273
349
  const interval = this.heartbeatInterval;
274
350
  if (interval === void 0 || proposedBlocks.length === 0) return proposedBlocks;
275
- const headEpoch = this.windowedFinalizedChain.at(-1)?.[0].$epoch;
351
+ const head = this.windowedFinalizedChain.at(-1);
352
+ const headEpoch = head === void 0 ? void 0 : signedEpochOfBlock(head);
276
353
  if (!isDefined2(headEpoch)) return proposedBlocks;
277
354
  const paced = proposedBlocks.filter((candidate) => {
355
+ if (candidate[0].block <= (head?.[0].block ?? -1)) return true;
278
356
  if (candidate[1].some((payload) => isTransactionBoundWitness(payload))) return true;
279
- const candidateEpoch = candidate[0].$epoch;
357
+ const candidateEpoch = signedEpochOfBlock(candidate);
280
358
  if (!isDefined2(candidateEpoch)) return true;
281
359
  return candidateEpoch - headEpoch >= interval;
282
360
  });
283
361
  if (paced.length < proposedBlocks.length) {
362
+ const pacedOut = proposedBlocks.filter((candidate) => !paced.includes(candidate));
284
363
  this.context.logger?.debug(
285
- `findBestHead: paced out ${proposedBlocks.length - paced.length} heartbeat candidate(s) younger than ${interval}ms since the head`
364
+ `findBestHead: paced out ${pacedOut.length} heartbeat candidate(s) younger than ${interval}ms since head ${head?.[0].block} (signed epoch ${headEpoch}): ` + pacedOut.map((candidate) => `${candidate[0].block} [${candidate[0]._hash}] signed epoch ${signedEpochOfBlock(candidate)}`).join(", ")
286
365
  );
287
366
  }
288
367
  return paced;
289
368
  }
369
+ /**
370
+ * Refuses candidates whose signed time claims cannot be true.
371
+ *
372
+ * Every rule reads the candidate's own signed time payload — the one statement about a block's
373
+ * timing its producer cannot alter after signing — so a producer with a bad clock or a bad EVM
374
+ * view cannot move the chain's clock, and neither can anything between it and this finalizer.
375
+ * A chain is kept up to its first failing block: what stands before the failure still builds on
376
+ * the head and is still worth finalizing.
377
+ */
378
+ async filterTimeValidUncles(uncles) {
379
+ const rules = this.params.timeRules;
380
+ if (rules === void 0 || uncles.length === 0) return uncles;
381
+ const bounds = await this.resolveTimeBounds(rules, uncles);
382
+ return uncles.map((uncle) => this.timeValidPrefix(uncle, bounds, rules)).filter((uncle) => uncle.length > 0);
383
+ }
290
384
  /** Whether the configured allow-list admits a candidate. An empty list admits everyone. */
291
385
  isAllowed(candidate) {
292
386
  const allowedProducers = this.allowedProducers;
@@ -333,6 +427,61 @@ var ChainHeadSelector = class _ChainHeadSelector extends ChainFinalizer {
333
427
  `findBestHead: candidates have failed uncle qualification continuously for ${now - state.unqualifiedSince}ms \u2014 ${proposedBlocks.length} pending candidate(s): ` + proposedBlocks.map((b) => `${b[0].block} [${b[0]._hash}]`).join(", ")
334
428
  );
335
429
  }
430
+ /**
431
+ * Says, once per candidate, which rule refused it.
432
+ *
433
+ * A refused candidate lingers in the pool until it ages out and a fresh selector runs every
434
+ * tick, so the memory of what was already reported lives in context.singletons alongside the
435
+ * other per-pool state; repeating the warning every half second would bury the one line that
436
+ * explains a stalled producer.
437
+ */
438
+ reportTimeRejection(rejection, rules) {
439
+ const singletons = this.context.singletons;
440
+ const stateKey = "ChainHeadSelector:timeRejectionsReported";
441
+ const reported = singletons[stateKey] ?? /* @__PURE__ */ new Set();
442
+ singletons[stateKey] = reported;
443
+ if (reported.has(rejection.hash)) return;
444
+ if (reported.size >= 1e4) reported.clear();
445
+ reported.add(rejection.hash);
446
+ this.context.logger?.warn(
447
+ `findBestHead: candidate ${rejection.block} [${rejection.hash}] refused by '${rejection.rule}': ${rejection.detail}`
448
+ );
449
+ rules.onRejection?.(rejection);
450
+ }
451
+ /**
452
+ * The present, as this selector sees it, resolved once per pass.
453
+ *
454
+ * The EVM head is read only when some candidate actually claims an anchor: it is the one rule
455
+ * input that costs a network call, and a chain running without anchors should not pay for it.
456
+ * An unreadable EVM yields no bound rather than a failure — that is a fact about this observer,
457
+ * and it must never stop the chain from finalizing.
458
+ */
459
+ async resolveTimeBounds(rules, uncles) {
460
+ const now = rules.now?.() ?? Date.now();
461
+ const anchored = uncles.some((uncle) => uncle.some((candidate) => evmAnchorOfBlock(candidate) !== void 0));
462
+ if (!anchored || rules.evmHead === void 0) return { now };
463
+ try {
464
+ return { evmHead: await rules.evmHead(), now };
465
+ } catch (error) {
466
+ this.context.logger?.warn(`findBestHead: could not read the EVM head; candidate anchors are unbounded this pass: ${String(error)}`);
467
+ return { now };
468
+ }
469
+ }
470
+ /** The longest prefix of an uncle chain in which every block's time claims hold against its parent. */
471
+ timeValidPrefix(uncle, bounds, rules) {
472
+ const prefix = [];
473
+ let parent = this.windowedFinalizedChain.at(-1);
474
+ for (const candidate of uncle) {
475
+ const rejection = evaluateBlockTime(candidate, parent, bounds, rules);
476
+ if (rejection !== void 0) {
477
+ this.reportTimeRejection(rejection, rules);
478
+ break;
479
+ }
480
+ prefix.push(candidate);
481
+ parent = candidate;
482
+ }
483
+ return prefix;
484
+ }
336
485
  };
337
486
 
338
487
  // src/modules/analyze/producer/ChainProducersAnalyzer.ts
@@ -509,6 +658,7 @@ export {
509
658
  AdmissionModes,
510
659
  AdmissionShadowPublishRunnerMoniker,
511
660
  BalanceAnalyzer,
661
+ BlockTimeRuleIds,
512
662
  ChainFinalizer,
513
663
  ChainHeadSelector,
514
664
  ChainProducersAnalyzer,
@@ -517,13 +667,19 @@ export {
517
667
  ChainSummaryProducersSchema,
518
668
  ChainSummaryStakeIntentSchema,
519
669
  ChainSummaryTransfersSchema,
670
+ DEFAULT_MAX_CLOCK_SKEW_MS,
671
+ DEFAULT_MAX_EVM_LEAD_BLOCKS,
520
672
  TransferAnalyzer,
521
673
  analyzeChain,
674
+ evaluateBlockTime,
675
+ evmAnchorOfBlock,
522
676
  isAdmissionShadowPublishRunner,
523
677
  isChainSummaryBalances,
524
678
  isChainSummaryProducers,
525
679
  isChainSummaryStakeIntent,
526
680
  isChainSummaryTransfers,
527
- isDivergent
681
+ isDivergent,
682
+ signedEpochOfBlock,
683
+ timePayloadOfBlock
528
684
  };
529
685
  //# sourceMappingURL=analyze.mjs.map
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
- "sources": ["../../src/modules/analyze/admission.ts", "../../src/modules/analyze/analyzeChain.ts", "../../src/modules/analyze/BalanceAnalyzer/BalanceAnalyzer.ts", "../../src/modules/analyze/BalanceAnalyzer/ChainSummaryBalances.ts", "../../src/modules/analyze/ChainFinalizer.ts", "../../src/modules/analyze/ChainHeadSelector.ts", "../../src/modules/analyze/producer/ChainProducersAnalyzer.ts", "../../src/modules/analyze/producer/ChainSummaryProducers.ts", "../../src/modules/analyze/producer/ChainStakeIntentAnalyzer.ts", "../../src/modules/analyze/producer/ChainSummaryStakeIntent.ts", "../../src/modules/analyze/TransferAnalyzer/ChainSummaryTransfers.ts", "../../src/modules/analyze/TransferAnalyzer/TransferAnalyzer.ts"],
4
- "sourcesContent": ["import type { Promisable } from '@ariestools/sdk'\nimport type { XyoAddress } from '@xyo-network/sdk'\nimport type { SignedHydratedBlockWithHashMeta } from '@xyo-network/xl1-sdk'\n\nimport type { EligibilityEvaluation } from '#slashing'\n\n/**\n * How a finalizer decides which candidates it will consider.\n *\n * Three postures, and moving between them is a configuration change rather than a different\n * build. That constraint is deliberate: a network that had to be redeployed to step back from\n * enforcement would, in the moment it most needed to step back, be unable to.\n *\n * - `allowlist` \u2014 only the named producers, which is how the chain runs today\n * - `shadow` \u2014 the same gate, plus participation computed and published for every candidate\n * - `enforce` \u2014 participation itself gates\n */\nexport const AdmissionModes = ['allowlist', 'shadow', 'enforce'] as const\n\n/** How a finalizer decides which candidates it will consider. */\nexport type AdmissionMode = typeof AdmissionModes[number]\n\n/** Evaluates whether a candidate's producer had standing to produce it. */\nexport type ProducerEligibilityEvaluator\n = (candidate: SignedHydratedBlockWithHashMeta) => Promisable<EligibilityEvaluation>\n\n/** What both gates concluded about one candidate. */\nexport interface AdmissionAssessment {\n /** Whether the allow-list admits it. */\n admittedByAllowList: boolean\n /** The candidate's height. */\n block: number\n /** The candidate's signed identity. */\n dataHash: string\n /** What the participation predicate concluded. */\n eligibility: EligibilityEvaluation\n /** The producer. */\n producer: XyoAddress\n}\n\n/**\n * What the two gates said about a round of candidates, and where they disagreed.\n *\n * Published rather than logged, because the whole purpose of the shadow posture is that anyone \u2014\n * not just the operator holding the terminal \u2014 can see what enforcement would have done before it\n * does it. An operator convinced by their own logs has convinced nobody.\n */\nexport interface AdmissionShadowReport {\n /** Every candidate considered this round. */\n assessments: AdmissionAssessment[]\n /** Only those where the two gates disagreed. */\n divergent: AdmissionAssessment[]\n /** The finalized height this round was selected against. */\n head: number\n}\n\n/** Moniker the admission shadow publisher resolves under. */\nexport const AdmissionShadowPublishRunnerMoniker = 'AdmissionShadowPublishRunner'\n\n/**\n * Publishes what both admission gates concluded.\n *\n * An interface rather than a class here because the finalizer must not depend on the launcher\n * that supplies the implementation \u2014 the actor states what it needs, and orchestration decides\n * what satisfies it.\n */\nexport interface AdmissionShadowPublishRunner {\n publish(report: AdmissionShadowReport): Promisable<void>\n}\n\n/** Whether a resolved provider can actually publish shadow reports. */\nexport function isAdmissionShadowPublishRunner(value: unknown): value is AdmissionShadowPublishRunner {\n return typeof (value as Partial<AdmissionShadowPublishRunner> | undefined)?.publish === 'function'\n}\n\n/** Whether the two gates reached different conclusions about a candidate. */\nexport function isDivergent(assessment: AdmissionAssessment): boolean {\n // an unresolvable evaluation is not a disagreement \u2014 it is an absence of one\n if (!assessment.eligibility.resolvable) return false\n return assessment.admittedByAllowList !== (assessment.eligibility.eligible === true)\n}\n", "/* eslint-disable complexity */\nimport type { Hash } from '@ariestools/sdk'\nimport {\n assertEx, isError, spanRootAsync,\n} from '@ariestools/sdk'\nimport type { Payload } from '@xyo-network/sdk'\nimport type {\n BlockContextRead,\n ChainAnalyzer,\n HydratedBlock, HydratedBlockWithHashMeta,\n} from '@xyo-network/xl1-sdk'\nimport { hydrateBlock } from '@xyo-network/xl1-sdk'\n\nconst analyzeBlock = async (block: HydratedBlock, handlers: ChainAnalyzer[]): Promise<HydratedBlock> => {\n await Promise.all(handlers.map(async (handler) => {\n await handler.onBlock(block)\n }))\n return block\n}\n\nexport const analyzeChain = async (\n context: BlockContextRead,\n handlers: ChainAnalyzer[],\n /* the hash to start at */\n head: Hash | HydratedBlockWithHashMeta,\n /* a hash that we should stop at if we reach it */\n previousHead: Hash | null = null,\n /* max number of blocks to analyze (-1 for no limit) */\n maxDepth = -1n,\n): Promise<Payload[]> => {\n const { logger } = context\n logger?.log('analyzeChain.start', previousHead)\n return assertEx(await spanRootAsync('analyzeChain', async () => {\n let lastBlockNumber = -1\n let maxDepthLimit = maxDepth === -1n ? BigInt(Number.MAX_SAFE_INTEGER) : maxDepth\n let cursor: HydratedBlockWithHashMeta | null = typeof head === 'string' ? await hydrateBlock(context, head) : head\n // If the terminal block is the current head, we don't need to analyze it\n logger?.log('analyzeChain.cursor', cursor?.[0].block)\n if (previousHead !== null && cursor?.[0]._hash === previousHead) return []\n while (cursor && (maxDepthLimit > 0n)) {\n maxDepthLimit -= 1n\n await analyzeBlock(cursor, handlers)\n lastBlockNumber = cursor[0].block\n try {\n cursor = cursor[0].previous === null ? null : await hydrateBlock(context, cursor[0].previous)\n } catch (ex) {\n if (isError(ex)) {\n logger?.warn('Error in hydrateBlock', ex)\n }\n break\n }\n if (cursor?.[0]._hash === previousHead) break\n }\n if (previousHead === null && maxDepth === -1n && lastBlockNumber !== 0) {\n logger?.warn(`Expected root block number to be 0, but was ${lastBlockNumber} : ${previousHead}`)\n }\n logger?.log('analyzeChain.done', lastBlockNumber)\n return handlers.map((handler) => {\n return handler.result()\n })\n }, context), () => 'analyzeChain failed')\n}\n", "import type { Address, Hash } from '@ariestools/sdk'\nimport { isDefined, toAddress } from '@ariestools/sdk'\nimport { trace, type Tracer } from '@opentelemetry/api'\nimport { PayloadBuilder } from '@xyo-network/sdk'\nimport type {\n ChainAnalyzer,\n HydratedBlockWithHashMeta, SignedBigInt,\n} from '@xyo-network/xl1-sdk'\nimport {\n netBalancesForPayloads,\n parseSignedBigInt, toSignedBigInt,\n} from '@xyo-network/xl1-sdk'\n\nimport type { ChainSummaryBalances } from './ChainSummaryBalances.ts'\nimport { ChainSummaryBalancesSchema } from './ChainSummaryBalances.ts'\n\nconst bigintBalances = (balances: Partial<Record<Address, SignedBigInt>>) => {\n const result: Record<string, bigint> = {}\n for (const [address, balance] of Object.entries(balances)) {\n if (isDefined(balance)) {\n const propertyKey = String(toAddress(address))\n result[propertyKey] = parseSignedBigInt(balance)\n }\n }\n return result\n}\n\nexport class BalanceAnalyzer implements ChainAnalyzer {\n protected _tracer: Tracer | undefined\n private _addresses: Address[]\n private _balances: Record<string, bigint> = {}\n private _firstBlock?: Hash\n private _lastBlock?: Hash\n private _minBalances: bigint[]\n\n constructor(addresses: Address[] = [], minBalances: bigint[] = [], startingBalances?: ChainSummaryBalances) {\n this._addresses = addresses\n this._minBalances = minBalances\n this._balances = startingBalances ? bigintBalances(startingBalances.balances) : {}\n }\n\n protected get tracer() {\n if (this._tracer) return this._tracer\n this._tracer = trace.getTracer('BalanceAnalyzer')\n return this._tracer\n }\n\n onBlock([block, payloads]: HydratedBlockWithHashMeta): boolean {\n this._firstBlock = this._firstBlock ?? block._hash\n this._lastBlock = block._hash\n const netBalances = netBalancesForPayloads({ singletons: {} }, payloads)\n for (const [address, balance] of Object.entries(netBalances)) {\n const propertyKey = String(toAddress(address))\n this._balances[propertyKey] = (this._balances[propertyKey] ?? 0n) + balance\n }\n return this.shouldContinue()\n }\n\n result() {\n const balances: Record<string, SignedBigInt> = {}\n const positiveBalances = Object.entries(this._balances).filter(([, balance]) => balance > 0n)\n for (const [address, balance] of positiveBalances) {\n const propertyKey = String(toAddress(address))\n balances[propertyKey] = toSignedBigInt(balance)\n }\n const builder = new PayloadBuilder<ChainSummaryBalances>({ schema: ChainSummaryBalancesSchema })\n return builder.fields({\n firstBlock: this._firstBlock ?? null,\n lastBlock: this._lastBlock ?? null,\n balances,\n }).build()\n }\n\n shouldContinue() {\n return !this.allMinBalancesReached()\n }\n\n protected allMinBalancesReached() {\n if (this._minBalances.length === 0) return true\n for (let i = 0; i < this._addresses.length; i++) {\n const propertyKey = String(this._addresses[i])\n if ((this._balances[propertyKey] ?? 0n) < this._minBalances[i]) return false\n }\n return true\n }\n}\n", "import {\n type Address, type Hash, type Hex,\n} from '@ariestools/sdk'\nimport type { Payload } from '@xyo-network/sdk'\nimport { asSchema, isPayloadOfSchemaType } from '@xyo-network/sdk'\nimport type { SignedBigInt } from '@xyo-network/xl1-sdk'\n\nexport const ChainSummaryBalancesSchema = asSchema('network.xyo.chain.summary.balances', true)\nexport type ChainSummaryBalancesSchema = typeof ChainSummaryBalancesSchema\n\nexport interface ChainSummaryBalancesFields {\n balances: Partial<Record<Address, SignedBigInt>>\n firstBlock: Hash | null\n hasMinBalance?: Record<Address, Hex>\n lastBlock: Hash | null\n}\n\nexport type ChainSummaryBalances = Payload<ChainSummaryBalancesFields, ChainSummaryBalancesSchema>\nexport const isChainSummaryBalances = isPayloadOfSchemaType<ChainSummaryBalances>(ChainSummaryBalancesSchema)\n", "import type { BaseParams, Promisable } from '@ariestools/sdk'\nimport { Base } from '@ariestools/sdk'\nimport type { Tracer } from '@opentelemetry/api'\nimport type { BlockViewer, SignedHydratedBlockWithHashMeta } from '@xyo-network/xl1-sdk'\nimport { asXL1BlockRange } from '@xyo-network/xl1-sdk'\n\nexport interface ChainFinalizerParams extends BaseParams {\n blockViewer: BlockViewer\n maxWindowSize?: number\n windowedFinalizedChain?: SignedHydratedBlockWithHashMeta[]\n}\n\nexport class ChainFinalizer<TParams extends ChainFinalizerParams = ChainFinalizerParams> extends Base<TParams> {\n protected _tracer: Tracer | undefined\n // the chainArchivist is assumed to be an archivist with already validated blocks in it along with all the transactions and payloads\n // it is also assumed that the sequence order of archivist is that newer blocks come after older blocks\n\n protected windowedFinalizedChain: SignedHydratedBlockWithHashMeta[]\n\n constructor(params: TParams) {\n super(params)\n this.windowedFinalizedChain = [...(params.windowedFinalizedChain ?? [])]\n }\n\n protected get blockViewer() {\n return this.params.blockViewer\n }\n\n protected get finalizedBlockNumber() {\n return this.windowedFinalizedChain?.at(-1)?.[0].block ?? -1\n }\n\n protected get maxWindowSize() {\n return this.params.maxWindowSize ?? 100\n }\n\n protected get windowRange() {\n return asXL1BlockRange([this.windowedFinalizedChain.at(0)?.[0].block ?? 0, this.finalizedBlockNumber])\n }\n\n finalizeChainFragment(chain: SignedHydratedBlockWithHashMeta[]): Promisable<void> {\n this.windowedFinalizedChain = [...this.windowedFinalizedChain, ...chain].slice(-this.maxWindowSize)\n }\n}\n", "import type { Address } from '@ariestools/sdk'\nimport {\n isDefined, spanRootAsync, toAddress,\n} from '@ariestools/sdk'\nimport type {\n BaseContext, MempoolViewer, SignedHydratedBlockWithHashMeta,\n} from '@xyo-network/xl1-sdk'\nimport {\n findBestUncle, findUncles, getWindowedChain, isTransactionBoundWitness,\n} from '@xyo-network/xl1-sdk'\n\nimport type {\n AdmissionAssessment, AdmissionMode, AdmissionShadowReport, ProducerEligibilityEvaluator,\n} from './admission.ts'\nimport { isDivergent } from './admission.ts'\nimport type { ChainFinalizerParams } from './ChainFinalizer.ts'\nimport { ChainFinalizer } from './ChainFinalizer.ts'\n\nexport interface ChainHeadSelectorParams extends ChainFinalizerParams {\n /**\n * How candidates are admitted. Defaults to `allowlist`, which is how the chain runs today.\n */\n admission?: AdmissionMode\n /**\n * List of allowed producer addresses, if undefined anyone can participate\n */\n allowedProducers?: Address[]\n /**\n * Back-off timeout (ms) after which a head is accepted even with fewer than\n * minCandidates uncle chains available.\n * When undefined, findBestUncle applies DEFAULT_BACKOFF_MS from the protocol SDK.\n */\n backoffMs?: number\n context: BaseContext\n /**\n * Computes whether a candidate's producer had standing. Absent means the question is never\n * asked, which is what keeps a node with no stake readers running exactly as it does today.\n */\n eligibility?: ProducerEligibilityEvaluator\n /**\n * Minimum epoch spacing (ms) between transaction-less heartbeat blocks this selector will\n * qualify \u2014 a defense against producers heartbeating faster than the network's intended pace.\n * Transaction-carrying candidates are exempt. Undefined disables the check.\n */\n heartbeatInterval?: number\n mempoolViewer: MempoolViewer\n /**\n * Minimum number of uncle candidates required before accepting one.\n * When undefined, findBestUncle applies DEFAULT_MIN_CANDIDATES from the protocol SDK.\n */\n minCandidates?: number\n /** Where to publish what the two gates concluded, in shadow posture. */\n publishAdmissionShadow?: (report: AdmissionShadowReport) => Promise<void>\n}\n\ninterface UncleSelectionHealth {\n lastWarnedAt: number\n unqualifiedSince?: number\n}\n\nexport class ChainHeadSelector extends ChainFinalizer<ChainHeadSelectorParams> {\n /**\n * How long uncle selection may go without a single qualifying candidate (while candidates are\n * present) before a warning is emitted; also the minimum interval between repeat warnings.\n */\n static readonly SilentUncleRejectionWarnAfterMs = 5000\n\n protected admission: AdmissionMode\n protected allowedProducers?: Address[]\n protected backoffMs?: number\n protected heartbeatInterval?: number\n protected minCandidates?: number\n private _timeSinceLastUncle = Date.now()\n\n constructor(params: ChainHeadSelectorParams) {\n super(params)\n this.admission = params.admission ?? 'allowlist'\n this.allowedProducers = params.allowedProducers\n this.backoffMs = params.backoffMs\n this.heartbeatInterval = params.heartbeatInterval\n this.minCandidates = params.minCandidates\n }\n\n protected get context() {\n return this.params.context\n }\n\n protected get mempoolViewer() {\n return this.params.mempoolViewer\n }\n\n async findBestHead(): Promise<SignedHydratedBlockWithHashMeta[]> {\n return await spanRootAsync('findBestHead', async () => {\n const allProposedBlocks = await this.mempoolViewer.pendingBlocks()\n const allowed = allProposedBlocks.filter(pb => this.isAllowed(pb))\n const proposedBlocks = await this.admit(allProposedBlocks, allowed)\n\n // TODO: Validate proposed blocks\n this.windowedFinalizedChain = await getWindowedChain(this.context, this.blockViewer, this.maxWindowSize, this.windowedFinalizedChain)\n const pacedBlocks = this.filterPacedHeartbeatCandidates(proposedBlocks)\n const uncles = findUncles(this.context, this.windowedFinalizedChain, pacedBlocks)\n this.reportSilentUncleRejections(pacedBlocks, uncles.length)\n return findBestUncle(this.windowedFinalizedChain, uncles, { backoffMs: this.backoffMs, minCandidates: this.minCandidates }) ?? this.windowedFinalizedChain\n }, this.context)\n }\n\n /**\n * Applies the configured admission posture.\n *\n * In `shadow` the allow-list still decides \u2014 participation is computed and published beside it,\n * and changes nothing. That is the entire value of the posture: the network keeps behaving\n * exactly as it did while everyone gets to see what the new rule would have done.\n *\n * In `enforce` participation gates, but only when it reached a conclusion. A candidate whose\n * standing cannot be established is admitted, not rejected \u2014 otherwise a finalizer that lost\n * its stake reader would quietly stop accepting anyone's blocks and stall the chain.\n *\n * Assessments publish in both postures. The audit trail must not disappear at the moment\n * admission starts gating \u2014 enforce is when it matters most.\n */\n protected async admit(\n all: SignedHydratedBlockWithHashMeta[],\n allowed: SignedHydratedBlockWithHashMeta[],\n ): Promise<SignedHydratedBlockWithHashMeta[]> {\n if (this.admission === 'allowlist' || this.params.eligibility === undefined) return allowed\n const assessments = await this.assess(all)\n await this.publishShadow(assessments)\n if (this.admission === 'shadow') return allowed\n const ineligible = new Set(assessments\n .filter(assessment => assessment.eligibility.resolvable && assessment.eligibility.eligible !== true)\n .map(assessment => assessment.dataHash))\n return allowed.filter(candidate => !ineligible.has(candidate[0]._dataHash))\n }\n\n /**\n * Runs both gates over every candidate.\n *\n * An evaluator that throws yields an unresolvable assessment rather than an exception: this\n * sits on the head-selection path, and a flaky stake reader must never be able to stop the\n * chain from finalizing.\n */\n protected async assess(all: SignedHydratedBlockWithHashMeta[]): Promise<AdmissionAssessment[]> {\n const evaluate = this.params.eligibility\n if (evaluate === undefined) return []\n const evaluateSafely = async (candidate: SignedHydratedBlockWithHashMeta) => {\n try {\n return await evaluate(candidate)\n } catch {\n return { resolvable: false }\n }\n }\n return await Promise.all(all.map(async candidate => ({\n admittedByAllowList: this.isAllowed(candidate),\n block: candidate[0].block,\n dataHash: candidate[0]._dataHash,\n eligibility: await evaluateSafely(candidate),\n producer: candidate[0].addresses[0],\n })))\n }\n\n /**\n * Defense against unwanted heartbeat rates: a transaction-less (heartbeat) candidate qualifies\n * only when its signing epoch is at least heartbeatInterval after the current head's. Measured\n * on chain data, so every finalizer applies the same pace deterministically regardless of wall\n * clock. A producer heartbeating faster than the network's pace \u2014 misconfigured or hostile \u2014\n * cannot drive the chain's block rate: its candidates never qualify and age out of the pool,\n * while transaction-carrying candidates are exempt and finalize immediately.\n */\n protected filterPacedHeartbeatCandidates(proposedBlocks: SignedHydratedBlockWithHashMeta[]): SignedHydratedBlockWithHashMeta[] {\n const interval = this.heartbeatInterval\n if (interval === undefined || proposedBlocks.length === 0) return proposedBlocks\n const headEpoch = this.windowedFinalizedChain.at(-1)?.[0].$epoch\n if (!isDefined(headEpoch)) return proposedBlocks\n const paced = proposedBlocks.filter((candidate) => {\n if (candidate[1].some(payload => isTransactionBoundWitness(payload))) return true\n const candidateEpoch = candidate[0].$epoch\n // a candidate without an epoch cannot be paced here; shape validation owns that case\n if (!isDefined(candidateEpoch)) return true\n return candidateEpoch - headEpoch >= interval\n })\n if (paced.length < proposedBlocks.length) {\n this.context.logger?.debug(\n `findBestHead: paced out ${proposedBlocks.length - paced.length} heartbeat candidate(s) `\n + `younger than ${interval}ms since the head`,\n )\n }\n return paced\n }\n\n /** Whether the configured allow-list admits a candidate. An empty list admits everyone. */\n protected isAllowed(candidate: SignedHydratedBlockWithHashMeta): boolean {\n const allowedProducers = this.allowedProducers\n return isDefined(allowedProducers) && allowedProducers.length > 0\n ? allowedProducers.includes(toAddress(candidate[0].addresses[0]))\n : true\n }\n\n /** Publishes what the two gates concluded, swallowing failures. */\n protected async publishShadow(assessments: AdmissionAssessment[]): Promise<void> {\n const publish = this.params.publishAdmissionShadow\n // an empty round says nothing worth a write\n if (publish === undefined || assessments.length === 0) return\n try {\n await publish({\n assessments,\n divergent: assessments.filter(assessment => isDivergent(assessment)),\n head: this.windowedFinalizedChain[0]?.[0]?.block ?? 0,\n })\n } catch {\n // observing must never be able to stop the chain from finalizing\n }\n }\n\n /**\n * Surfaces the otherwise-silent case where candidates are continuously present but none\n * qualifies as an uncle \u2014 without this, the only signal of a stalled selector is an endless\n * \"No new blocks found N to N\" while rejected candidates cycle through the pool. Zero-qualify\n * moments are routine in healthy operation (a just-finalized candidate lingers until the pool\n * GC), so this warns only after candidates have been failing continuously for\n * SilentUncleRejectionWarnAfterMs \u2014 an idle (candidate-free) tick resets the clock \u2014 then\n * rate-limits to one warn per that interval. State lives in context.singletons because a\n * fresh ChainHeadSelector is constructed on every finalization tick.\n */\n private reportSilentUncleRejections(proposedBlocks: SignedHydratedBlockWithHashMeta[], uncleCount: number): void {\n const singletons = this.context.singletons\n const stateKey = 'ChainHeadSelector:uncleSelectionHealth'\n const now = Date.now()\n const state = (singletons[stateKey] ?? { lastWarnedAt: 0 }) as UncleSelectionHealth\n singletons[stateKey] = state\n if (uncleCount > 0 || proposedBlocks.length === 0) {\n state.unqualifiedSince = undefined\n return\n }\n state.unqualifiedSince ??= now\n if (now - state.unqualifiedSince < ChainHeadSelector.SilentUncleRejectionWarnAfterMs) return\n if (now - state.lastWarnedAt < ChainHeadSelector.SilentUncleRejectionWarnAfterMs) return\n state.lastWarnedAt = now\n this.context.logger?.warn(\n `findBestHead: candidates have failed uncle qualification continuously for ${now - state.unqualifiedSince}ms \u2014 `\n + `${proposedBlocks.length} pending candidate(s): `\n + proposedBlocks.map(b => `${b[0].block} [${b[0]._hash}]`).join(', '),\n )\n }\n}\n\n/*\n\nComparing two heads for which is better\n\nProducer Energy\n\n1. +1 Point for every block not produced by it in recent chain LOG(active producers * 100)\n2a. Energy Multiplied by - # of transactions in block is above normal (last 1000 blocks) (+0.5 per std deviation) (max 2.5)\n2b. Energy Divided by - # of transactions in block is below normal (last 1000 blocks) (+0.5 per std deviation) (max 2.5)\n3a. Energy Multiplied by - # of gas in block is above normal (last 1000 blocks) (+0.5 per std deviation) (max 2.5)\n3b. Energy Divided by - # of gas in block is below normal (last 1000 blocks) (+0.5 per std deviation) (max 2.5)\n\nOther Ideas\n\n1. Total diverse addresses in block (not quite incentivizing what we really want)\n2. Total transactions in block (more is better, encourages non-lethargic blocks)\n3. Sum total of transactions in block (higher value means more influential/better)\n4. Sum of gas of transactions in block (higher is better, prioritizes paying contributors)\n5. Exp of transactions in block (lower is better, prioritizes not aging out before inclusion)\n6. Recency of this producer's last produced block (encourages diversification in block producers)\n\n*/\n", "import type { Promisable } from '@ariestools/sdk'\nimport { isDefined, toAddress } from '@ariestools/sdk'\nimport { type ChainAnalyzer, type HydratedBlock } from '@xyo-network/xl1-sdk'\n\nimport type { AnalyzedProducer, ChainSummaryProducers } from './ChainSummaryProducers.ts'\nimport { ChainSummaryProducersSchema } from './ChainSummaryProducers.ts'\n\nexport class ChainProducersAnalyzer implements ChainAnalyzer<ChainSummaryProducers> {\n private endingBlock = 0\n private producers: Record<string, AnalyzedProducer> = {}\n private startingBlock = 0\n\n onBlock([block]: HydratedBlock): Promisable<boolean> {\n // avoid analyzing the same block twice\n if (block.block === this.endingBlock) return true\n\n // update the ending block\n this.endingBlock = block.block\n const blockProducerAddressRaw = block.addresses[0]\n\n if (blockProducerAddressRaw === undefined) return true\n\n const blockProducerAddress = toAddress(blockProducerAddressRaw)\n\n const producerKey = String(blockProducerAddress)\n const existingProducer: AnalyzedProducer | undefined = this.producers[producerKey]\n if (isDefined(existingProducer)) {\n existingProducer.rewardsEarned = (existingProducer.rewardsEarned ?? 0n) + 0n\n existingProducer.blocksProduced++\n } else {\n const newProducer: AnalyzedProducer = {\n address: blockProducerAddress, rewardsEarned: 0n, blocksProduced: 1,\n }\n this.producers[producerKey] = newProducer\n }\n\n return true\n }\n\n result(): ChainSummaryProducers {\n return {\n endingBlock: this.endingBlock,\n producers: this.producers,\n schema: ChainSummaryProducersSchema,\n startingBlock: this.startingBlock,\n }\n }\n\n shouldContinue(): boolean {\n return true\n }\n}\n", "import type { Address } from '@ariestools/sdk'\nimport type { Payload } from '@xyo-network/sdk'\nimport { asSchema, isPayloadOfSchemaType } from '@xyo-network/sdk'\n\nexport const ChainSummaryProducersSchema = asSchema('network.xyo.chain.summary.producers', true)\nexport type ChainSummaryProducersSchema = typeof ChainSummaryProducersSchema\n\nexport interface AnalyzedProducer {\n address: Address\n blocksProduced: number\n rewardsEarned: bigint\n}\n\nexport interface ChainSummaryProducersFields {\n endingBlock: number\n producers: Record<string, AnalyzedProducer>\n startingBlock: number\n}\n\nexport type ChainSummaryProducers = Payload<ChainSummaryProducersFields, ChainSummaryProducersSchema>\nexport const isChainSummaryProducers = isPayloadOfSchemaType<ChainSummaryProducers>(ChainSummaryProducersSchema)\n", "import type { Promisable } from '@ariestools/sdk'\nimport { assertEx } from '@ariestools/sdk'\nimport {\n type ChainAnalyzer, type ChainStakeIntent, type HydratedBlock,\n type Intent,\n isChainStakeIntentWithStorageMeta,\n} from '@xyo-network/xl1-sdk'\n\nimport type { ChainSummaryStakeIntent } from './ChainSummaryStakeIntent.ts'\nimport { ChainSummaryStakeIntentSchema } from './ChainSummaryStakeIntent.ts'\n\nexport class ChainStakeIntentAnalyzer implements ChainAnalyzer<ChainSummaryStakeIntent> {\n private _endingBlock: number | undefined = undefined\n private _intent: Intent\n private _intentPayloads: ChainStakeIntent[] = []\n private _startingBlock: number | undefined = undefined\n\n constructor(intent: Intent) {\n this._intent = intent\n }\n\n onBlock([block, validatedPayloads]: HydratedBlock): Promisable<boolean> {\n this._startingBlock ??= block.block\n\n // Update the ending block\n this._endingBlock = block.block\n const intents = validatedPayloads.filter(isChainStakeIntentWithStorageMeta)\n const desiredIntents = intents.filter(intent => intent.intent === this._intent)\n this._intentPayloads.push(...desiredIntents)\n return true\n }\n\n result(): ChainSummaryStakeIntent {\n assertEx(this._startingBlock !== undefined, () => 'ChainStakeIntentAnalyzer: Invalid startingBlock')\n assertEx(this._endingBlock !== undefined, () => 'ChainStakeIntentAnalyzer: Invalid endingBlock')\n return {\n endingBlock: this._endingBlock!,\n intents: this._intentPayloads,\n schema: ChainSummaryStakeIntentSchema,\n startingBlock: this._startingBlock!,\n }\n }\n\n shouldContinue(): boolean {\n return true\n }\n}\n", "import type { Payload } from '@xyo-network/sdk'\nimport { asSchema, isPayloadOfSchemaType } from '@xyo-network/sdk'\nimport type { ChainStakeIntent } from '@xyo-network/xl1-sdk'\n\nexport const ChainSummaryStakeIntentSchema = asSchema('network.xyo.chain.summary.staked.intent', true)\nexport type ChainSummaryStakeIntentSchema = typeof ChainSummaryStakeIntentSchema\n\ninterface ChainSummaryStakeIntentFields {\n endingBlock: number\n intents: ChainStakeIntent[]\n startingBlock: number\n}\n\nexport type ChainSummaryStakeIntent = Payload<ChainSummaryStakeIntentFields, ChainSummaryStakeIntentSchema>\nexport const isChainSummaryStakeIntent = isPayloadOfSchemaType<ChainSummaryStakeIntent>(ChainSummaryStakeIntentSchema)\n", "import {\n type Address, type Hash, type Hex,\n} from '@ariestools/sdk'\nimport type { Payload } from '@xyo-network/sdk'\nimport { asSchema, isPayloadOfSchemaType } from '@xyo-network/sdk'\nimport type { SignedBigInt } from '@xyo-network/xl1-sdk'\n\nexport const ChainSummaryTransfersSchema = asSchema('network.xyo.chain.summary.transfers', true)\nexport type ChainSummaryTransfersSchema = typeof ChainSummaryTransfersSchema\n\nexport interface ChainSummaryTransfersFields {\n firstBlock: Hash | null\n hasMinTransfer?: Record<Address, Hex>\n lastBlock: Hash | null\n transfers: Record<Address, Record<Address, SignedBigInt>>\n}\n\nexport type ChainSummaryTransfers = Payload<ChainSummaryTransfersFields, ChainSummaryTransfersSchema>\nexport const isChainSummaryTransfers = isPayloadOfSchemaType<ChainSummaryTransfers>(ChainSummaryTransfersSchema)\n", "import type { Address, Hash } from '@ariestools/sdk'\nimport { isDefined, toAddress } from '@ariestools/sdk'\nimport { trace, type Tracer } from '@opentelemetry/api'\nimport { PayloadBuilder } from '@xyo-network/sdk'\nimport type {\n ChainAnalyzer,\n HydratedBlockWithHashMeta, SignedBigInt,\n} from '@xyo-network/xl1-sdk'\nimport {\n netTransfersForPayloads,\n parseSignedBigInt, toSignedBigInt,\n} from '@xyo-network/xl1-sdk'\n\nimport type { ChainSummaryTransfers } from './ChainSummaryTransfers.ts'\nimport { ChainSummaryTransfersSchema } from './ChainSummaryTransfers.ts'\n\nconst bigintTransfers = (transfers: Record<Address, Record<Address, SignedBigInt>>) => {\n const result: Record<string, Record<string, bigint>> = {}\n for (const [from, toMap] of Object.entries(transfers)) {\n const fromKey = String(toAddress(from))\n for (const [to, balance] of Object.entries(toMap)) {\n const toKey = String(toAddress(to))\n if (isDefined(balance)) {\n const fromTransfers = result[fromKey] ?? {}\n result[fromKey] = fromTransfers\n fromTransfers[toKey] = parseSignedBigInt(balance)\n }\n }\n }\n return result\n}\n\nexport class TransferAnalyzer implements ChainAnalyzer {\n protected _tracer: Tracer | undefined\n private _firstBlock?: Hash\n private _lastBlock?: Hash\n private _transfers: Record<string, Record<string, bigint>> = {}\n\n constructor(startingTransfers?: ChainSummaryTransfers) {\n this._transfers = startingTransfers ? bigintTransfers(startingTransfers.transfers) : {}\n }\n\n protected get tracer() {\n if (this._tracer) return this._tracer\n this._tracer = trace.getTracer('TransferAnalyzer')\n return this._tracer\n }\n\n onBlock([block, payloads]: HydratedBlockWithHashMeta): boolean {\n this._firstBlock = this._firstBlock ?? block._hash\n this._lastBlock = block._hash\n const netTransfers = netTransfersForPayloads({ singletons: {} }, payloads)\n for (const [from, toMap] of Object.entries(netTransfers)) {\n const fromKey = String(toAddress(from))\n const fromTransfers = this._transfers[fromKey] ?? {}\n this._transfers[fromKey] = fromTransfers\n for (const [to, balance] of Object.entries(toMap)) {\n const toKey = String(toAddress(to))\n fromTransfers[toKey] = (fromTransfers[toKey] ?? 0n) + balance\n }\n }\n return this.shouldContinue()\n }\n\n result() {\n const transfers: Record<string, Record<string, SignedBigInt>> = {}\n for (const [from, toMap] of Object.entries(this._transfers)) {\n const fromKey = String(toAddress(from))\n const positiveTransfers = Object.entries(toMap).filter(([, balance]) => balance > 0n)\n for (const [to, balance] of positiveTransfers) {\n const fromTransfers = transfers[fromKey] ?? {}\n transfers[fromKey] = fromTransfers\n const toKey = String(toAddress(to))\n fromTransfers[toKey] = toSignedBigInt(balance)\n }\n }\n const builder = new PayloadBuilder<ChainSummaryTransfers>({ schema: ChainSummaryTransfersSchema })\n return builder.fields({\n firstBlock: this._firstBlock ?? null,\n lastBlock: this._lastBlock ?? null,\n transfers,\n }).build()\n }\n\n shouldContinue() {\n return true\n }\n}\n"],
5
- "mappings": "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",
3
+ "sources": ["../../src/modules/analyze/admission.ts", "../../src/modules/analyze/analyzeChain.ts", "../../src/modules/analyze/BalanceAnalyzer/BalanceAnalyzer.ts", "../../src/modules/analyze/BalanceAnalyzer/ChainSummaryBalances.ts", "../../src/modules/analyze/blockTime.ts", "../../src/modules/analyze/ChainFinalizer.ts", "../../src/modules/analyze/ChainHeadSelector.ts", "../../src/modules/analyze/producer/ChainProducersAnalyzer.ts", "../../src/modules/analyze/producer/ChainSummaryProducers.ts", "../../src/modules/analyze/producer/ChainStakeIntentAnalyzer.ts", "../../src/modules/analyze/producer/ChainSummaryStakeIntent.ts", "../../src/modules/analyze/TransferAnalyzer/ChainSummaryTransfers.ts", "../../src/modules/analyze/TransferAnalyzer/TransferAnalyzer.ts"],
4
+ "sourcesContent": ["import type { Promisable } from '@ariestools/sdk'\nimport type { XyoAddress } from '@xyo-network/sdk'\nimport type { SignedHydratedBlockWithHashMeta } from '@xyo-network/xl1-sdk'\n\nimport type { EligibilityEvaluation } from '#slashing'\n\n/**\n * How a finalizer decides which candidates it will consider.\n *\n * Three postures, and moving between them is a configuration change rather than a different\n * build. That constraint is deliberate: a network that had to be redeployed to step back from\n * enforcement would, in the moment it most needed to step back, be unable to.\n *\n * - `allowlist` \u2014 only the named producers, which is how the chain runs today\n * - `shadow` \u2014 the same gate, plus participation computed and published for every candidate\n * - `enforce` \u2014 participation itself gates\n */\nexport const AdmissionModes = ['allowlist', 'shadow', 'enforce'] as const\n\n/** How a finalizer decides which candidates it will consider. */\nexport type AdmissionMode = typeof AdmissionModes[number]\n\n/** Evaluates whether a candidate's producer had standing to produce it. */\nexport type ProducerEligibilityEvaluator\n = (candidate: SignedHydratedBlockWithHashMeta) => Promisable<EligibilityEvaluation>\n\n/** What both gates concluded about one candidate. */\nexport interface AdmissionAssessment {\n /** Whether the allow-list admits it. */\n admittedByAllowList: boolean\n /** The candidate's height. */\n block: number\n /** The candidate's signed identity. */\n dataHash: string\n /** What the participation predicate concluded. */\n eligibility: EligibilityEvaluation\n /** The producer. */\n producer: XyoAddress\n}\n\n/**\n * What the two gates said about a round of candidates, and where they disagreed.\n *\n * Published rather than logged, because the whole purpose of the shadow posture is that anyone \u2014\n * not just the operator holding the terminal \u2014 can see what enforcement would have done before it\n * does it. An operator convinced by their own logs has convinced nobody.\n */\nexport interface AdmissionShadowReport {\n /** Every candidate considered this round. */\n assessments: AdmissionAssessment[]\n /** Only those where the two gates disagreed. */\n divergent: AdmissionAssessment[]\n /** The finalized height this round was selected against. */\n head: number\n}\n\n/** Moniker the admission shadow publisher resolves under. */\nexport const AdmissionShadowPublishRunnerMoniker = 'AdmissionShadowPublishRunner'\n\n/**\n * Publishes what both admission gates concluded.\n *\n * An interface rather than a class here because the finalizer must not depend on the launcher\n * that supplies the implementation \u2014 the actor states what it needs, and orchestration decides\n * what satisfies it.\n */\nexport interface AdmissionShadowPublishRunner {\n publish(report: AdmissionShadowReport): Promisable<void>\n}\n\n/** Whether a resolved provider can actually publish shadow reports. */\nexport function isAdmissionShadowPublishRunner(value: unknown): value is AdmissionShadowPublishRunner {\n return typeof (value as Partial<AdmissionShadowPublishRunner> | undefined)?.publish === 'function'\n}\n\n/** Whether the two gates reached different conclusions about a candidate. */\nexport function isDivergent(assessment: AdmissionAssessment): boolean {\n // an unresolvable evaluation is not a disagreement \u2014 it is an absence of one\n if (!assessment.eligibility.resolvable) return false\n return assessment.admittedByAllowList !== (assessment.eligibility.eligible === true)\n}\n", "/* eslint-disable complexity */\nimport type { Hash } from '@ariestools/sdk'\nimport {\n assertEx, isError, spanRootAsync,\n} from '@ariestools/sdk'\nimport type { Payload } from '@xyo-network/sdk'\nimport type {\n BlockContextRead,\n ChainAnalyzer,\n HydratedBlock, HydratedBlockWithHashMeta,\n} from '@xyo-network/xl1-sdk'\nimport { hydrateBlock } from '@xyo-network/xl1-sdk'\n\nconst analyzeBlock = async (block: HydratedBlock, handlers: ChainAnalyzer[]): Promise<HydratedBlock> => {\n await Promise.all(handlers.map(async (handler) => {\n await handler.onBlock(block)\n }))\n return block\n}\n\nexport const analyzeChain = async (\n context: BlockContextRead,\n handlers: ChainAnalyzer[],\n /* the hash to start at */\n head: Hash | HydratedBlockWithHashMeta,\n /* a hash that we should stop at if we reach it */\n previousHead: Hash | null = null,\n /* max number of blocks to analyze (-1 for no limit) */\n maxDepth = -1n,\n): Promise<Payload[]> => {\n const { logger } = context\n logger?.log('analyzeChain.start', previousHead)\n return assertEx(await spanRootAsync('analyzeChain', async () => {\n let lastBlockNumber = -1\n let maxDepthLimit = maxDepth === -1n ? BigInt(Number.MAX_SAFE_INTEGER) : maxDepth\n let cursor: HydratedBlockWithHashMeta | null = typeof head === 'string' ? await hydrateBlock(context, head) : head\n // If the terminal block is the current head, we don't need to analyze it\n logger?.log('analyzeChain.cursor', cursor?.[0].block)\n if (previousHead !== null && cursor?.[0]._hash === previousHead) return []\n while (cursor && (maxDepthLimit > 0n)) {\n maxDepthLimit -= 1n\n await analyzeBlock(cursor, handlers)\n lastBlockNumber = cursor[0].block\n try {\n cursor = cursor[0].previous === null ? null : await hydrateBlock(context, cursor[0].previous)\n } catch (ex) {\n if (isError(ex)) {\n logger?.warn('Error in hydrateBlock', ex)\n }\n break\n }\n if (cursor?.[0]._hash === previousHead) break\n }\n if (previousHead === null && maxDepth === -1n && lastBlockNumber !== 0) {\n logger?.warn(`Expected root block number to be 0, but was ${lastBlockNumber} : ${previousHead}`)\n }\n logger?.log('analyzeChain.done', lastBlockNumber)\n return handlers.map((handler) => {\n return handler.result()\n })\n }, context), () => 'analyzeChain failed')\n}\n", "import type { Address, Hash } from '@ariestools/sdk'\nimport { isDefined, toAddress } from '@ariestools/sdk'\nimport { trace, type Tracer } from '@opentelemetry/api'\nimport { PayloadBuilder } from '@xyo-network/sdk'\nimport type {\n ChainAnalyzer,\n HydratedBlockWithHashMeta, SignedBigInt,\n} from '@xyo-network/xl1-sdk'\nimport {\n netBalancesForPayloads,\n parseSignedBigInt, toSignedBigInt,\n} from '@xyo-network/xl1-sdk'\n\nimport type { ChainSummaryBalances } from './ChainSummaryBalances.ts'\nimport { ChainSummaryBalancesSchema } from './ChainSummaryBalances.ts'\n\nconst bigintBalances = (balances: Partial<Record<Address, SignedBigInt>>) => {\n const result: Record<string, bigint> = {}\n for (const [address, balance] of Object.entries(balances)) {\n if (isDefined(balance)) {\n const propertyKey = String(toAddress(address))\n result[propertyKey] = parseSignedBigInt(balance)\n }\n }\n return result\n}\n\nexport class BalanceAnalyzer implements ChainAnalyzer {\n protected _tracer: Tracer | undefined\n private _addresses: Address[]\n private _balances: Record<string, bigint> = {}\n private _firstBlock?: Hash\n private _lastBlock?: Hash\n private _minBalances: bigint[]\n\n constructor(addresses: Address[] = [], minBalances: bigint[] = [], startingBalances?: ChainSummaryBalances) {\n this._addresses = addresses\n this._minBalances = minBalances\n this._balances = startingBalances ? bigintBalances(startingBalances.balances) : {}\n }\n\n protected get tracer() {\n if (this._tracer) return this._tracer\n this._tracer = trace.getTracer('BalanceAnalyzer')\n return this._tracer\n }\n\n onBlock([block, payloads]: HydratedBlockWithHashMeta): boolean {\n this._firstBlock = this._firstBlock ?? block._hash\n this._lastBlock = block._hash\n const netBalances = netBalancesForPayloads({ singletons: {} }, payloads)\n for (const [address, balance] of Object.entries(netBalances)) {\n const propertyKey = String(toAddress(address))\n this._balances[propertyKey] = (this._balances[propertyKey] ?? 0n) + balance\n }\n return this.shouldContinue()\n }\n\n result() {\n const balances: Record<string, SignedBigInt> = {}\n const positiveBalances = Object.entries(this._balances).filter(([, balance]) => balance > 0n)\n for (const [address, balance] of positiveBalances) {\n const propertyKey = String(toAddress(address))\n balances[propertyKey] = toSignedBigInt(balance)\n }\n const builder = new PayloadBuilder<ChainSummaryBalances>({ schema: ChainSummaryBalancesSchema })\n return builder.fields({\n firstBlock: this._firstBlock ?? null,\n lastBlock: this._lastBlock ?? null,\n balances,\n }).build()\n }\n\n shouldContinue() {\n return !this.allMinBalancesReached()\n }\n\n protected allMinBalancesReached() {\n if (this._minBalances.length === 0) return true\n for (let i = 0; i < this._addresses.length; i++) {\n const propertyKey = String(this._addresses[i])\n if ((this._balances[propertyKey] ?? 0n) < this._minBalances[i]) return false\n }\n return true\n }\n}\n", "import {\n type Address, type Hash, type Hex,\n} from '@ariestools/sdk'\nimport type { Payload } from '@xyo-network/sdk'\nimport { asSchema, isPayloadOfSchemaType } from '@xyo-network/sdk'\nimport type { SignedBigInt } from '@xyo-network/xl1-sdk'\n\nexport const ChainSummaryBalancesSchema = asSchema('network.xyo.chain.summary.balances', true)\nexport type ChainSummaryBalancesSchema = typeof ChainSummaryBalancesSchema\n\nexport interface ChainSummaryBalancesFields {\n balances: Partial<Record<Address, SignedBigInt>>\n firstBlock: Hash | null\n hasMinBalance?: Record<Address, Hex>\n lastBlock: Hash | null\n}\n\nexport type ChainSummaryBalances = Payload<ChainSummaryBalancesFields, ChainSummaryBalancesSchema>\nexport const isChainSummaryBalances = isPayloadOfSchemaType<ChainSummaryBalances>(ChainSummaryBalancesSchema)\n", "import type { Hash, Promisable } from '@ariestools/sdk'\nimport type { XyoAddress } from '@xyo-network/sdk'\nimport type {\n HydratedBlock, SignedHydratedBlockWithHashMeta, TimePayload,\n} from '@xyo-network/xl1-sdk'\nimport { isTimePayload } from '@xyo-network/xl1-sdk'\n\n/**\n * How far ahead of this observer's clock a candidate's signed epoch may sit before it is refused.\n *\n * Producers and finalizers keep their own clocks, and a candidate reaches the finalizer some time\n * after it was signed, so a strict \"not after now\" would refuse honest blocks from any producer\n * whose clock runs slightly ahead. The allowance is what keeps this a defense against far-future\n * claims rather than a lockout for imperfect NTP.\n */\nexport const DEFAULT_MAX_CLOCK_SKEW_MS = 30_000\n\n/**\n * How many EVM blocks past this observer's EVM head a candidate's anchor may sit before it is refused.\n *\n * Two nodes reading the tip through different RPC providers routinely disagree by a block for a\n * few seconds, and that disagreement is about the observers, not the producer.\n */\nexport const DEFAULT_MAX_EVM_LEAD_BLOCKS = 2\n\n/** The time rules, named so a rejection says which one a candidate failed. */\nexport const BlockTimeRuleIds = [\n 'time-present', 'epoch-after-parent', 'epoch-not-future', 'anchor-monotonic', 'anchor-not-ahead',\n] as const\n\n/** One of the time rules. */\nexport type BlockTimeRuleId = typeof BlockTimeRuleIds[number]\n\n/** What an observer knows about the present, resolved to numbers a rule can compare against. */\nexport interface BlockTimeBounds {\n /** The observer's EVM head; absent skips the anchor upper bound. */\n evmHead?: number\n /** The observer's wall clock, in epoch milliseconds. */\n now: number\n}\n\n/** Tolerances the observer-relative rules apply. */\nexport interface BlockTimeRuleOptions {\n /** Defaults to {@link DEFAULT_MAX_CLOCK_SKEW_MS}. */\n maxClockSkewMs?: number\n /** Defaults to {@link DEFAULT_MAX_EVM_LEAD_BLOCKS}. */\n maxEvmLeadBlocks?: number\n}\n\n/**\n * How a head selector reaches the present: readers rather than values, because a selection pass\n * resolves them once, lazily, and only when a candidate makes the corresponding claim.\n */\nexport interface BlockTimeRuleParams extends BlockTimeRuleOptions {\n /** Reads the observer's EVM head; absent, or answering undefined, skips the anchor upper bound. */\n evmHead?: () => Promisable<number | undefined>\n /** The observer's wall clock. Injectable for tests. */\n now?: () => number\n /** Told once per distinct candidate a rule refused. */\n onRejection?: (rejection: BlockTimeRejection) => void\n}\n\n/** A candidate a time rule refused, with enough context to explain it. */\nexport interface BlockTimeRejection {\n /** The candidate's height. */\n block: number\n /** What specifically was wrong. */\n detail: string\n /** The candidate's hash. */\n hash: Hash\n /** Who produced it. */\n producer?: XyoAddress\n /** Which rule. */\n rule: BlockTimeRuleId\n}\n\ntype RuleFailure = [rule: BlockTimeRuleId, detail: string]\n\n/** The signed time payload a block carries, when it has one. */\nexport function timePayloadOfBlock(block: HydratedBlock): TimePayload | undefined {\n return block[1].find(payload => isTimePayload(payload))\n}\n\n/**\n * The epoch a block's signed time payload claims, in milliseconds.\n *\n * Deliberately no fallback to the unsigned `$epoch` meta (unlike `epochOfBlock` in xl1-sdk, which\n * reads history): everything here gates on the answer, and a gate must not accept a claim the\n * producer never signed.\n */\nexport function signedEpochOfBlock(block: HydratedBlock): number | undefined {\n return timePayloadOfBlock(block)?.epoch\n}\n\n/** The EVM height a block's signed time payload claims, when it claims one. */\nexport function evmAnchorOfBlock(block: HydratedBlock): number | undefined {\n return timePayloadOfBlock(block)?.ethereum\n}\n\n/** The epoch rules: strictly after the parent, and not past what the observer's clock allows. */\nfunction checkEpoch(\n time: TimePayload,\n parentTime: TimePayload | undefined,\n bounds: BlockTimeBounds,\n options: BlockTimeRuleOptions,\n): RuleFailure | undefined {\n if (parentTime !== undefined && time.epoch <= parentTime.epoch) {\n return ['epoch-after-parent', `epoch ${time.epoch} is not after the parent's ${parentTime.epoch}`]\n }\n const maxClockSkewMs = options.maxClockSkewMs ?? DEFAULT_MAX_CLOCK_SKEW_MS\n if (time.epoch > bounds.now + maxClockSkewMs) {\n return [\n 'epoch-not-future',\n `epoch ${time.epoch} is ${time.epoch - bounds.now}ms ahead of this observer's clock (${maxClockSkewMs}ms allowed)`,\n ]\n }\n return undefined\n}\n\n/** The anchor rules, applied only when the candidate claims one: never behind the parent, never past the observer's EVM head. */\nfunction checkAnchor(\n time: TimePayload,\n parentTime: TimePayload | undefined,\n bounds: BlockTimeBounds,\n options: BlockTimeRuleOptions,\n): RuleFailure | undefined {\n const anchor = time.ethereum\n if (anchor === undefined) return undefined\n const parentAnchor = parentTime?.ethereum\n if (parentAnchor !== undefined && anchor < parentAnchor) {\n return ['anchor-monotonic', `anchor went backwards, from ${parentAnchor} to ${anchor}`]\n }\n const maxEvmLeadBlocks = options.maxEvmLeadBlocks ?? DEFAULT_MAX_EVM_LEAD_BLOCKS\n if (bounds.evmHead !== undefined && anchor > bounds.evmHead + maxEvmLeadBlocks) {\n return [\n 'anchor-not-ahead',\n `anchor ${anchor} is ahead of this observer's EVM head ${bounds.evmHead} (${maxEvmLeadBlocks} blocks allowed)`,\n ]\n }\n return undefined\n}\n\n/**\n * Checks a candidate's signed time claims against its parent and against the observer.\n *\n * Every rule reads the candidate's own signed time payload, the one statement about a block's\n * timing its producer cannot alter after signing. The parent-relative rules are deterministic:\n * two finalizers holding the same pair reach the same verdict whenever they ask. The\n * observer-relative rules are not, and are tolerant on purpose. They exist to refuse claims no\n * honest clock or EVM view could have produced, not to arbitrate between two nodes' slightly\n * different ideas of the present. That is also why they belong in head selection rather than in\n * protocol validation: whether a block is valid must not depend on who looked, or when.\n * @param candidate The block under consideration\n * @param parent The block it builds on, when known; absent skips the parent-relative rules\n * @param bounds What the observer knows about the present\n * @param options Tolerances for the observer-relative rules\n * @returns The first rule the candidate failed, or undefined when every rule holds\n */\nexport function evaluateBlockTime(\n candidate: SignedHydratedBlockWithHashMeta,\n parent: HydratedBlock | undefined,\n bounds: BlockTimeBounds,\n options: BlockTimeRuleOptions = {},\n): BlockTimeRejection | undefined {\n const at = {\n block: candidate[0].block, hash: candidate[0]._hash, producer: candidate[0].addresses[0],\n }\n const time = timePayloadOfBlock(candidate)\n if (time === undefined) {\n return {\n ...at, detail: 'carries no signed time payload', rule: 'time-present',\n }\n }\n const parentTime = parent === undefined ? undefined : timePayloadOfBlock(parent)\n const failure = checkEpoch(time, parentTime, bounds, options) ?? checkAnchor(time, parentTime, bounds, options)\n if (failure === undefined) return undefined\n const [rule, detail] = failure\n return {\n ...at, detail, rule,\n }\n}\n", "import type { BaseParams, Promisable } from '@ariestools/sdk'\nimport { Base } from '@ariestools/sdk'\nimport type { Tracer } from '@opentelemetry/api'\nimport type { BlockViewer, SignedHydratedBlockWithHashMeta } from '@xyo-network/xl1-sdk'\nimport { asXL1BlockRange } from '@xyo-network/xl1-sdk'\n\nexport interface ChainFinalizerParams extends BaseParams {\n blockViewer: BlockViewer\n maxWindowSize?: number\n windowedFinalizedChain?: SignedHydratedBlockWithHashMeta[]\n}\n\nexport class ChainFinalizer<TParams extends ChainFinalizerParams = ChainFinalizerParams> extends Base<TParams> {\n protected _tracer: Tracer | undefined\n // the chainArchivist is assumed to be an archivist with already validated blocks in it along with all the transactions and payloads\n // it is also assumed that the sequence order of archivist is that newer blocks come after older blocks\n\n protected windowedFinalizedChain: SignedHydratedBlockWithHashMeta[]\n\n constructor(params: TParams) {\n super(params)\n this.windowedFinalizedChain = [...(params.windowedFinalizedChain ?? [])]\n }\n\n protected get blockViewer() {\n return this.params.blockViewer\n }\n\n protected get finalizedBlockNumber() {\n return this.windowedFinalizedChain?.at(-1)?.[0].block ?? -1\n }\n\n protected get maxWindowSize() {\n return this.params.maxWindowSize ?? 100\n }\n\n protected get windowRange() {\n return asXL1BlockRange([this.windowedFinalizedChain.at(0)?.[0].block ?? 0, this.finalizedBlockNumber])\n }\n\n finalizeChainFragment(chain: SignedHydratedBlockWithHashMeta[]): Promisable<void> {\n this.windowedFinalizedChain = [...this.windowedFinalizedChain, ...chain].slice(-this.maxWindowSize)\n }\n}\n", "import type { Address, Hash } from '@ariestools/sdk'\nimport {\n isDefined, spanRootAsync, toAddress,\n} from '@ariestools/sdk'\nimport type {\n BaseContext, MempoolViewer, SignedHydratedBlockWithHashMeta,\n} from '@xyo-network/xl1-sdk'\nimport {\n findBestUncle, findUncles, getWindowedChain, isTransactionBoundWitness,\n} from '@xyo-network/xl1-sdk'\n\nimport type {\n AdmissionAssessment, AdmissionMode, AdmissionShadowReport, ProducerEligibilityEvaluator,\n} from './admission.ts'\nimport { isDivergent } from './admission.ts'\nimport type {\n BlockTimeBounds, BlockTimeRejection, BlockTimeRuleParams,\n} from './blockTime.ts'\nimport {\n evaluateBlockTime, evmAnchorOfBlock, signedEpochOfBlock,\n} from './blockTime.ts'\nimport type { ChainFinalizerParams } from './ChainFinalizer.ts'\nimport { ChainFinalizer } from './ChainFinalizer.ts'\n\nexport interface ChainHeadSelectorParams extends ChainFinalizerParams {\n /**\n * How candidates are admitted. Defaults to `allowlist`, which is how the chain runs today.\n */\n admission?: AdmissionMode\n /**\n * List of allowed producer addresses, if undefined anyone can participate\n */\n allowedProducers?: Address[]\n /**\n * Back-off timeout (ms) after which a head is accepted even with fewer than\n * minCandidates uncle chains available.\n * When undefined, findBestUncle applies DEFAULT_BACKOFF_MS from the protocol SDK.\n */\n backoffMs?: number\n context: BaseContext\n /**\n * Computes whether a candidate's producer had standing. Absent means the question is never\n * asked, which is what keeps a node with no stake readers running exactly as it does today.\n */\n eligibility?: ProducerEligibilityEvaluator\n /**\n * Minimum signed-epoch spacing (ms) between transaction-less heartbeat blocks this selector\n * will qualify \u2014 a defense against producers heartbeating faster than the network's intended\n * pace. Transaction-carrying candidates are exempt. Undefined disables the check.\n */\n heartbeatInterval?: number\n mempoolViewer: MempoolViewer\n /**\n * Minimum number of uncle candidates required before accepting one.\n * When undefined, findBestUncle applies DEFAULT_MIN_CANDIDATES from the protocol SDK.\n */\n minCandidates?: number\n /** Where to publish what the two gates concluded, in shadow posture. */\n publishAdmissionShadow?: (report: AdmissionShadowReport) => Promise<void>\n /**\n * What this selector knows about the present, against which every candidate's signed time\n * claims are bounded. Absent disables the time rules, which is what keeps a selector built\n * without them behaving exactly as it did before they existed.\n */\n timeRules?: BlockTimeRuleParams\n}\n\ninterface UncleSelectionHealth {\n lastWarnedAt: number\n unqualifiedSince?: number\n}\n\nexport class ChainHeadSelector extends ChainFinalizer<ChainHeadSelectorParams> {\n /**\n * How long uncle selection may go without a single qualifying candidate (while candidates are\n * present) before a warning is emitted; also the minimum interval between repeat warnings.\n */\n static readonly SilentUncleRejectionWarnAfterMs = 5000\n\n protected admission: AdmissionMode\n protected allowedProducers?: Address[]\n protected backoffMs?: number\n protected heartbeatInterval?: number\n protected minCandidates?: number\n private _timeSinceLastUncle = Date.now()\n\n constructor(params: ChainHeadSelectorParams) {\n super(params)\n this.admission = params.admission ?? 'allowlist'\n this.allowedProducers = params.allowedProducers\n this.backoffMs = params.backoffMs\n this.heartbeatInterval = params.heartbeatInterval\n this.minCandidates = params.minCandidates\n }\n\n protected get context() {\n return this.params.context\n }\n\n protected get mempoolViewer() {\n return this.params.mempoolViewer\n }\n\n async findBestHead(): Promise<SignedHydratedBlockWithHashMeta[]> {\n return await spanRootAsync('findBestHead', async () => {\n const allProposedBlocks = await this.mempoolViewer.pendingBlocks()\n const allowed = allProposedBlocks.filter(pb => this.isAllowed(pb))\n const proposedBlocks = await this.admit(allProposedBlocks, allowed)\n\n // TODO: Validate proposed blocks\n this.windowedFinalizedChain = await getWindowedChain(this.context, this.blockViewer, this.maxWindowSize, this.windowedFinalizedChain)\n const pacedBlocks = this.filterPacedHeartbeatCandidates(proposedBlocks)\n const uncles = await this.filterTimeValidUncles(findUncles(this.context, this.windowedFinalizedChain, pacedBlocks))\n this.reportSilentUncleRejections(pacedBlocks, uncles.length)\n return findBestUncle(this.windowedFinalizedChain, uncles, { backoffMs: this.backoffMs, minCandidates: this.minCandidates }) ?? this.windowedFinalizedChain\n }, this.context)\n }\n\n /**\n * Applies the configured admission posture.\n *\n * In `shadow` the allow-list still decides \u2014 participation is computed and published beside it,\n * and changes nothing. That is the entire value of the posture: the network keeps behaving\n * exactly as it did while everyone gets to see what the new rule would have done.\n *\n * In `enforce` participation gates, but only when it reached a conclusion. A candidate whose\n * standing cannot be established is admitted, not rejected \u2014 otherwise a finalizer that lost\n * its stake reader would quietly stop accepting anyone's blocks and stall the chain.\n *\n * Assessments publish in both postures. The audit trail must not disappear at the moment\n * admission starts gating \u2014 enforce is when it matters most.\n */\n protected async admit(\n all: SignedHydratedBlockWithHashMeta[],\n allowed: SignedHydratedBlockWithHashMeta[],\n ): Promise<SignedHydratedBlockWithHashMeta[]> {\n if (this.admission === 'allowlist' || this.params.eligibility === undefined) return allowed\n const assessments = await this.assess(all)\n await this.publishShadow(assessments)\n if (this.admission === 'shadow') return allowed\n const ineligible = new Set(assessments\n .filter(assessment => assessment.eligibility.resolvable && assessment.eligibility.eligible !== true)\n .map(assessment => assessment.dataHash))\n return allowed.filter(candidate => !ineligible.has(candidate[0]._dataHash))\n }\n\n /**\n * Runs both gates over every candidate.\n *\n * An evaluator that throws yields an unresolvable assessment rather than an exception: this\n * sits on the head-selection path, and a flaky stake reader must never be able to stop the\n * chain from finalizing.\n */\n protected async assess(all: SignedHydratedBlockWithHashMeta[]): Promise<AdmissionAssessment[]> {\n const evaluate = this.params.eligibility\n if (evaluate === undefined) return []\n const evaluateSafely = async (candidate: SignedHydratedBlockWithHashMeta) => {\n try {\n return await evaluate(candidate)\n } catch {\n return { resolvable: false }\n }\n }\n return await Promise.all(all.map(async candidate => ({\n admittedByAllowList: this.isAllowed(candidate),\n block: candidate[0].block,\n dataHash: candidate[0]._dataHash,\n eligibility: await evaluateSafely(candidate),\n producer: candidate[0].addresses[0],\n })))\n }\n\n /**\n * Defense against unwanted heartbeat rates: a transaction-less (heartbeat) candidate qualifies\n * only when its signed epoch is at least heartbeatInterval after the current head's. Both\n * epochs come from the blocks' signed time payloads \u2014 never from client meta a producer or\n * relay could rewrite after signing \u2014 so every finalizer applies the same pace deterministically\n * regardless of wall clock. A producer heartbeating faster than the network's pace \u2014\n * misconfigured or hostile \u2014 cannot drive the chain's block rate: its candidates never qualify\n * and age out of the pool, while transaction-carrying candidates are exempt and finalize\n * immediately.\n */\n protected filterPacedHeartbeatCandidates(proposedBlocks: SignedHydratedBlockWithHashMeta[]): SignedHydratedBlockWithHashMeta[] {\n const interval = this.heartbeatInterval\n if (interval === undefined || proposedBlocks.length === 0) return proposedBlocks\n const head = this.windowedFinalizedChain.at(-1)\n const headEpoch = head === undefined ? undefined : signedEpochOfBlock(head)\n if (!isDefined(headEpoch)) return proposedBlocks\n const paced = proposedBlocks.filter((candidate) => {\n // a candidate at or below the head is already finalized or a stale sibling; the uncle checks\n // refuse it with a proper reason, and pacing it here would only muddy the count\n if (candidate[0].block <= (head?.[0].block ?? -1)) return true\n if (candidate[1].some(payload => isTransactionBoundWitness(payload))) return true\n const candidateEpoch = signedEpochOfBlock(candidate)\n // a candidate without a signed epoch cannot be paced here; the time rules own that case\n if (!isDefined(candidateEpoch)) return true\n return candidateEpoch - headEpoch >= interval\n })\n if (paced.length < proposedBlocks.length) {\n const pacedOut = proposedBlocks.filter(candidate => !paced.includes(candidate))\n this.context.logger?.debug(\n `findBestHead: paced out ${pacedOut.length} heartbeat candidate(s) younger than ${interval}ms `\n + `since head ${head?.[0].block} (signed epoch ${headEpoch}): `\n + pacedOut.map(candidate => `${candidate[0].block} [${candidate[0]._hash}] signed epoch ${signedEpochOfBlock(candidate)}`).join(', '),\n )\n }\n return paced\n }\n\n /**\n * Refuses candidates whose signed time claims cannot be true.\n *\n * Every rule reads the candidate's own signed time payload \u2014 the one statement about a block's\n * timing its producer cannot alter after signing \u2014 so a producer with a bad clock or a bad EVM\n * view cannot move the chain's clock, and neither can anything between it and this finalizer.\n * A chain is kept up to its first failing block: what stands before the failure still builds on\n * the head and is still worth finalizing.\n */\n protected async filterTimeValidUncles(uncles: SignedHydratedBlockWithHashMeta[][]): Promise<SignedHydratedBlockWithHashMeta[][]> {\n const rules = this.params.timeRules\n if (rules === undefined || uncles.length === 0) return uncles\n const bounds = await this.resolveTimeBounds(rules, uncles)\n return uncles\n .map(uncle => this.timeValidPrefix(uncle, bounds, rules))\n .filter(uncle => uncle.length > 0)\n }\n\n /** Whether the configured allow-list admits a candidate. An empty list admits everyone. */\n protected isAllowed(candidate: SignedHydratedBlockWithHashMeta): boolean {\n const allowedProducers = this.allowedProducers\n return isDefined(allowedProducers) && allowedProducers.length > 0\n ? allowedProducers.includes(toAddress(candidate[0].addresses[0]))\n : true\n }\n\n /** Publishes what the two gates concluded, swallowing failures. */\n protected async publishShadow(assessments: AdmissionAssessment[]): Promise<void> {\n const publish = this.params.publishAdmissionShadow\n // an empty round says nothing worth a write\n if (publish === undefined || assessments.length === 0) return\n try {\n await publish({\n assessments,\n divergent: assessments.filter(assessment => isDivergent(assessment)),\n head: this.windowedFinalizedChain[0]?.[0]?.block ?? 0,\n })\n } catch {\n // observing must never be able to stop the chain from finalizing\n }\n }\n\n /**\n * Surfaces the otherwise-silent case where candidates are continuously present but none\n * qualifies as an uncle \u2014 without this, the only signal of a stalled selector is an endless\n * \"No new blocks found N to N\" while rejected candidates cycle through the pool. Zero-qualify\n * moments are routine in healthy operation (a just-finalized candidate lingers until the pool\n * GC), so this warns only after candidates have been failing continuously for\n * SilentUncleRejectionWarnAfterMs \u2014 an idle (candidate-free) tick resets the clock \u2014 then\n * rate-limits to one warn per that interval. State lives in context.singletons because a\n * fresh ChainHeadSelector is constructed on every finalization tick.\n */\n private reportSilentUncleRejections(proposedBlocks: SignedHydratedBlockWithHashMeta[], uncleCount: number): void {\n const singletons = this.context.singletons\n const stateKey = 'ChainHeadSelector:uncleSelectionHealth'\n const now = Date.now()\n const state = (singletons[stateKey] ?? { lastWarnedAt: 0 }) as UncleSelectionHealth\n singletons[stateKey] = state\n if (uncleCount > 0 || proposedBlocks.length === 0) {\n state.unqualifiedSince = undefined\n return\n }\n state.unqualifiedSince ??= now\n if (now - state.unqualifiedSince < ChainHeadSelector.SilentUncleRejectionWarnAfterMs) return\n if (now - state.lastWarnedAt < ChainHeadSelector.SilentUncleRejectionWarnAfterMs) return\n state.lastWarnedAt = now\n this.context.logger?.warn(\n `findBestHead: candidates have failed uncle qualification continuously for ${now - state.unqualifiedSince}ms \u2014 `\n + `${proposedBlocks.length} pending candidate(s): `\n + proposedBlocks.map(b => `${b[0].block} [${b[0]._hash}]`).join(', '),\n )\n }\n\n /**\n * Says, once per candidate, which rule refused it.\n *\n * A refused candidate lingers in the pool until it ages out and a fresh selector runs every\n * tick, so the memory of what was already reported lives in context.singletons alongside the\n * other per-pool state; repeating the warning every half second would bury the one line that\n * explains a stalled producer.\n */\n private reportTimeRejection(rejection: BlockTimeRejection, rules: BlockTimeRuleParams): void {\n const singletons = this.context.singletons\n const stateKey = 'ChainHeadSelector:timeRejectionsReported'\n const reported = (singletons[stateKey] ?? new Set<Hash>()) as Set<Hash>\n singletons[stateKey] = reported\n if (reported.has(rejection.hash)) return\n if (reported.size >= 10_000) reported.clear()\n reported.add(rejection.hash)\n this.context.logger?.warn(\n `findBestHead: candidate ${rejection.block} [${rejection.hash}] refused by '${rejection.rule}': ${rejection.detail}`,\n )\n rules.onRejection?.(rejection)\n }\n\n /**\n * The present, as this selector sees it, resolved once per pass.\n *\n * The EVM head is read only when some candidate actually claims an anchor: it is the one rule\n * input that costs a network call, and a chain running without anchors should not pay for it.\n * An unreadable EVM yields no bound rather than a failure \u2014 that is a fact about this observer,\n * and it must never stop the chain from finalizing.\n */\n private async resolveTimeBounds(rules: BlockTimeRuleParams, uncles: SignedHydratedBlockWithHashMeta[][]): Promise<BlockTimeBounds> {\n const now = rules.now?.() ?? Date.now()\n const anchored = uncles.some(uncle => uncle.some(candidate => evmAnchorOfBlock(candidate) !== undefined))\n if (!anchored || rules.evmHead === undefined) return { now }\n try {\n return { evmHead: await rules.evmHead(), now }\n } catch (error) {\n this.context.logger?.warn(`findBestHead: could not read the EVM head; candidate anchors are unbounded this pass: ${String(error)}`)\n return { now }\n }\n }\n\n /** The longest prefix of an uncle chain in which every block's time claims hold against its parent. */\n private timeValidPrefix(\n uncle: SignedHydratedBlockWithHashMeta[],\n bounds: BlockTimeBounds,\n rules: BlockTimeRuleParams,\n ): SignedHydratedBlockWithHashMeta[] {\n const prefix: SignedHydratedBlockWithHashMeta[] = []\n let parent = this.windowedFinalizedChain.at(-1)\n for (const candidate of uncle) {\n const rejection = evaluateBlockTime(candidate, parent, bounds, rules)\n if (rejection !== undefined) {\n this.reportTimeRejection(rejection, rules)\n break\n }\n prefix.push(candidate)\n parent = candidate\n }\n return prefix\n }\n}\n\n/*\n\nComparing two heads for which is better\n\nProducer Energy\n\n1. +1 Point for every block not produced by it in recent chain LOG(active producers * 100)\n2a. Energy Multiplied by - # of transactions in block is above normal (last 1000 blocks) (+0.5 per std deviation) (max 2.5)\n2b. Energy Divided by - # of transactions in block is below normal (last 1000 blocks) (+0.5 per std deviation) (max 2.5)\n3a. Energy Multiplied by - # of gas in block is above normal (last 1000 blocks) (+0.5 per std deviation) (max 2.5)\n3b. Energy Divided by - # of gas in block is below normal (last 1000 blocks) (+0.5 per std deviation) (max 2.5)\n\nOther Ideas\n\n1. Total diverse addresses in block (not quite incentivizing what we really want)\n2. Total transactions in block (more is better, encourages non-lethargic blocks)\n3. Sum total of transactions in block (higher value means more influential/better)\n4. Sum of gas of transactions in block (higher is better, prioritizes paying contributors)\n5. Exp of transactions in block (lower is better, prioritizes not aging out before inclusion)\n6. Recency of this producer's last produced block (encourages diversification in block producers)\n\n*/\n", "import type { Promisable } from '@ariestools/sdk'\nimport { isDefined, toAddress } from '@ariestools/sdk'\nimport { type ChainAnalyzer, type HydratedBlock } from '@xyo-network/xl1-sdk'\n\nimport type { AnalyzedProducer, ChainSummaryProducers } from './ChainSummaryProducers.ts'\nimport { ChainSummaryProducersSchema } from './ChainSummaryProducers.ts'\n\nexport class ChainProducersAnalyzer implements ChainAnalyzer<ChainSummaryProducers> {\n private endingBlock = 0\n private producers: Record<string, AnalyzedProducer> = {}\n private startingBlock = 0\n\n onBlock([block]: HydratedBlock): Promisable<boolean> {\n // avoid analyzing the same block twice\n if (block.block === this.endingBlock) return true\n\n // update the ending block\n this.endingBlock = block.block\n const blockProducerAddressRaw = block.addresses[0]\n\n if (blockProducerAddressRaw === undefined) return true\n\n const blockProducerAddress = toAddress(blockProducerAddressRaw)\n\n const producerKey = String(blockProducerAddress)\n const existingProducer: AnalyzedProducer | undefined = this.producers[producerKey]\n if (isDefined(existingProducer)) {\n existingProducer.rewardsEarned = (existingProducer.rewardsEarned ?? 0n) + 0n\n existingProducer.blocksProduced++\n } else {\n const newProducer: AnalyzedProducer = {\n address: blockProducerAddress, rewardsEarned: 0n, blocksProduced: 1,\n }\n this.producers[producerKey] = newProducer\n }\n\n return true\n }\n\n result(): ChainSummaryProducers {\n return {\n endingBlock: this.endingBlock,\n producers: this.producers,\n schema: ChainSummaryProducersSchema,\n startingBlock: this.startingBlock,\n }\n }\n\n shouldContinue(): boolean {\n return true\n }\n}\n", "import type { Address } from '@ariestools/sdk'\nimport type { Payload } from '@xyo-network/sdk'\nimport { asSchema, isPayloadOfSchemaType } from '@xyo-network/sdk'\n\nexport const ChainSummaryProducersSchema = asSchema('network.xyo.chain.summary.producers', true)\nexport type ChainSummaryProducersSchema = typeof ChainSummaryProducersSchema\n\nexport interface AnalyzedProducer {\n address: Address\n blocksProduced: number\n rewardsEarned: bigint\n}\n\nexport interface ChainSummaryProducersFields {\n endingBlock: number\n producers: Record<string, AnalyzedProducer>\n startingBlock: number\n}\n\nexport type ChainSummaryProducers = Payload<ChainSummaryProducersFields, ChainSummaryProducersSchema>\nexport const isChainSummaryProducers = isPayloadOfSchemaType<ChainSummaryProducers>(ChainSummaryProducersSchema)\n", "import type { Promisable } from '@ariestools/sdk'\nimport { assertEx } from '@ariestools/sdk'\nimport {\n type ChainAnalyzer, type ChainStakeIntent, type HydratedBlock,\n type Intent,\n isChainStakeIntentWithStorageMeta,\n} from '@xyo-network/xl1-sdk'\n\nimport type { ChainSummaryStakeIntent } from './ChainSummaryStakeIntent.ts'\nimport { ChainSummaryStakeIntentSchema } from './ChainSummaryStakeIntent.ts'\n\nexport class ChainStakeIntentAnalyzer implements ChainAnalyzer<ChainSummaryStakeIntent> {\n private _endingBlock: number | undefined = undefined\n private _intent: Intent\n private _intentPayloads: ChainStakeIntent[] = []\n private _startingBlock: number | undefined = undefined\n\n constructor(intent: Intent) {\n this._intent = intent\n }\n\n onBlock([block, validatedPayloads]: HydratedBlock): Promisable<boolean> {\n this._startingBlock ??= block.block\n\n // Update the ending block\n this._endingBlock = block.block\n const intents = validatedPayloads.filter(isChainStakeIntentWithStorageMeta)\n const desiredIntents = intents.filter(intent => intent.intent === this._intent)\n this._intentPayloads.push(...desiredIntents)\n return true\n }\n\n result(): ChainSummaryStakeIntent {\n assertEx(this._startingBlock !== undefined, () => 'ChainStakeIntentAnalyzer: Invalid startingBlock')\n assertEx(this._endingBlock !== undefined, () => 'ChainStakeIntentAnalyzer: Invalid endingBlock')\n return {\n endingBlock: this._endingBlock!,\n intents: this._intentPayloads,\n schema: ChainSummaryStakeIntentSchema,\n startingBlock: this._startingBlock!,\n }\n }\n\n shouldContinue(): boolean {\n return true\n }\n}\n", "import type { Payload } from '@xyo-network/sdk'\nimport { asSchema, isPayloadOfSchemaType } from '@xyo-network/sdk'\nimport type { ChainStakeIntent } from '@xyo-network/xl1-sdk'\n\nexport const ChainSummaryStakeIntentSchema = asSchema('network.xyo.chain.summary.staked.intent', true)\nexport type ChainSummaryStakeIntentSchema = typeof ChainSummaryStakeIntentSchema\n\ninterface ChainSummaryStakeIntentFields {\n endingBlock: number\n intents: ChainStakeIntent[]\n startingBlock: number\n}\n\nexport type ChainSummaryStakeIntent = Payload<ChainSummaryStakeIntentFields, ChainSummaryStakeIntentSchema>\nexport const isChainSummaryStakeIntent = isPayloadOfSchemaType<ChainSummaryStakeIntent>(ChainSummaryStakeIntentSchema)\n", "import {\n type Address, type Hash, type Hex,\n} from '@ariestools/sdk'\nimport type { Payload } from '@xyo-network/sdk'\nimport { asSchema, isPayloadOfSchemaType } from '@xyo-network/sdk'\nimport type { SignedBigInt } from '@xyo-network/xl1-sdk'\n\nexport const ChainSummaryTransfersSchema = asSchema('network.xyo.chain.summary.transfers', true)\nexport type ChainSummaryTransfersSchema = typeof ChainSummaryTransfersSchema\n\nexport interface ChainSummaryTransfersFields {\n firstBlock: Hash | null\n hasMinTransfer?: Record<Address, Hex>\n lastBlock: Hash | null\n transfers: Record<Address, Record<Address, SignedBigInt>>\n}\n\nexport type ChainSummaryTransfers = Payload<ChainSummaryTransfersFields, ChainSummaryTransfersSchema>\nexport const isChainSummaryTransfers = isPayloadOfSchemaType<ChainSummaryTransfers>(ChainSummaryTransfersSchema)\n", "import type { Address, Hash } from '@ariestools/sdk'\nimport { isDefined, toAddress } from '@ariestools/sdk'\nimport { trace, type Tracer } from '@opentelemetry/api'\nimport { PayloadBuilder } from '@xyo-network/sdk'\nimport type {\n ChainAnalyzer,\n HydratedBlockWithHashMeta, SignedBigInt,\n} from '@xyo-network/xl1-sdk'\nimport {\n netTransfersForPayloads,\n parseSignedBigInt, toSignedBigInt,\n} from '@xyo-network/xl1-sdk'\n\nimport type { ChainSummaryTransfers } from './ChainSummaryTransfers.ts'\nimport { ChainSummaryTransfersSchema } from './ChainSummaryTransfers.ts'\n\nconst bigintTransfers = (transfers: Record<Address, Record<Address, SignedBigInt>>) => {\n const result: Record<string, Record<string, bigint>> = {}\n for (const [from, toMap] of Object.entries(transfers)) {\n const fromKey = String(toAddress(from))\n for (const [to, balance] of Object.entries(toMap)) {\n const toKey = String(toAddress(to))\n if (isDefined(balance)) {\n const fromTransfers = result[fromKey] ?? {}\n result[fromKey] = fromTransfers\n fromTransfers[toKey] = parseSignedBigInt(balance)\n }\n }\n }\n return result\n}\n\nexport class TransferAnalyzer implements ChainAnalyzer {\n protected _tracer: Tracer | undefined\n private _firstBlock?: Hash\n private _lastBlock?: Hash\n private _transfers: Record<string, Record<string, bigint>> = {}\n\n constructor(startingTransfers?: ChainSummaryTransfers) {\n this._transfers = startingTransfers ? bigintTransfers(startingTransfers.transfers) : {}\n }\n\n protected get tracer() {\n if (this._tracer) return this._tracer\n this._tracer = trace.getTracer('TransferAnalyzer')\n return this._tracer\n }\n\n onBlock([block, payloads]: HydratedBlockWithHashMeta): boolean {\n this._firstBlock = this._firstBlock ?? block._hash\n this._lastBlock = block._hash\n const netTransfers = netTransfersForPayloads({ singletons: {} }, payloads)\n for (const [from, toMap] of Object.entries(netTransfers)) {\n const fromKey = String(toAddress(from))\n const fromTransfers = this._transfers[fromKey] ?? {}\n this._transfers[fromKey] = fromTransfers\n for (const [to, balance] of Object.entries(toMap)) {\n const toKey = String(toAddress(to))\n fromTransfers[toKey] = (fromTransfers[toKey] ?? 0n) + balance\n }\n }\n return this.shouldContinue()\n }\n\n result() {\n const transfers: Record<string, Record<string, SignedBigInt>> = {}\n for (const [from, toMap] of Object.entries(this._transfers)) {\n const fromKey = String(toAddress(from))\n const positiveTransfers = Object.entries(toMap).filter(([, balance]) => balance > 0n)\n for (const [to, balance] of positiveTransfers) {\n const fromTransfers = transfers[fromKey] ?? {}\n transfers[fromKey] = fromTransfers\n const toKey = String(toAddress(to))\n fromTransfers[toKey] = toSignedBigInt(balance)\n }\n }\n const builder = new PayloadBuilder<ChainSummaryTransfers>({ schema: ChainSummaryTransfersSchema })\n return builder.fields({\n firstBlock: this._firstBlock ?? null,\n lastBlock: this._lastBlock ?? null,\n transfers,\n }).build()\n }\n\n shouldContinue() {\n return true\n }\n}\n"],
5
+ "mappings": "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",
6
6
  "names": ["isDefined", "spanRootAsync", "toAddress", "spanRootAsync", "isDefined", "toAddress", "isDefined", "toAddress", "asSchema", "isPayloadOfSchemaType", "toAddress", "isDefined", "assertEx", "asSchema", "isPayloadOfSchemaType", "assertEx", "asSchema", "isPayloadOfSchemaType", "isDefined", "toAddress", "trace", "PayloadBuilder", "parseSignedBigInt", "toSignedBigInt", "toAddress", "isDefined", "parseSignedBigInt", "trace", "toSignedBigInt", "PayloadBuilder"]
7
7
  }
@@ -1,6 +1,7 @@
1
1
  import type { Address } from '@ariestools/sdk';
2
2
  import type { BaseContext, MempoolViewer, SignedHydratedBlockWithHashMeta } from '@xyo-network/xl1-sdk';
3
3
  import type { AdmissionAssessment, AdmissionMode, AdmissionShadowReport, ProducerEligibilityEvaluator } from './admission.js';
4
+ import type { BlockTimeRuleParams } from './blockTime.js';
4
5
  import type { ChainFinalizerParams } from './ChainFinalizer.js';
5
6
  import { ChainFinalizer } from './ChainFinalizer.js';
6
7
  export interface ChainHeadSelectorParams extends ChainFinalizerParams {
@@ -25,9 +26,9 @@ export interface ChainHeadSelectorParams extends ChainFinalizerParams {
25
26
  */
26
27
  eligibility?: ProducerEligibilityEvaluator;
27
28
  /**
28
- * Minimum epoch spacing (ms) between transaction-less heartbeat blocks this selector will
29
- * qualify — a defense against producers heartbeating faster than the network's intended pace.
30
- * Transaction-carrying candidates are exempt. Undefined disables the check.
29
+ * Minimum signed-epoch spacing (ms) between transaction-less heartbeat blocks this selector
30
+ * will qualify — a defense against producers heartbeating faster than the network's intended
31
+ * pace. Transaction-carrying candidates are exempt. Undefined disables the check.
31
32
  */
32
33
  heartbeatInterval?: number;
33
34
  mempoolViewer: MempoolViewer;
@@ -38,6 +39,12 @@ export interface ChainHeadSelectorParams extends ChainFinalizerParams {
38
39
  minCandidates?: number;
39
40
  /** Where to publish what the two gates concluded, in shadow posture. */
40
41
  publishAdmissionShadow?: (report: AdmissionShadowReport) => Promise<void>;
42
+ /**
43
+ * What this selector knows about the present, against which every candidate's signed time
44
+ * claims are bounded. Absent disables the time rules, which is what keeps a selector built
45
+ * without them behaving exactly as it did before they existed.
46
+ */
47
+ timeRules?: BlockTimeRuleParams;
41
48
  }
42
49
  export declare class ChainHeadSelector extends ChainFinalizer<ChainHeadSelectorParams> {
43
50
  /**
@@ -87,13 +94,25 @@ export declare class ChainHeadSelector extends ChainFinalizer<ChainHeadSelectorP
87
94
  protected assess(all: SignedHydratedBlockWithHashMeta[]): Promise<AdmissionAssessment[]>;
88
95
  /**
89
96
  * Defense against unwanted heartbeat rates: a transaction-less (heartbeat) candidate qualifies
90
- * only when its signing epoch is at least heartbeatInterval after the current head's. Measured
91
- * on chain data, so every finalizer applies the same pace deterministically regardless of wall
92
- * clock. A producer heartbeating faster than the network's pace — misconfigured or hostile —
93
- * cannot drive the chain's block rate: its candidates never qualify and age out of the pool,
94
- * while transaction-carrying candidates are exempt and finalize immediately.
97
+ * only when its signed epoch is at least heartbeatInterval after the current head's. Both
98
+ * epochs come from the blocks' signed time payloads — never from client meta a producer or
99
+ * relay could rewrite after signing — so every finalizer applies the same pace deterministically
100
+ * regardless of wall clock. A producer heartbeating faster than the network's pace —
101
+ * misconfigured or hostile — cannot drive the chain's block rate: its candidates never qualify
102
+ * and age out of the pool, while transaction-carrying candidates are exempt and finalize
103
+ * immediately.
95
104
  */
96
105
  protected filterPacedHeartbeatCandidates(proposedBlocks: SignedHydratedBlockWithHashMeta[]): SignedHydratedBlockWithHashMeta[];
106
+ /**
107
+ * Refuses candidates whose signed time claims cannot be true.
108
+ *
109
+ * Every rule reads the candidate's own signed time payload — the one statement about a block's
110
+ * timing its producer cannot alter after signing — so a producer with a bad clock or a bad EVM
111
+ * view cannot move the chain's clock, and neither can anything between it and this finalizer.
112
+ * A chain is kept up to its first failing block: what stands before the failure still builds on
113
+ * the head and is still worth finalizing.
114
+ */
115
+ protected filterTimeValidUncles(uncles: SignedHydratedBlockWithHashMeta[][]): Promise<SignedHydratedBlockWithHashMeta[][]>;
97
116
  /** Whether the configured allow-list admits a candidate. An empty list admits everyone. */
98
117
  protected isAllowed(candidate: SignedHydratedBlockWithHashMeta): boolean;
99
118
  /** Publishes what the two gates concluded, swallowing failures. */
@@ -109,5 +128,25 @@ export declare class ChainHeadSelector extends ChainFinalizer<ChainHeadSelectorP
109
128
  * fresh ChainHeadSelector is constructed on every finalization tick.
110
129
  */
111
130
  private reportSilentUncleRejections;
131
+ /**
132
+ * Says, once per candidate, which rule refused it.
133
+ *
134
+ * A refused candidate lingers in the pool until it ages out and a fresh selector runs every
135
+ * tick, so the memory of what was already reported lives in context.singletons alongside the
136
+ * other per-pool state; repeating the warning every half second would bury the one line that
137
+ * explains a stalled producer.
138
+ */
139
+ private reportTimeRejection;
140
+ /**
141
+ * The present, as this selector sees it, resolved once per pass.
142
+ *
143
+ * The EVM head is read only when some candidate actually claims an anchor: it is the one rule
144
+ * input that costs a network call, and a chain running without anchors should not pay for it.
145
+ * An unreadable EVM yields no bound rather than a failure — that is a fact about this observer,
146
+ * and it must never stop the chain from finalizing.
147
+ */
148
+ private resolveTimeBounds;
149
+ /** The longest prefix of an uncle chain in which every block's time claims hold against its parent. */
150
+ private timeValidPrefix;
112
151
  }
113
152
  //# sourceMappingURL=ChainHeadSelector.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"ChainHeadSelector.d.ts","sourceRoot":"","sources":["../../../../src/modules/analyze/ChainHeadSelector.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,iBAAiB,CAAA;AAI9C,OAAO,KAAK,EACV,WAAW,EAAE,aAAa,EAAE,+BAA+B,EAC5D,MAAM,sBAAsB,CAAA;AAK7B,OAAO,KAAK,EACV,mBAAmB,EAAE,aAAa,EAAE,qBAAqB,EAAE,4BAA4B,EACxF,MAAM,gBAAgB,CAAA;AAEvB,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,qBAAqB,CAAA;AAC/D,OAAO,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAA;AAEpD,MAAM,WAAW,uBAAwB,SAAQ,oBAAoB;IACnE;;OAEG;IACH,SAAS,CAAC,EAAE,aAAa,CAAA;IACzB;;OAEG;IACH,gBAAgB,CAAC,EAAE,OAAO,EAAE,CAAA;IAC5B;;;;OAIG;IACH,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,OAAO,EAAE,WAAW,CAAA;IACpB;;;OAGG;IACH,WAAW,CAAC,EAAE,4BAA4B,CAAA;IAC1C;;;;OAIG;IACH,iBAAiB,CAAC,EAAE,MAAM,CAAA;IAC1B,aAAa,EAAE,aAAa,CAAA;IAC5B;;;OAGG;IACH,aAAa,CAAC,EAAE,MAAM,CAAA;IACtB,wEAAwE;IACxE,sBAAsB,CAAC,EAAE,CAAC,MAAM,EAAE,qBAAqB,KAAK,OAAO,CAAC,IAAI,CAAC,CAAA;CAC1E;AAOD,qBAAa,iBAAkB,SAAQ,cAAc,CAAC,uBAAuB,CAAC;IAC5E;;;OAGG;IACH,MAAM,CAAC,QAAQ,CAAC,+BAA+B,QAAO;IAEtD,SAAS,CAAC,SAAS,EAAE,aAAa,CAAA;IAClC,SAAS,CAAC,gBAAgB,CAAC,EAAE,OAAO,EAAE,CAAA;IACtC,SAAS,CAAC,SAAS,CAAC,EAAE,MAAM,CAAA;IAC5B,SAAS,CAAC,iBAAiB,CAAC,EAAE,MAAM,CAAA;IACpC,SAAS,CAAC,aAAa,CAAC,EAAE,MAAM,CAAA;IAChC,OAAO,CAAC,mBAAmB,CAAa;gBAE5B,MAAM,EAAE,uBAAuB;IAS3C,SAAS,KAAK,OAAO;;;;;;;MAEpB;IAED,SAAS,KAAK,aAAa,kBAE1B;IAEK,YAAY,IAAI,OAAO,CAAC,+BAA+B,EAAE,CAAC;IAehE;;;;;;;;;;;;;OAaG;cACa,KAAK,CACnB,GAAG,EAAE,+BAA+B,EAAE,EACtC,OAAO,EAAE,+BAA+B,EAAE,GACzC,OAAO,CAAC,+BAA+B,EAAE,CAAC;IAW7C;;;;;;OAMG;cACa,MAAM,CAAC,GAAG,EAAE,+BAA+B,EAAE,GAAG,OAAO,CAAC,mBAAmB,EAAE,CAAC;IAmB9F;;;;;;;OAOG;IACH,SAAS,CAAC,8BAA8B,CAAC,cAAc,EAAE,+BAA+B,EAAE,GAAG,+BAA+B,EAAE;IAqB9H,2FAA2F;IAC3F,SAAS,CAAC,SAAS,CAAC,SAAS,EAAE,+BAA+B,GAAG,OAAO;IAOxE,mEAAmE;cACnD,aAAa,CAAC,WAAW,EAAE,mBAAmB,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC;IAehF;;;;;;;;;OASG;IACH,OAAO,CAAC,2BAA2B;CAoBpC"}
1
+ {"version":3,"file":"ChainHeadSelector.d.ts","sourceRoot":"","sources":["../../../../src/modules/analyze/ChainHeadSelector.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAQ,MAAM,iBAAiB,CAAA;AAIpD,OAAO,KAAK,EACV,WAAW,EAAE,aAAa,EAAE,+BAA+B,EAC5D,MAAM,sBAAsB,CAAA;AAK7B,OAAO,KAAK,EACV,mBAAmB,EAAE,aAAa,EAAE,qBAAqB,EAAE,4BAA4B,EACxF,MAAM,gBAAgB,CAAA;AAEvB,OAAO,KAAK,EAC2B,mBAAmB,EACzD,MAAM,gBAAgB,CAAA;AAIvB,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,qBAAqB,CAAA;AAC/D,OAAO,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAA;AAEpD,MAAM,WAAW,uBAAwB,SAAQ,oBAAoB;IACnE;;OAEG;IACH,SAAS,CAAC,EAAE,aAAa,CAAA;IACzB;;OAEG;IACH,gBAAgB,CAAC,EAAE,OAAO,EAAE,CAAA;IAC5B;;;;OAIG;IACH,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,OAAO,EAAE,WAAW,CAAA;IACpB;;;OAGG;IACH,WAAW,CAAC,EAAE,4BAA4B,CAAA;IAC1C;;;;OAIG;IACH,iBAAiB,CAAC,EAAE,MAAM,CAAA;IAC1B,aAAa,EAAE,aAAa,CAAA;IAC5B;;;OAGG;IACH,aAAa,CAAC,EAAE,MAAM,CAAA;IACtB,wEAAwE;IACxE,sBAAsB,CAAC,EAAE,CAAC,MAAM,EAAE,qBAAqB,KAAK,OAAO,CAAC,IAAI,CAAC,CAAA;IACzE;;;;OAIG;IACH,SAAS,CAAC,EAAE,mBAAmB,CAAA;CAChC;AAOD,qBAAa,iBAAkB,SAAQ,cAAc,CAAC,uBAAuB,CAAC;IAC5E;;;OAGG;IACH,MAAM,CAAC,QAAQ,CAAC,+BAA+B,QAAO;IAEtD,SAAS,CAAC,SAAS,EAAE,aAAa,CAAA;IAClC,SAAS,CAAC,gBAAgB,CAAC,EAAE,OAAO,EAAE,CAAA;IACtC,SAAS,CAAC,SAAS,CAAC,EAAE,MAAM,CAAA;IAC5B,SAAS,CAAC,iBAAiB,CAAC,EAAE,MAAM,CAAA;IACpC,SAAS,CAAC,aAAa,CAAC,EAAE,MAAM,CAAA;IAChC,OAAO,CAAC,mBAAmB,CAAa;gBAE5B,MAAM,EAAE,uBAAuB;IAS3C,SAAS,KAAK,OAAO;;;;;;;MAEpB;IAED,SAAS,KAAK,aAAa,kBAE1B;IAEK,YAAY,IAAI,OAAO,CAAC,+BAA+B,EAAE,CAAC;IAehE;;;;;;;;;;;;;OAaG;cACa,KAAK,CACnB,GAAG,EAAE,+BAA+B,EAAE,EACtC,OAAO,EAAE,+BAA+B,EAAE,GACzC,OAAO,CAAC,+BAA+B,EAAE,CAAC;IAW7C;;;;;;OAMG;cACa,MAAM,CAAC,GAAG,EAAE,+BAA+B,EAAE,GAAG,OAAO,CAAC,mBAAmB,EAAE,CAAC;IAmB9F;;;;;;;;;OASG;IACH,SAAS,CAAC,8BAA8B,CAAC,cAAc,EAAE,+BAA+B,EAAE,GAAG,+BAA+B,EAAE;IA2B9H;;;;;;;;OAQG;cACa,qBAAqB,CAAC,MAAM,EAAE,+BAA+B,EAAE,EAAE,GAAG,OAAO,CAAC,+BAA+B,EAAE,EAAE,CAAC;IAShI,2FAA2F;IAC3F,SAAS,CAAC,SAAS,CAAC,SAAS,EAAE,+BAA+B,GAAG,OAAO;IAOxE,mEAAmE;cACnD,aAAa,CAAC,WAAW,EAAE,mBAAmB,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC;IAehF;;;;;;;;;OASG;IACH,OAAO,CAAC,2BAA2B;IAqBnC;;;;;;;OAOG;IACH,OAAO,CAAC,mBAAmB;IAc3B;;;;;;;OAOG;YACW,iBAAiB;IAY/B,uGAAuG;IACvG,OAAO,CAAC,eAAe;CAkBxB"}