@xyo-network/xl1-cli 1.22.0 → 1.23.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/cli-min.mjs CHANGED
@@ -53500,7 +53500,7 @@ function isTransferDescriptor(thing) {
53500
53500
  // src/master/invocation-proxy.ts
53501
53501
  var debugMessages = debug$3("threads:master:messages");
53502
53502
  var nextJobUID = 1;
53503
- var dedupe = (array) => [...new Set(array)];
53503
+ var dedupe$1 = (array) => [...new Set(array)];
53504
53504
  var isJobErrorMessage = (data) => data?.type === "error" /* error */;
53505
53505
  var isJobResultMessage = (data) => data?.type === "result" /* result */;
53506
53506
  var isJobStartMessage = (data) => data?.type === "running" /* running */;
@@ -53570,7 +53570,7 @@ function prepareArguments(rawArgs) {
53570
53570
  }
53571
53571
  return {
53572
53572
  args,
53573
- transferables: transferables.length === 0 ? transferables : dedupe(transferables)
53573
+ transferables: transferables.length === 0 ? transferables : dedupe$1(transferables)
53574
53574
  };
53575
53575
  }
53576
53576
  function createProxyFunction(worker, method) {
@@ -73059,6 +73059,7 @@ UnsignedTransactionBoundWitnessZod.safeExtend(StorageMetaZod.shape);
73059
73059
  var SignedTransactionBoundWitnessZod = SignedBoundWitnessZod.safeExtend(TransactionBoundWitnessFieldsZod.shape);
73060
73060
  var asSignedTransactionBoundWitness = zodAsFactory(SignedTransactionBoundWitnessZod, "asSignedTransactionBoundWitness");
73061
73061
  var SignedTransactionBoundWitnessWithHashMetaZod = WithHashMetaZod(SignedTransactionBoundWitnessZod);
73062
+ var isSignedTransactionBoundWitnessWithHashMeta = zodIsFactory(SignedTransactionBoundWitnessWithHashMetaZod);
73062
73063
  var asSignedTransactionBoundWitnessWithHashMeta = zodAsFactory(
73063
73064
  SignedTransactionBoundWitnessWithHashMetaZod,
73064
73065
  "asSignedTransactionBoundWitnessWithHashMeta"
@@ -73353,7 +73354,7 @@ var defaultRewardRatio = 0.05;
73353
73354
  var minTransactionFees = {
73354
73355
  base: AttoXL1(1000n * AttoXL1ConvertFactor.nano),
73355
73356
  gasPrice: AttoXL1(10n * AttoXL1ConvertFactor.nano),
73356
- gasLimit: AttoXL1(1000000n * AttoXL1ConvertFactor.nano),
73357
+ gasLimit: AttoXL1(10n * AttoXL1ConvertFactor.nano),
73357
73358
  priority: AttoXL1(0n * AttoXL1ConvertFactor.nano)
73358
73359
  };
73359
73360
 
@@ -73361,7 +73362,7 @@ var minTransactionFees = {
73361
73362
  var defaultTransactionFees = {
73362
73363
  base: minTransactionFees.base,
73363
73364
  gasPrice: AttoXL1(10n * AttoXL1ConvertFactor.nano),
73364
- gasLimit: AttoXL1(1000000n * AttoXL1ConvertFactor.nano),
73365
+ gasLimit: AttoXL1(1000n * AttoXL1ConvertFactor.nano),
73365
73366
  priority: minTransactionFees.priority
73366
73367
  };
73367
73368
  var HydratedTransactionZod = tuple([
@@ -73404,7 +73405,7 @@ tuple([
73404
73405
  WithStorageMetaZod(SignedTransactionBoundWitnessZod),
73405
73406
  array$1(WithStorageMetaZod(PayloadZod).loose())
73406
73407
  ]);
73407
- var CaveatTypesZod = _enum$1(["chain", "expiration", "filteredResponse", "rateLimit", "restrictReturnedAccounts"]);
73408
+ var CaveatTypesZod = _enum$1(["chain", "dataLakeAccess", "expiration", "filteredResponse", "rateLimit", "restrictReturnedAccounts"]);
73408
73409
  var CaveatsZod = object$4({
73409
73410
  type: CaveatTypesZod,
73410
73411
  value: json$3()
@@ -73607,13 +73608,16 @@ var HydratedBlockValidationError = class extends ValidationError {
73607
73608
  };
73608
73609
  ValidationErrorZod.extend({
73609
73610
  chainId: HexZod,
73610
- name: literal$2("HydratedBlockStateValidationError")
73611
+ name: literal$2("HydratedBlockStateValidationError"),
73612
+ offendingTransactionHashes: array$1(HashZod).optional()
73611
73613
  }).loose();
73612
73614
  var HydratedBlockStateValidationError = class extends ValidationError {
73613
73615
  chainId;
73614
- constructor(hash, chainId, value, message, cause) {
73616
+ offendingTransactionHashes;
73617
+ constructor(hash, chainId, value, message, cause, offendingTransactionHashes) {
73615
73618
  super(hash, value, message, cause);
73616
73619
  this.chainId = chainId;
73620
+ this.offendingTransactionHashes = offendingTransactionHashes;
73617
73621
  }
73618
73622
  };
73619
73623
 
@@ -74434,7 +74438,7 @@ var RpcRemoteConfigZod = union$1([HttpRpcRemoteConfigZod, PostMessageRpcRemoteCo
74434
74438
  var RemoteConfigZod = object$4({ rpc: RpcRemoteConfigZod.optional() }).describe("Configuration for remote connections, including RPC");
74435
74439
  var hasMongoConfig = (config) => {
74436
74440
  if (isUndefined(config)) return false;
74437
- return isDefined(config.connectionString) && isDefined(config.database) && isDefined(config.domain) && isDefined(config.password) && isDefined(config.username);
74441
+ return isDefined(config.connectionString) && isDefined(config.database) && isDefined(config.domain);
74438
74442
  };
74439
74443
  var MongoConfigZod = object$4({
74440
74444
  // TODO: Create from other arguments
@@ -74833,26 +74837,6 @@ async function externalBlockRangeFromStep(context, blockViewer, stepIdentity) {
74833
74837
  return await externalBlockRangeFromXL1BlockRange(context, blockViewer, xl1BlockRange);
74834
74838
  });
74835
74839
  }
74836
- async function addDataLakePayloadsToPayloads(hashes, payloads, dataLakeViewer) {
74837
- if (isUndefined(dataLakeViewer)) return [payloads, []];
74838
- const missingPayloadHashes = hashes.filter((hash) => !payloads.some((p) => p._hash === hash));
74839
- const payloadsFromDataLake = await PayloadBuilder.addHashMeta(
74840
- await PayloadBuilder.addHashMeta((await dataLakeViewer.get(missingPayloadHashes)).filter(isAnyPayload))
74841
- );
74842
- return [[...payloads, ...payloadsFromDataLake], payloadsFromDataLake.map((p) => p._hash)];
74843
- }
74844
-
74845
- // src/primitives/datalake/addDataLakePayloads.ts
74846
- async function addDataLakePayloads([boundWitness, payloads], dataLakeViewer) {
74847
- const [updatedPayloads, foundHashes] = await addDataLakePayloadsToPayloads(boundWitness.payload_hashes, payloads, dataLakeViewer);
74848
- return [
74849
- [
74850
- boundWitness,
74851
- updatedPayloads
74852
- ],
74853
- foundHashes
74854
- ];
74855
- }
74856
74840
  function mapToMapType(map) {
74857
74841
  return {
74858
74842
  get: (key) => map.get(key),
@@ -75634,6 +75618,7 @@ var registerCreatableProviderFactory = (registry, factory, labels, primary = fal
75634
75618
  const factoryClone = buildProviderFactory(factory, factory.defaultParams, labels);
75635
75619
  registry[factoryClone.defaultMoniker] = [factoryClone, ...registry[factoryClone.defaultMoniker] ?? []];
75636
75620
  for (const moniker of factoryClone.monikers) {
75621
+ if (moniker === factoryClone.defaultMoniker) continue;
75637
75622
  registry[moniker] = isPrimaryForMoniker(moniker) ? [factoryClone, ...registry[moniker] ?? []] : [...registry[moniker] ?? [], factoryClone];
75638
75623
  }
75639
75624
  };
@@ -76249,10 +76234,9 @@ var MIN_HEAD_POLL_INTERVAL_MS$1 = 5e3;
76249
76234
  var SimpleBlockViewer = class extends AbstractCreatableProvider {
76250
76235
  moniker = SimpleBlockViewer.defaultMoniker;
76251
76236
  _store;
76252
- dataLakeViewer;
76253
76237
  finalizationViewer;
76254
76238
  payloadCache = new LruCacheMap({ max: 1e4 });
76255
- signedHydratedBlockWithDataLakePayloadsCache = new LruCacheMap({ max: 2e3, ttl: 1e3 * 60 * 60 });
76239
+ signedHydratedBlockWithHashMetaCache = new LruCacheMap({ max: 2e3, ttl: 1e3 * 60 * 60 });
76256
76240
  _headPollHash;
76257
76241
  _headPollInProgress = false;
76258
76242
  _headPollTimer = null;
@@ -76288,17 +76272,16 @@ var SimpleBlockViewer = class extends AbstractCreatableProvider {
76288
76272
  }
76289
76273
  async blockByHash(hash) {
76290
76274
  return await this.spanAsync("blockByHash", async () => {
76291
- const cachedBlock = this.signedHydratedBlockWithDataLakePayloadsCache.get(hash);
76275
+ const cachedBlock = this.signedHydratedBlockWithHashMetaCache.get(hash);
76292
76276
  if (cachedBlock) {
76293
76277
  return cachedBlock;
76294
76278
  }
76295
76279
  const cache = this.hydratedBlockCache;
76296
76280
  const block = await cache.get(hash);
76297
- const [result] = block ? await addDataLakePayloads(block, this.dataLakeViewer) : [null, []];
76298
- if (result) {
76299
- this.signedHydratedBlockWithDataLakePayloadsCache.set(hash, result);
76281
+ if (block) {
76282
+ this.signedHydratedBlockWithHashMetaCache.set(hash, block);
76300
76283
  }
76301
- return result;
76284
+ return block ?? null;
76302
76285
  }, { ...this.context, timeBudgetLimit: 100 });
76303
76286
  }
76304
76287
  async blockByNumber(blockNumber) {
@@ -76351,13 +76334,11 @@ var SimpleBlockViewer = class extends AbstractCreatableProvider {
76351
76334
  }
76352
76335
  async createHandler() {
76353
76336
  await super.createHandler();
76354
- this.dataLakeViewer = await this.locator.tryGetInstance(DataLakeViewerMoniker);
76355
76337
  this.finalizationViewer = await this.locator.getInstance(FinalizationViewerMoniker);
76356
76338
  this._store = { chainMap: this.params.finalizedArchivist };
76357
76339
  }
76358
76340
  async currentBlock() {
76359
- const [result] = await addDataLakePayloads(await this.finalizationViewer.head(), this.dataLakeViewer);
76360
- return result;
76341
+ return await this.finalizationViewer.head();
76361
76342
  }
76362
76343
  async currentBlockHash() {
76363
76344
  return await this.finalizationViewer.headHash();
@@ -76375,7 +76356,7 @@ var SimpleBlockViewer = class extends AbstractCreatableProvider {
76375
76356
  const cachedHashes = new Set(cachedPayloads.map((p) => p._hash));
76376
76357
  remainingHashes = remainingHashes.filter((h) => !cachedHashes.has(h));
76377
76358
  const finalizedPayloads = remainingHashes.length > 0 ? await this.finalizedArchivist.get(remainingHashes) : [];
76378
- const [resultPayloads] = await addDataLakePayloadsToPayloads(hashes, [...cachedPayloads, ...finalizedPayloads.filter(exists$2)], this.dataLakeViewer);
76359
+ const resultPayloads = [...cachedPayloads, ...finalizedPayloads.filter(exists$2)];
76379
76360
  resultPayloads.map((payload) => {
76380
76361
  this.payloadCache.set(payload._hash, payload);
76381
76362
  });
@@ -76414,9 +76395,7 @@ var SimpleBlockViewer = class extends AbstractCreatableProvider {
76414
76395
  await super.stopHandler();
76415
76396
  }
76416
76397
  async blockByNumberWithContext(chainContext, blockNumber) {
76417
- const block = asSignedHydratedBlockWithHashMeta(await hydratedBlockByNumber(chainContext, blockNumber)) ?? null;
76418
- const [result] = block ? await addDataLakePayloads(block, this.dataLakeViewer) : [null, []];
76419
- return result;
76398
+ return asSignedHydratedBlockWithHashMeta(await hydratedBlockByNumber(chainContext, blockNumber)) ?? null;
76420
76399
  }
76421
76400
  async pollHead(emitOnChange) {
76422
76401
  if (this._headPollInProgress) return;
@@ -76998,15 +76977,25 @@ var SimpleXyoGatewayRunner = class _SimpleXyoGatewayRunner extends AbstractCreat
76998
76977
  const [hash] = await this.addPayloadsToChain([transfer], [], options);
76999
76978
  return hash;
77000
76979
  }
76980
+ transactionRequiredGas(hydratedTransaction) {
76981
+ return transactionRequiredGas(hydratedTransaction);
76982
+ }
77001
76983
  };
77002
76984
  var DEFAULT_SYNC_INTERVAL = 3e4;
77003
76985
  var DEFAULT_SYNC_LIMIT = 100;
76986
+ var ENFORCE_CAP_BATCH_SIZE = 1e3;
76987
+ function isDemotionAware(viewer) {
76988
+ if (!viewer) return false;
76989
+ const candidate = viewer;
76990
+ return typeof candidate.forget === "function" && typeof candidate.getEvictionPriorityOrder === "function";
76991
+ }
77004
76992
  var SimpleMempoolRunner = class extends AbstractCreatableProvider {
77005
76993
  moniker = SimpleMempoolRunner.defaultMoniker;
77006
76994
  _blockValidationViewer;
77007
76995
  _chainContractViewer;
77008
76996
  _deadLetterQueueRunner;
77009
76997
  _finalizationViewer;
76998
+ _mempoolViewer;
77010
76999
  _transactionValidationViewer;
77011
77000
  _syncMutex = new Mutex$1();
77012
77001
  _syncTimerId = null;
@@ -77025,6 +77014,9 @@ var SimpleMempoolRunner = class extends AbstractCreatableProvider {
77025
77014
  get maxExpAhead() {
77026
77015
  return this.params.maxExpAhead ?? DEFAULT_MAX_EXP_AHEAD;
77027
77016
  }
77017
+ get maxPendingTransactions() {
77018
+ return this.params.maxPendingTransactions ?? 0;
77019
+ }
77028
77020
  get pendingBlocksArchivist() {
77029
77021
  return this.params.pendingBlocksArchivist;
77030
77022
  }
@@ -77040,6 +77032,9 @@ var SimpleMempoolRunner = class extends AbstractCreatableProvider {
77040
77032
  get transactionValidationViewer() {
77041
77033
  return this._transactionValidationViewer;
77042
77034
  }
77035
+ get validateOnSubmit() {
77036
+ return this.params.validateOnSubmit ?? true;
77037
+ }
77043
77038
  static async paramsHandler(params) {
77044
77039
  return {
77045
77040
  ...await super.paramsHandler(params),
@@ -77054,6 +77049,7 @@ var SimpleMempoolRunner = class extends AbstractCreatableProvider {
77054
77049
  this._finalizationViewer = await this.locator.getInstance(FinalizationViewerMoniker);
77055
77050
  this._transactionValidationViewer = await this.locator.getInstance(TransactionValidationViewerMoniker);
77056
77051
  this._deadLetterQueueRunner = await this.locator.tryGetInstance(DeadLetterQueueRunnerMoniker);
77052
+ this._mempoolViewer = await this.locator.tryGetInstance(MempoolViewerMoniker);
77057
77053
  }
77058
77054
  async prunePendingBlocks({
77059
77055
  batchSize = 10,
@@ -77168,6 +77164,7 @@ var SimpleMempoolRunner = class extends AbstractCreatableProvider {
77168
77164
  pruned += pruneHashes.length;
77169
77165
  total += batch.length;
77170
77166
  await this.pendingTransactionsArchivist.delete(pruneHashes);
77167
+ this.forgetBundleHashes(pruneHashes);
77171
77168
  const pruneSet = new Set(pruneHashes);
77172
77169
  const lastSurvivor = batch.findLast((p) => !pruneSet.has(p._hash));
77173
77170
  cursor = lastSurvivor?._sequence ?? cursor;
@@ -77177,8 +77174,7 @@ var SimpleMempoolRunner = class extends AbstractCreatableProvider {
77177
77174
  order: "desc"
77178
77175
  });
77179
77176
  }
77180
- this.logger?.debug(`prunePendingTransactions completed: pruned=${pruned}, totalChecked=${total}`);
77181
- return [pruned, total];
77177
+ return this.finalizePruneTransactionsResult(pruned, total);
77182
77178
  }
77183
77179
  async submitBlocks(blocks) {
77184
77180
  const bundles = await Promise.all(blocks.map(async ([bw, payloads]) => {
@@ -77193,20 +77189,38 @@ var SimpleMempoolRunner = class extends AbstractCreatableProvider {
77193
77189
  async submitTransactions(transactions) {
77194
77190
  const headNumber = await this.finalizationViewer.headNumber();
77195
77191
  const maxExp = headNumber + this.maxExpAhead;
77196
- const validTransactions = transactions.filter(([tx]) => {
77192
+ const expValid = transactions.filter(([tx]) => {
77197
77193
  if (tx.exp > maxExp) {
77198
77194
  this.logger?.debug(`Rejecting transaction with exp ${tx.exp} exceeding max allowed ${maxExp}`);
77199
77195
  return false;
77200
77196
  }
77201
77197
  return true;
77202
77198
  });
77203
- const bundles = await Promise.all(validTransactions.map(async ([tx, payloads]) => {
77204
- return hydratedTransactionToPayloadBundle([
77199
+ const hashedTransactions = await Promise.all(
77200
+ expValid.map(async ([tx, payloads]) => [
77205
77201
  await PayloadBuilder.addHashMeta(tx),
77206
77202
  await PayloadBuilder.addHashMeta(payloads)
77207
- ]);
77208
- }));
77203
+ ])
77204
+ );
77205
+ if (this.validateOnSubmit) {
77206
+ const validationResults = await Promise.all(
77207
+ hashedTransactions.map(async (tx) => this.transactionValidationViewer.validateTransaction(tx, { value: true, state: true }))
77208
+ );
77209
+ const failures = [];
77210
+ for (const [i, result] of validationResults.entries()) {
77211
+ if (!isSignedHydratedTransactionWithHashMeta(result)) {
77212
+ failures.push({ tx: hashedTransactions[i], errors: result });
77213
+ }
77214
+ }
77215
+ if (failures.length > 0) {
77216
+ await Promise.all(failures.map((f) => this.routeRejectedTransaction(f.tx, f.errors)));
77217
+ const detail = failures.map((f) => `${f.tx[0]._hash}: ${f.errors.map((e) => e.message).join("; ")}`).join(" | ");
77218
+ throw new Error(`SimpleMempoolRunner: rejected ${failures.length} transaction(s) at admission: ${detail}`);
77219
+ }
77220
+ }
77221
+ const bundles = hashedTransactions.map((tx) => hydratedTransactionToPayloadBundle(tx));
77209
77222
  const inserted = await this.pendingTransactionsArchivist.insert(bundles);
77223
+ await this.enforceCap();
77210
77224
  return inserted.map((p) => p._hash);
77211
77225
  }
77212
77226
  async startHandler() {
@@ -77225,6 +77239,50 @@ var SimpleMempoolRunner = class extends AbstractCreatableProvider {
77225
77239
  this._syncTimerId = null;
77226
77240
  }
77227
77241
  }
77242
+ async collectAllPendingTransactionBundles() {
77243
+ const all = [];
77244
+ let cursor;
77245
+ while (true) {
77246
+ const batch = await this.pendingTransactionsArchivist.next({
77247
+ limit: ENFORCE_CAP_BATCH_SIZE,
77248
+ cursor,
77249
+ order: "asc"
77250
+ });
77251
+ if (batch.length === 0) break;
77252
+ all.push(...batch);
77253
+ cursor = batch.at(-1)?._sequence;
77254
+ if (batch.length < ENFORCE_CAP_BATCH_SIZE) break;
77255
+ }
77256
+ return all;
77257
+ }
77258
+ async enforceCap() {
77259
+ const cap = this.maxPendingTransactions;
77260
+ if (cap <= 0) return;
77261
+ const all = await this.collectAllPendingTransactionBundles();
77262
+ if (all.length <= cap) return;
77263
+ const excess = all.length - cap;
77264
+ const bundleHashesOldestFirst = all.map((p) => p._hash);
77265
+ const orderedForEviction = this.orderForEviction(bundleHashesOldestFirst);
77266
+ const toEvict = orderedForEviction.slice(0, excess);
77267
+ await this.pendingTransactionsArchivist.delete(toEvict);
77268
+ this.forgetBundleHashes(toEvict);
77269
+ this.logger?.debug(`enforceCap evicted ${toEvict.length} bundles (pool=${all.length}, cap=${cap})`);
77270
+ }
77271
+ async finalizePruneTransactionsResult(pruned, total) {
77272
+ this.logger?.debug(`prunePendingTransactions completed: pruned=${pruned}, totalChecked=${total}`);
77273
+ await this.enforceCap();
77274
+ return [pruned, total];
77275
+ }
77276
+ forgetBundleHashes(bundleHashes) {
77277
+ if (bundleHashes.length === 0) return;
77278
+ if (isDemotionAware(this._mempoolViewer)) this._mempoolViewer.forget(bundleHashes);
77279
+ }
77280
+ orderForEviction(bundleHashesOldestFirst) {
77281
+ if (isDemotionAware(this._mempoolViewer)) {
77282
+ return this._mempoolViewer.getEvictionPriorityOrder(bundleHashesOldestFirst);
77283
+ }
77284
+ return bundleHashesOldestFirst;
77285
+ }
77228
77286
  async routeRejectedTransaction(transaction, errors) {
77229
77287
  if (!this._deadLetterQueueRunner) return;
77230
77288
  const rejectionErrors = errors.map((e) => ({
@@ -77329,9 +77387,18 @@ SimpleMempoolRunner = __decorateClass$4([
77329
77387
  creatableProvider()
77330
77388
  ], SimpleMempoolRunner);
77331
77389
  var DEFAULT_MEMPOOL_SELECTION_RATIO = 0.66;
77390
+ var DEFAULT_DEMOTION_THRESHOLD = 3;
77391
+ var DEFAULT_HANDOUT_STATS_TTL_BLOCKS = 1e3;
77332
77392
  var SimpleMempoolViewer = class extends AbstractCreatableProvider {
77333
77393
  moniker = SimpleMempoolViewer.defaultMoniker;
77394
+ _handoutStats = /* @__PURE__ */ new Map();
77334
77395
  _windowedBlockViewer;
77396
+ get demotionThreshold() {
77397
+ return this.params.demotionThreshold ?? DEFAULT_DEMOTION_THRESHOLD;
77398
+ }
77399
+ get handoutStatsTtlBlocks() {
77400
+ return this.params.handoutStatsTtlBlocks ?? DEFAULT_HANDOUT_STATS_TTL_BLOCKS;
77401
+ }
77335
77402
  get pendingBlocksArchivist() {
77336
77403
  return this.params.pendingBlocksArchivist;
77337
77404
  }
@@ -77345,6 +77412,32 @@ var SimpleMempoolViewer = class extends AbstractCreatableProvider {
77345
77412
  await super.createHandler();
77346
77413
  this._windowedBlockViewer = await this.locator.getInstance(WindowedBlockViewerMoniker);
77347
77414
  }
77415
+ /** Drop handout stats for the given bundle hashes. Called when a bundle has been evicted or otherwise removed from the pool. */
77416
+ forget(bundleHashes) {
77417
+ for (const hash of bundleHashes) this._handoutStats.delete(hash);
77418
+ }
77419
+ /** Return the subset of the given bundle hashes that are currently considered demoted. */
77420
+ getDemotedBundleHashes(bundleHashes) {
77421
+ return bundleHashes.filter((hash) => this.isDemoted(hash));
77422
+ }
77423
+ /**
77424
+ * Return the bundle hashes in the order they should be evicted under size pressure.
77425
+ * Demoted entries come first, sorted by handouts descending; the remainder is left as-is
77426
+ * so the caller can append by FIFO sequence order.
77427
+ */
77428
+ getEvictionPriorityOrder(bundleHashes) {
77429
+ const demoted = [];
77430
+ const nonDemoted = [];
77431
+ for (const hash of bundleHashes) {
77432
+ if (this.isDemoted(hash)) demoted.push(hash);
77433
+ else nonDemoted.push(hash);
77434
+ }
77435
+ demoted.sort((a, b) => (this._handoutStats.get(b)?.handouts ?? 0) - (this._handoutStats.get(a)?.handouts ?? 0));
77436
+ return [...demoted, ...nonDemoted];
77437
+ }
77438
+ getHandoutStats(bundleHash) {
77439
+ return this._handoutStats.get(bundleHash);
77440
+ }
77348
77441
  async pendingBlocks({ cursor: providedCursor } = {}) {
77349
77442
  let cursor = void 0;
77350
77443
  if (isHash(providedCursor)) {
@@ -77387,6 +77480,7 @@ var SimpleMempoolViewer = class extends AbstractCreatableProvider {
77387
77480
  })
77388
77481
  )).filter(exists$2);
77389
77482
  const currentBlock = await this.windowedBlockViewer.currentBlock();
77483
+ const currentBlockNumber = currentBlock[0].block;
77390
77484
  const evaluated = await Promise.all(
77391
77485
  hydratedWithBundle.map(async ({ bundle: bundle3, tx }) => ({
77392
77486
  bundle: bundle3,
@@ -77400,25 +77494,43 @@ var SimpleMempoolViewer = class extends AbstractCreatableProvider {
77400
77494
  await Promise.all(
77401
77495
  deletionCandidates.map(async ({ bundle: bundle3, tx }) => {
77402
77496
  await this.deleteBundledTransaction(bundle3);
77497
+ this._handoutStats.delete(bundle3._hash);
77403
77498
  this.logger?.debug(`Purged completed/expired bundled transaction: ${bundle3._hash}/${tx[0]._hash}`);
77404
77499
  })
77405
77500
  );
77406
- const inclusionCandidates = (await Promise.all(validTransactions.map((x) => x.tx).map(async (tx) => {
77407
- if (await this.isInclusionCandidate(tx, currentBlock, false)) return tx;
77501
+ this.gcHandoutStats(currentBlockNumber);
77502
+ const inclusionCandidates = (await Promise.all(validTransactions.map(async ({ bundle: bundle3, tx }) => {
77503
+ if (await this.isInclusionCandidate(tx, currentBlock, false)) return { bundle: bundle3, tx };
77408
77504
  }))).filter(exists$2);
77409
77505
  const selectionRatio = this.params.mempoolSelectionRatio ?? DEFAULT_MEMPOOL_SELECTION_RATIO;
77410
- const maxByRatio = Math.ceil(inclusionCandidates.length * selectionRatio);
77411
- const effectiveLimit = Math.min(limit, maxByRatio);
77412
- const randomInclusionCandidates = deduplicateBySigner(
77413
- inclusionCandidates.filter(() => Math.random() < selectionRatio)
77414
- ).slice(0, effectiveLimit);
77415
- const result = randomInclusionCandidates.length > 0 ? randomInclusionCandidates : deduplicateBySigner(inclusionCandidates).slice(0, 1);
77416
- this.logger?.debug(`Inclusion candidates: ${inclusionCandidates.length}`);
77417
- return result;
77506
+ const nonDemoted = inclusionCandidates.filter(({ bundle: bundle3 }) => !this.isDemoted(bundle3._hash));
77507
+ const demoted = inclusionCandidates.filter(({ bundle: bundle3 }) => this.isDemoted(bundle3._hash));
77508
+ const primary = this.selectWithRatio(nonDemoted, limit, selectionRatio);
77509
+ const topupNeeded = limit - primary.length;
77510
+ const topup = topupNeeded > 0 ? this.selectWithRatio(demoted, topupNeeded, selectionRatio) : [];
77511
+ let combined = [...primary, ...topup];
77512
+ if (combined.length === 0 && inclusionCandidates.length > 0) {
77513
+ combined = deduplicateWithBundleBySigner(inclusionCandidates).slice(0, 1);
77514
+ }
77515
+ for (const { bundle: bundle3 } of combined) {
77516
+ this.recordHandout(bundle3._hash, currentBlockNumber);
77517
+ }
77518
+ this.logger?.debug(`Inclusion candidates: ${inclusionCandidates.length} (nonDemoted=${nonDemoted.length}, demoted=${demoted.length}); returning ${combined.length}`);
77519
+ return combined.map(({ tx }) => tx);
77520
+ }
77521
+ isDemoted(bundleHash) {
77522
+ const stats = this._handoutStats.get(bundleHash);
77523
+ return stats !== void 0 && stats.handouts >= this.demotionThreshold;
77418
77524
  }
77419
77525
  async deleteBundledTransaction(bundle3) {
77420
77526
  await this.pendingTransactionsArchivist.delete([bundle3._hash]);
77421
77527
  }
77528
+ gcHandoutStats(currentBlockNumber) {
77529
+ const ttl = this.handoutStatsTtlBlocks;
77530
+ for (const [hash, stats] of this._handoutStats) {
77531
+ if (currentBlockNumber - stats.firstHandoutAt > ttl) this._handoutStats.delete(hash);
77532
+ }
77533
+ }
77422
77534
  /**
77423
77535
  * Evaluates a transaction to determine if it should be purged from the mempool.
77424
77536
  * @param tx The transaction to evaluate
@@ -77451,6 +77563,21 @@ var SimpleMempoolViewer = class extends AbstractCreatableProvider {
77451
77563
  if (checkForDeletable && await this.isDeletable(tx, currentBlock)) return false;
77452
77564
  return true;
77453
77565
  }
77566
+ recordHandout(bundleHash, currentBlockNumber) {
77567
+ const existing = this._handoutStats.get(bundleHash);
77568
+ if (existing) {
77569
+ existing.handouts += 1;
77570
+ } else {
77571
+ this._handoutStats.set(bundleHash, { handouts: 1, firstHandoutAt: currentBlockNumber });
77572
+ }
77573
+ }
77574
+ selectWithRatio(group, take, selectionRatio) {
77575
+ if (take <= 0 || group.length === 0) return [];
77576
+ const maxByRatio = Math.ceil(group.length * selectionRatio);
77577
+ const effectiveLimit = Math.min(take, maxByRatio);
77578
+ const randomSelected = group.filter(() => Math.random() < selectionRatio);
77579
+ return deduplicateWithBundleBySigner(randomSelected).slice(0, effectiveLimit);
77580
+ }
77454
77581
  };
77455
77582
  __publicField$4(SimpleMempoolViewer, "defaultMoniker", MempoolViewerMoniker);
77456
77583
  __publicField$4(SimpleMempoolViewer, "dependencies", [WindowedBlockViewerMoniker]);
@@ -77458,9 +77585,9 @@ __publicField$4(SimpleMempoolViewer, "monikers", [MempoolViewerMoniker]);
77458
77585
  SimpleMempoolViewer = __decorateClass$4([
77459
77586
  creatableProvider()
77460
77587
  ], SimpleMempoolViewer);
77461
- function deduplicateBySigner(txs) {
77588
+ function deduplicateWithBundleBySigner(items) {
77462
77589
  const seen = /* @__PURE__ */ new Set();
77463
- return txs.filter((tx) => {
77590
+ return items.filter(({ tx }) => {
77464
77591
  const key = tx[0].addresses.toSorted().join(",");
77465
77592
  if (seen.has(key)) return false;
77466
77593
  seen.add(key);
@@ -79567,7 +79694,7 @@ function requireRe () {
79567
79694
  createToken('GTLT', '((?:<|>)?=?)');
79568
79695
 
79569
79696
  // Something like "2.*" or "1.2.x".
79570
- // Note that "x.x" is a valid xRange identifer, meaning "any version"
79697
+ // Note that "x.x" is a valid xRange identifier, meaning "any version"
79571
79698
  // Only the first item is strictly required.
79572
79699
  createToken('XRANGEIDENTIFIERLOOSE', `${src[t.NUMERICIDENTIFIERLOOSE]}|x|X|\\*`);
79573
79700
  createToken('XRANGEIDENTIFIER', `${src[t.NUMERICIDENTIFIER]}|x|X|\\*`);
@@ -80563,6 +80690,62 @@ function requireCoerce () {
80563
80690
  return coerce_1;
80564
80691
  }
80565
80692
 
80693
+ var truncate_1;
80694
+ var hasRequiredTruncate;
80695
+
80696
+ function requireTruncate () {
80697
+ if (hasRequiredTruncate) return truncate_1;
80698
+ hasRequiredTruncate = 1;
80699
+
80700
+ const parse = requireParse$1();
80701
+ const constants = requireConstants();
80702
+ const SemVer = requireSemver$1();
80703
+
80704
+ const truncate = (version, truncation, options) => {
80705
+ if (!constants.RELEASE_TYPES.includes(truncation)) {
80706
+ return null
80707
+ }
80708
+
80709
+ const clonedVersion = cloneInputVersion(version, options);
80710
+ return clonedVersion && doTruncation(clonedVersion, truncation)
80711
+ };
80712
+
80713
+ const cloneInputVersion = (version, options) => {
80714
+ const versionStringToParse = (
80715
+ version instanceof SemVer ? version.version : version
80716
+ );
80717
+
80718
+ return parse(versionStringToParse, options)
80719
+ };
80720
+
80721
+ const doTruncation = (version, truncation) => {
80722
+ if (isPrerelease(truncation)) {
80723
+ return version.version
80724
+ }
80725
+
80726
+ version.prerelease = [];
80727
+
80728
+ switch (truncation) {
80729
+ case 'major':
80730
+ version.minor = 0;
80731
+ version.patch = 0;
80732
+ break
80733
+ case 'minor':
80734
+ version.patch = 0;
80735
+ break
80736
+ }
80737
+
80738
+ return version.format()
80739
+ };
80740
+
80741
+ const isPrerelease = (type) => {
80742
+ return type.startsWith('pre')
80743
+ };
80744
+
80745
+ truncate_1 = truncate;
80746
+ return truncate_1;
80747
+ }
80748
+
80566
80749
  var lrucache;
80567
80750
  var hasRequiredLrucache;
80568
80751
 
@@ -82012,6 +82195,7 @@ function requireSemver () {
82012
82195
  const lte = requireLte();
82013
82196
  const cmp = requireCmp();
82014
82197
  const coerce = requireCoerce();
82198
+ const truncate = requireTruncate();
82015
82199
  const Comparator = requireComparator();
82016
82200
  const Range = requireRange$1();
82017
82201
  const satisfies = requireSatisfies();
@@ -82050,6 +82234,7 @@ function requireSemver () {
82050
82234
  lte,
82051
82235
  cmp,
82052
82236
  coerce,
82237
+ truncate,
82053
82238
  Comparator,
82054
82239
  Range,
82055
82240
  satisfies,
@@ -85149,38 +85334,29 @@ JsonRpcAccountBalanceViewer = __decorateClass$3([
85149
85334
  ], JsonRpcAccountBalanceViewer);
85150
85335
  var JsonRpcBlockViewerMethods = class extends AbstractJsonRpcViewer {
85151
85336
  moniker = BlockViewerMoniker;
85152
- dataLakeViewer;
85153
85337
  async blocksByHash(hash, limit) {
85154
- const result = await this.transport.sendRequest(
85338
+ return await this.transport.sendRequest(
85155
85339
  "blockViewer_blocksByHash",
85156
85340
  isDefined(limit) ? [hash, limit] : [hash]
85157
85341
  );
85158
- return await Promise.all(result.map(async (block) => (await addDataLakePayloads(block, this.dataLakeViewer))[0]));
85159
85342
  }
85160
85343
  async blocksByNumber(block, limit) {
85161
- const result = await this.transport.sendRequest(
85344
+ return await this.transport.sendRequest(
85162
85345
  "blockViewer_blocksByNumber",
85163
85346
  isDefined(limit) ? [block, limit] : [block]
85164
85347
  );
85165
- return await Promise.all(result.map(async (block2) => (await addDataLakePayloads(block2, this.dataLakeViewer))[0]));
85166
- }
85167
- async createHandler() {
85168
- await super.createHandler();
85169
- this.dataLakeViewer = await this.locator.tryGetInstance(DataLakeViewerMoniker);
85170
85348
  }
85171
85349
  async currentBlock() {
85172
- const result = await this.transport.sendRequest(
85350
+ return await this.transport.sendRequest(
85173
85351
  "blockViewer_currentBlock",
85174
85352
  []
85175
85353
  );
85176
- return (await addDataLakePayloads(result, this.dataLakeViewer))[0];
85177
85354
  }
85178
85355
  async payloadsByHash(hashes) {
85179
- const result = await this.transport.sendRequest(
85356
+ return await this.transport.sendRequest(
85180
85357
  "blockViewer_payloadsByHash",
85181
85358
  [hashes]
85182
85359
  );
85183
- return (await addDataLakePayloadsToPayloads(hashes, result, this.dataLakeViewer))[0];
85184
85360
  }
85185
85361
  schemas() {
85186
85362
  return BlockViewerRpcSchemas;
@@ -85530,7 +85706,6 @@ JsonRpcTimeSyncViewer = __decorateClass$3([
85530
85706
  ], JsonRpcTimeSyncViewer);
85531
85707
  var JsonRpcTransactionViewer = class extends AbstractJsonRpcViewer {
85532
85708
  moniker = JsonRpcTransactionViewer.defaultMoniker;
85533
- dataLakeViewer;
85534
85709
  _blockViewer;
85535
85710
  get blockViewer() {
85536
85711
  return this._blockViewer;
@@ -85566,24 +85741,23 @@ var JsonRpcTransactionViewer = class extends AbstractJsonRpcViewer {
85566
85741
  }
85567
85742
  async byHash(transactionHash) {
85568
85743
  const result = await this.transport.sendRequest("transactionViewer_byHash", [transactionHash]);
85569
- return result ? (await addDataLakePayloads(result, this.dataLakeViewer))[0] : null;
85744
+ return result ?? null;
85570
85745
  }
85571
85746
  async createHandler() {
85572
85747
  await super.createHandler();
85573
85748
  this._blockViewer = await this.locator.getInstance(BlockViewerMoniker);
85574
- this.dataLakeViewer = await this.locator.tryGetInstance(DataLakeViewerMoniker);
85575
85749
  }
85576
85750
  async transactionByBlockHashAndIndex(blockHash, transactionIndex) {
85577
85751
  const result = await this.transport.sendRequest("transactionViewer_transactionByBlockHashAndIndex", [blockHash, transactionIndex]);
85578
- return result ? (await addDataLakePayloads(result, this.dataLakeViewer))[0] : null;
85752
+ return result ?? null;
85579
85753
  }
85580
85754
  async transactionByBlockNumberAndIndex(blockNumber, transactionIndex) {
85581
85755
  const result = await this.transport.sendRequest("transactionViewer_transactionByBlockNumberAndIndex", [blockNumber, transactionIndex]);
85582
- return result ? (await addDataLakePayloads(result, this.dataLakeViewer))[0] : null;
85756
+ return result ?? null;
85583
85757
  }
85584
85758
  async transactionByHash(transactionHash) {
85585
85759
  const result = await this.transport.sendRequest("transactionViewer_transactionByHash", [transactionHash]);
85586
- return result ? (await addDataLakePayloads(result, this.dataLakeViewer))[0] : null;
85760
+ return result ?? null;
85587
85761
  }
85588
85762
  schemas() {
85589
85763
  return TransactionViewerRpcSchemas;
@@ -111901,6 +112075,16 @@ var validateHydratedBlock = /* @__PURE__ */ __name$g(async (context, hydratedBlo
111901
112075
  }
111902
112076
  return errors;
111903
112077
  }, "validateHydratedBlock");
112078
+ function offendingTransactionHashesForAddress(block, address) {
112079
+ const hashes = [];
112080
+ for (const payload of block[1]) {
112081
+ if (isSignedTransactionBoundWitnessWithHashMeta(payload) && payload.from === address) {
112082
+ hashes.push(payload._hash);
112083
+ }
112084
+ }
112085
+ return hashes;
112086
+ }
112087
+ __name$g(offendingTransactionHashesForAddress, "offendingTransactionHashesForAddress");
111904
112088
  var RequiredBalanceBlockStateValidator = /* @__PURE__ */ __name$g(async (context, block) => {
111905
112089
  return await spanRootAsync("RequiredBalanceBlockStateValidator", async () => {
111906
112090
  const errors = [];
@@ -111930,7 +112114,8 @@ var RequiredBalanceBlockStateValidator = /* @__PURE__ */ __name$g(async (context
111930
112114
  });
111931
112115
  const balance = result[address] ?? AttoXL1(0n);
111932
112116
  if (address !== XYO_ZERO_ADDRESS && reqBalance > balance) {
111933
- errors.push(new HydratedBlockStateValidationError(block?.[0]?._hash ?? ZERO_HASH, chainId, block, `insufficient balance for ${address} ${balance} < ${requiredBalances[address]}`));
112117
+ const offendingTransactionHashes = offendingTransactionHashesForAddress(block, address);
112118
+ errors.push(new HydratedBlockStateValidationError(block?.[0]?._hash ?? ZERO_HASH, chainId, block, `insufficient balance for ${address} ${balance} < ${requiredBalances[address]}`, void 0, offendingTransactionHashes.length > 0 ? offendingTransactionHashes : void 0));
111934
112119
  }
111935
112120
  }
111936
112121
  }, context);
@@ -111963,6 +112148,24 @@ var validateHydratedBlockState = /* @__PURE__ */ __name$g(async (context, hydrat
111963
112148
  return errors;
111964
112149
  }, context);
111965
112150
  }, "validateHydratedBlockState");
112151
+ async function balanceShortfallErrors(context, tx, requiredBalances) {
112152
+ const errors = [];
112153
+ const requiredAddresses = Object.keys(requiredBalances);
112154
+ if (requiredAddresses.length === 0) return errors;
112155
+ const headBlock = (await context.blockViewer.currentBlock())[0];
112156
+ const balances = await context.accountBalanceViewer.accountBalances(requiredAddresses, {
112157
+ head: headBlock._hash
112158
+ });
112159
+ for (const address of requiredAddresses) {
112160
+ const reqBalance = requiredBalances[address];
112161
+ const balance = balances[address] ?? AttoXL1(0n);
112162
+ if (address !== XYO_ZERO_ADDRESS && reqBalance > balance) {
112163
+ errors.push(new HydratedTransactionValidationError(tx?.[0]?._hash ?? ZERO_HASH, tx, `insufficient balance for ${address} ${balance} < ${reqBalance}`));
112164
+ }
112165
+ }
112166
+ return errors;
112167
+ }
112168
+ __name$g(balanceShortfallErrors, "balanceShortfallErrors");
111966
112169
  var RequiredBalanceTransactionStateValidator = /* @__PURE__ */ __name$g(async (context, tx) => {
111967
112170
  return await spanRootAsync("RequiredBalanceTransactionStateValidator", async () => {
111968
112171
  const errors = [];
@@ -111972,26 +112175,18 @@ var RequiredBalanceTransactionStateValidator = /* @__PURE__ */ __name$g(async (c
111972
112175
  }, tx[1]);
111973
112176
  const wrapper = await HydratedTransactionWrapper.parse(tx);
111974
112177
  const from = wrapper.boundWitness.from;
111975
- const gasCost = transactionRequiredGas(tx) * wrapper.fees.gasPrice;
112178
+ const gasRequired = transactionRequiredGas(tx);
112179
+ if (wrapper.fees.gasLimit < gasRequired) {
112180
+ errors.push(new HydratedTransactionValidationError(tx?.[0]?._hash ?? ZERO_HASH, tx, `fees.gasLimit ${wrapper.fees.gasLimit} < required gas ${gasRequired}`));
112181
+ }
112182
+ const gasCost = gasRequired * wrapper.fees.gasPrice;
111976
112183
  const baseCost = wrapper.fees.base;
111977
112184
  netBalances[from] = (netBalances[from] ?? 0n) - gasCost - baseCost;
111978
112185
  const requiredBalances = {};
111979
112186
  for (const [address, net] of Object.entries(netBalances)) {
111980
112187
  if (net < 0n) requiredBalances[address] = -net;
111981
112188
  }
111982
- const requiredAddresses = Object.keys(requiredBalances);
111983
- if (requiredAddresses.length === 0) return errors;
111984
- const headBlock = (await context.blockViewer.currentBlock())[0];
111985
- const balances = await context.accountBalanceViewer.accountBalances(requiredAddresses, {
111986
- head: headBlock._hash
111987
- });
111988
- for (const address of requiredAddresses) {
111989
- const reqBalance = requiredBalances[address];
111990
- const balance = balances[address] ?? AttoXL1(0n);
111991
- if (address !== XYO_ZERO_ADDRESS && reqBalance > balance) {
111992
- errors.push(new HydratedTransactionValidationError(tx?.[0]?._hash ?? ZERO_HASH, tx, `insufficient balance for ${address} ${balance} < ${reqBalance}`));
111993
- }
111994
- }
112189
+ errors.push(...await balanceShortfallErrors(context, tx, requiredBalances));
111995
112190
  } catch (ex) {
111996
112191
  errors.push(new HydratedTransactionValidationError(tx?.[0]?._hash ?? ZERO_HASH, tx, `Failed RequiredBalanceTransactionStateValidator: ${ex}`, ex));
111997
112192
  }
@@ -113615,6 +113810,70 @@ async function generateTransactionFeeTransfers(address, transactions) {
113615
113810
  return payloads;
113616
113811
  }
113617
113812
  __name$d(generateTransactionFeeTransfers, "generateTransactionFeeTransfers");
113813
+ async function scoreTransaction(tx) {
113814
+ const wrapper = await HydratedTransactionWrapper.parse(tx);
113815
+ const from = wrapper.boundWitness.from;
113816
+ const gasPrice = wrapper.fees.gasPrice;
113817
+ const gasCost = transactionRequiredGas(tx) * gasPrice;
113818
+ const baseCost = wrapper.fees.base;
113819
+ const netBalances = netBalancesForPayloads({
113820
+ }, tx[1]);
113821
+ const fromNet = netBalances[from] ?? 0n;
113822
+ const transferOut = fromNet < 0n ? -fromNet : 0n;
113823
+ return {
113824
+ cost: gasCost + baseCost + transferOut,
113825
+ gasPrice
113826
+ };
113827
+ }
113828
+ __name$d(scoreTransaction, "scoreTransaction");
113829
+ async function identifyOffendingTransactions(args) {
113830
+ const { errors, candidateTransactions } = args;
113831
+ const candidateHashes = new Set(candidateTransactions.map((tx) => tx[0]._hash));
113832
+ const txInvalidHashes = /* @__PURE__ */ new Set();
113833
+ for (const error of errors) {
113834
+ if (candidateHashes.has(error.hash)) txInvalidHashes.add(error.hash);
113835
+ const cause = error.cause;
113836
+ if (cause?.hash && candidateHashes.has(cause.hash)) txInvalidHashes.add(cause.hash);
113837
+ }
113838
+ if (txInvalidHashes.size > 0) {
113839
+ return {
113840
+ offendingHashes: [
113841
+ ...txInvalidHashes
113842
+ ],
113843
+ reason: "tx-invalid"
113844
+ };
113845
+ }
113846
+ const balanceOffenderHashes = /* @__PURE__ */ new Set();
113847
+ for (const error of errors) {
113848
+ const offendingHashes = error.offendingTransactionHashes ?? [];
113849
+ for (const hash of offendingHashes) {
113850
+ if (candidateHashes.has(hash)) balanceOffenderHashes.add(hash);
113851
+ }
113852
+ }
113853
+ if (balanceOffenderHashes.size === 0) {
113854
+ return {
113855
+ offendingHashes: [],
113856
+ reason: "unknown"
113857
+ };
113858
+ }
113859
+ const offenders = candidateTransactions.filter((tx) => balanceOffenderHashes.has(tx[0]._hash));
113860
+ const scored = await Promise.all(offenders.map(async (tx) => ({
113861
+ tx,
113862
+ ...await scoreTransaction(tx)
113863
+ })));
113864
+ scored.sort((a, b) => {
113865
+ if (a.cost !== b.cost) return a.cost < b.cost ? 1 : -1;
113866
+ if (a.gasPrice !== b.gasPrice) return a.gasPrice < b.gasPrice ? -1 : 1;
113867
+ return a.tx[0]._hash < b.tx[0]._hash ? -1 : 1;
113868
+ });
113869
+ return {
113870
+ offendingHashes: [
113871
+ scored[0].tx[0]._hash
113872
+ ],
113873
+ reason: "block-balance"
113874
+ };
113875
+ }
113876
+ __name$d(identifyOffendingTransactions, "identifyOffendingTransactions");
113618
113877
 
113619
113878
  // src/simple/block/runner/SimpleBlockRunner.ts
113620
113879
  function _ts_decorate4(decorators, target, key, desc) {
@@ -113647,6 +113906,9 @@ var SimpleBlockRunner = class _SimpleBlockRunner extends AbstractCreatableProvid
113647
113906
  moniker = _SimpleBlockRunner.defaultMoniker;
113648
113907
  _blockRewardDiviner;
113649
113908
  _deadLetterQueueRunner;
113909
+ // TODO(producer-exclusion-set): uncapped for v1. If memory grows in long-running producers,
113910
+ // add FIFO eviction or a TTL keyed off block number.
113911
+ _excludedTransactionHashes = /* @__PURE__ */ new Set();
113650
113912
  _lastRedeclarationBlock;
113651
113913
  _rejectedTransactionsArchivist;
113652
113914
  _account;
@@ -113788,17 +114050,18 @@ var SimpleBlockRunner = class _SimpleBlockRunner extends AbstractCreatableProvid
113788
114050
  const { block: previousBlock } = assertEx(asBlockBoundWitness(head), () => "Invalid head block");
113789
114051
  const nextBlock = previousBlock + 1;
113790
114052
  const chainId = await this.finalizationViewer.chainId();
113791
- const pendingTransactions = await this.mempoolViewer.pendingTransactions({
114053
+ const pendingTransactionsRaw = await this.mempoolViewer.pendingTransactions({
113792
114054
  limit: _SimpleBlockRunner.DefaultBlockSize
113793
114055
  });
114056
+ const pendingTransactions = pendingTransactionsRaw.filter((tx) => !this._excludedTransactionHashes.has(tx[0]._hash));
113794
114057
  const nextBlockTransactions = await this.rejectWrongChainTransactions(pendingTransactions, chainId);
113795
114058
  this.logger?.log(`Pending Tx Count ${nextBlockTransactions.length}`);
113796
- const blockPayloads = [];
114059
+ const nonTxPayloads = [];
113797
114060
  const producerRedeclarationPayload = await this.getProducerRedeclaration(head);
113798
- if (producerRedeclarationPayload) blockPayloads.push(producerRedeclarationPayload);
114061
+ if (producerRedeclarationPayload) nonTxPayloads.push(producerRedeclarationPayload);
113799
114062
  if (nextBlockTransactions.length === 0 && !this.heartbeatRequired(head) && !force) return;
113800
114063
  const rewardTransferPayloads = await this.getBlockRewardTransfers(nextBlock);
113801
- blockPayloads.push(...rewardTransferPayloads);
114064
+ nonTxPayloads.push(...rewardTransferPayloads);
113802
114065
  const transactionTransfers = await generateTransactionFeeTransfers(this.address, nextBlockTransactions);
113803
114066
  const timeStart = Date.now();
113804
114067
  const timePayload = await this.generateTimePayload();
@@ -113806,48 +114069,26 @@ var SimpleBlockRunner = class _SimpleBlockRunner extends AbstractCreatableProvid
113806
114069
  if (timeDuration > 100) {
113807
114070
  this.logger?.warn(`[Slow] Generated time payload in ${timeDuration}ms`);
113808
114071
  }
113809
- const [fundedNextBlockTransactions, fundedTransfers] = await this.filterByFunded(head, nextBlockTransactions, transactionTransfers, validateBalances);
113810
- blockPayloads.push(...fundedTransfers, timePayload);
113811
- this.logger?.info(`Building block ${head.block + 1}`);
113812
- const startBuild = Date.now();
114072
+ const [initialFundedTransactions, initialFundedTransfers] = await this.filterByFunded(head, nextBlockTransactions, transactionTransfers, validateBalances);
113813
114073
  const stepRewardPoolBalance = (await this.accountBalanceViewer.accountBalances([
113814
114074
  XYO_STEP_REWARD_ADDRESS
113815
114075
  ]))[XYO_STEP_REWARD_ADDRESS];
113816
- const block = await buildNextBlock(head, fundedNextBlockTransactions, blockPayloads, [
113817
- this.account
113818
- ], XYO_STEP_REWARD_ADDRESS, stepRewardPoolBalance, void 0, chainId);
113819
- this.logger?.info(`Built block ${block[0].block} in ${Date.now() - startBuild}ms with ${block[1].length} payloads`);
113820
- this.logger?.info(`Validating block ${block[0].block} with ${block[1].length} payloads`);
113821
- const startValidate = Date.now();
113822
- const validatedBlock = await this.blockValidationViewer.validateBlock(block, {
113823
- head: head._hash
114076
+ const result = await this.runBuildValidateRetryLoop({
114077
+ head,
114078
+ chainId,
114079
+ stepRewardPoolBalance,
114080
+ nonTxPayloads,
114081
+ timePayload,
114082
+ initialFundedTransactions,
114083
+ initialFundedTransfers
113824
114084
  });
113825
- this.logger?.info(`Validated block ${block[0].block} in ${Date.now() - startValidate}ms with ${block[1].length} payloads`);
113826
- if (isSignedHydratedBlockWithHashMeta(validatedBlock)) {
114085
+ if (isSignedHydratedBlockWithHashMeta(result.block)) {
113827
114086
  await this.mempoolRunner.submitBlocks([
113828
- validatedBlock
114087
+ result.block
113829
114088
  ]);
113830
- return validatedBlock;
113831
- } else {
113832
- const errors = validatedBlock;
113833
- this.logger?.warn(`Validation of produced block failed: ${errors.at(0)?.message}`);
113834
- if (this._deadLetterQueueRunner) {
113835
- const rejectionErrors = errors.map((e) => ({
113836
- hash: block[0]._hash,
113837
- name: "BlockValidationError",
113838
- message: String(e.message ?? e)
113839
- }));
113840
- await this._deadLetterQueueRunner.rejectBlock({
113841
- schema: BlockRejectionSchema,
113842
- block,
113843
- errors: rejectionErrors,
113844
- rejector: "producer"
113845
- });
113846
- } else {
113847
- const rejectedTransactions = block[1];
113848
- await this.rejectedTransactionsArchivist.insert(rejectedTransactions);
113849
- }
114089
+ return result.block;
113850
114090
  }
114091
+ if (result.block) await this.rejectExhaustedBlock(result.block, result.errors);
113851
114092
  } catch (error) {
113852
114093
  this.logger?.error(`Error proposing next valid block: ${error.message}`);
113853
114094
  throw error;
@@ -113894,6 +114135,24 @@ var SimpleBlockRunner = class _SimpleBlockRunner extends AbstractCreatableProvid
113894
114135
  }
113895
114136
  return false;
113896
114137
  }
114138
+ async rejectExhaustedBlock(block, errors) {
114139
+ this.logger?.warn(`Validation of produced block failed: ${errors.at(0)?.message}`);
114140
+ if (this._deadLetterQueueRunner) {
114141
+ const rejectionErrors = errors.map((e) => ({
114142
+ hash: block[0]._hash,
114143
+ name: "BlockValidationError",
114144
+ message: String(e.message ?? e)
114145
+ }));
114146
+ await this._deadLetterQueueRunner.rejectBlock({
114147
+ schema: BlockRejectionSchema,
114148
+ block,
114149
+ errors: rejectionErrors,
114150
+ rejector: "producer"
114151
+ });
114152
+ } else {
114153
+ await this.rejectedTransactionsArchivist.insert(block[1]);
114154
+ }
114155
+ }
113897
114156
  // Partition pending transactions by chain-ID match; route mismatches to the transaction DLQ.
113898
114157
  async rejectWrongChainTransactions(transactions, chainId) {
113899
114158
  const localChain = chainId.toLowerCase();
@@ -113924,6 +114183,51 @@ var SimpleBlockRunner = class _SimpleBlockRunner extends AbstractCreatableProvid
113924
114183
  }
113925
114184
  return matched;
113926
114185
  }
114186
+ async runBuildValidateRetryLoop(args) {
114187
+ const { chainId, head, initialFundedTransactions, initialFundedTransfers, nonTxPayloads, stepRewardPoolBalance, timePayload } = args;
114188
+ const maxAttempts = Math.max(1, initialFundedTransactions.length);
114189
+ let candidateTransactions = initialFundedTransactions;
114190
+ let candidateTransfers = initialFundedTransfers;
114191
+ let lastBlock;
114192
+ let lastErrors = [];
114193
+ for (let attempt = 0; attempt <= maxAttempts; attempt++) {
114194
+ const blockPayloads = [
114195
+ ...nonTxPayloads,
114196
+ ...candidateTransfers,
114197
+ timePayload
114198
+ ];
114199
+ this.logger?.info(`Building block ${head.block + 1}${attempt > 0 ? ` (retry ${attempt})` : ""}`);
114200
+ lastBlock = await buildNextBlock(head, candidateTransactions, blockPayloads, [
114201
+ this.account
114202
+ ], XYO_STEP_REWARD_ADDRESS, stepRewardPoolBalance, void 0, chainId);
114203
+ const validated = await this.blockValidationViewer.validateBlock(lastBlock, {
114204
+ head: head._hash
114205
+ });
114206
+ if (isSignedHydratedBlockWithHashMeta(validated)) {
114207
+ return {
114208
+ block: validated,
114209
+ errors: []
114210
+ };
114211
+ }
114212
+ lastErrors = validated;
114213
+ if (attempt === maxAttempts) break;
114214
+ const { offendingHashes, reason } = await identifyOffendingTransactions({
114215
+ candidateTransactions,
114216
+ errors: validated
114217
+ });
114218
+ if (offendingHashes.length === 0 || reason === "unknown") break;
114219
+ for (const offendingHash of offendingHashes) this._excludedTransactionHashes.add(offendingHash);
114220
+ const offendingSet = new Set(offendingHashes);
114221
+ candidateTransactions = candidateTransactions.filter((tx) => !offendingSet.has(tx[0]._hash));
114222
+ const remainingFromAddresses = new Set(candidateTransactions.map((tx) => tx[0].from));
114223
+ candidateTransfers = (await generateTransactionFeeTransfers(this.address, candidateTransactions)).filter((t) => remainingFromAddresses.has(t.from));
114224
+ this.logger?.warn(`Block validation failed (attempt ${attempt + 1}); excluded ${offendingHashes.length} tx(s), retrying`);
114225
+ }
114226
+ return {
114227
+ block: lastBlock,
114228
+ errors: lastErrors
114229
+ };
114230
+ }
113927
114231
  };
113928
114232
  SimpleBlockRunner = _ts_decorate4([
113929
114233
  creatableProvider()
@@ -185742,6 +186046,8 @@ BaseConfigContextZod.extend({
185742
186046
  });
185743
186047
  var DEFAULT_MEMPOOL_BLOCK_PRUNE_INTERVAL = 1e3;
185744
186048
  var DEFAULT_MEMPOOL_TRANSACTION_PRUNE_INTERVAL = 1e3;
186049
+ var DEFAULT_MEMPOOL_DEMOTION_THRESHOLD = 3;
186050
+ var DEFAULT_MEMPOOL_MAX_PENDING_TRANSACTIONS = 0;
185745
186051
  var MempoolConfigZod = HostActorConfigZod.extend({
185746
186052
  enabled: union$1([
185747
186053
  string$2(),
@@ -185778,6 +186084,16 @@ var MempoolConfigZod = HostActorConfigZod.extend({
185778
186084
  title: "mempool.blockPruneInterval",
185779
186085
  type: "number"
185780
186086
  }),
186087
+ demotionThreshold: number$2().int().positive().default(DEFAULT_MEMPOOL_DEMOTION_THRESHOLD).register(globalRegistry, {
186088
+ description: "Number of times a transaction may be handed out to producers without being included in a block before it is considered demoted",
186089
+ title: "mempool.demotionThreshold",
186090
+ type: "number"
186091
+ }),
186092
+ maxPendingTransactions: number$2().int().nonnegative().default(DEFAULT_MEMPOOL_MAX_PENDING_TRANSACTIONS).register(globalRegistry, {
186093
+ description: "Maximum number of pending transactions in the pool. When exceeded, demoted transactions are evicted first, then oldest by sequence. 0 disables the cap.",
186094
+ title: "mempool.maxPendingTransactions",
186095
+ type: "number"
186096
+ }),
185781
186097
  transactionPruneInterval: number$2().default(DEFAULT_MEMPOOL_TRANSACTION_PRUNE_INTERVAL).register(globalRegistry, {
185782
186098
  description: "The interval time (in milliseconds) between pending transaction prune attempts",
185783
186099
  title: "mempool.transactionPruneInterval",
@@ -186543,6 +186859,14 @@ function createDefaultCapabilityRegistry() {
186543
186859
  return registry;
186544
186860
  }
186545
186861
  __name$8(createDefaultCapabilityRegistry, "createDefaultCapabilityRegistry");
186862
+ function readMempoolTuning(ctx) {
186863
+ const cfg = ctx.actorContext.config;
186864
+ return {
186865
+ demotionThreshold: cfg?.mempool?.demotionThreshold,
186866
+ maxPendingTransactions: cfg?.mempool?.maxPendingTransactions
186867
+ };
186868
+ }
186869
+ __name$8(readMempoolTuning, "readMempoolTuning");
186546
186870
  var passes = /* @__PURE__ */ __name$8(() => true, "passes");
186547
186871
  var localTier1Descriptors = [
186548
186872
  {
@@ -186559,10 +186883,14 @@ var localTier1Descriptors = [
186559
186883
  ],
186560
186884
  surface: "node",
186561
186885
  preconditions: passes,
186562
- build: /* @__PURE__ */ __name$8((ctx) => SimpleMempoolViewer.factory(SimpleMempoolViewer.dependencies, {
186563
- pendingTransactionsArchivist: ctx.process.pendingTransactionsArchivist,
186564
- pendingBlocksArchivist: ctx.process.pendingBlocksArchivist
186565
- }), "build")
186886
+ build: /* @__PURE__ */ __name$8((ctx) => {
186887
+ const { demotionThreshold } = readMempoolTuning(ctx);
186888
+ return SimpleMempoolViewer.factory(SimpleMempoolViewer.dependencies, {
186889
+ pendingTransactionsArchivist: ctx.process.pendingTransactionsArchivist,
186890
+ pendingBlocksArchivist: ctx.process.pendingBlocksArchivist,
186891
+ demotionThreshold
186892
+ });
186893
+ }, "build")
186566
186894
  },
186567
186895
  {
186568
186896
  id: "SimpleMempoolRunner",
@@ -186578,10 +186906,14 @@ var localTier1Descriptors = [
186578
186906
  ],
186579
186907
  surface: "node",
186580
186908
  preconditions: passes,
186581
- build: /* @__PURE__ */ __name$8((ctx) => SimpleMempoolRunner.factory(SimpleMempoolRunner.dependencies, {
186582
- pendingTransactionsArchivist: ctx.process.pendingTransactionsArchivist,
186583
- pendingBlocksArchivist: ctx.process.pendingBlocksArchivist
186584
- }), "build")
186909
+ build: /* @__PURE__ */ __name$8((ctx) => {
186910
+ const { maxPendingTransactions } = readMempoolTuning(ctx);
186911
+ return SimpleMempoolRunner.factory(SimpleMempoolRunner.dependencies, {
186912
+ pendingTransactionsArchivist: ctx.process.pendingTransactionsArchivist,
186913
+ pendingBlocksArchivist: ctx.process.pendingBlocksArchivist,
186914
+ maxPendingTransactions
186915
+ });
186916
+ }, "build")
186585
186917
  },
186586
186918
  {
186587
186919
  id: "SimpleAccountBalanceViewer",
@@ -187731,6 +188063,167 @@ async function rootLocatorFromConfig(context, validateDepsOnRegister = false, on
187731
188063
  return locator;
187732
188064
  }
187733
188065
  __name$8(rootLocatorFromConfig, "rootLocatorFromConfig");
188066
+ var apiNeeds = {
188067
+ required: [
188068
+ AccountBalanceViewerMoniker,
188069
+ BlockViewerMoniker,
188070
+ FinalizationViewerMoniker,
188071
+ MempoolViewerMoniker,
188072
+ TransactionViewerMoniker,
188073
+ XyoConnectionMoniker$1,
188074
+ XyoGatewayMoniker,
188075
+ XyoRunnerMoniker,
188076
+ XyoViewerMoniker
188077
+ ]
188078
+ };
188079
+ var mempoolNeeds = {
188080
+ required: [
188081
+ MempoolRunnerMoniker,
188082
+ // SimpleMempoolRunner.dependencies includes TransactionValidationViewer
188083
+ // — not resolved by the actor directly but pulled in at construction.
188084
+ TransactionValidationViewerMoniker,
188085
+ // SimpleMempoolViewer.dependencies includes WindowedBlockViewer — the
188086
+ // mempool viewer is built when api/finalizer resolve MempoolViewer.
188087
+ WindowedBlockViewerMoniker
188088
+ ]
188089
+ };
188090
+ var finalizerNeeds = {
188091
+ required: [
188092
+ BlockValidationViewerMoniker,
188093
+ BlockViewerMoniker,
188094
+ FinalizationRunnerMoniker,
188095
+ MempoolViewerMoniker
188096
+ ],
188097
+ optional: [
188098
+ DeadLetterQueueRunnerMoniker
188099
+ ]
188100
+ };
188101
+ var ACTOR_MIRRORS = {
188102
+ api: apiNeeds,
188103
+ mempool: mempoolNeeds,
188104
+ finalizer: finalizerNeeds
188105
+ };
188106
+ var apiExtraDescriptors = [
188107
+ {
188108
+ id: "SimpleXyoRunner",
188109
+ satisfies: [
188110
+ XyoRunnerMoniker
188111
+ ],
188112
+ tier: 1,
188113
+ backings: [],
188114
+ surface: "node",
188115
+ preconditions: /* @__PURE__ */ __name$8(() => true, "preconditions"),
188116
+ build: /* @__PURE__ */ __name$8(() => SimpleXyoRunner.factory(SimpleXyoRunner.dependencies, {}), "build")
188117
+ },
188118
+ {
188119
+ id: "SimpleXyoConnectionRunner",
188120
+ satisfies: [
188121
+ XyoConnectionMoniker$1
188122
+ ],
188123
+ // Same tier as `SimpleXyoConnectionViewer`; higher priority wins the
188124
+ // `XyoConnection` moniker in any actor mix that includes the API.
188125
+ tier: 1,
188126
+ priority: 100,
188127
+ backings: [],
188128
+ surface: "node",
188129
+ preconditions: /* @__PURE__ */ __name$8(() => true, "preconditions"),
188130
+ build: /* @__PURE__ */ __name$8(() => SimpleXyoConnectionRunner.factory(SimpleXyoConnectionRunner.dependencies, {}), "build")
188131
+ },
188132
+ {
188133
+ id: "SimpleXyoGateway",
188134
+ satisfies: [
188135
+ XyoGatewayMoniker
188136
+ ],
188137
+ tier: 1,
188138
+ backings: [],
188139
+ surface: "node",
188140
+ preconditions: /* @__PURE__ */ __name$8(() => true, "preconditions"),
188141
+ build: /* @__PURE__ */ __name$8(() => SimpleXyoGateway.factory(SimpleXyoGateway.dependencies, {}), "build")
188142
+ }
188143
+ ];
188144
+ var ACTOR_EXTRA_DESCRIPTORS = {
188145
+ api: apiExtraDescriptors
188146
+ };
188147
+ var ACTOR_CONFIG_PARSERS = {
188148
+ api: ApiConfigZod,
188149
+ mempool: MempoolConfigZod,
188150
+ finalizer: FinalizerConfigZod
188151
+ };
188152
+ var SHARED_LOCATOR_SUPPORTED_ACTORS = [
188153
+ "api",
188154
+ "mempool",
188155
+ "finalizer"
188156
+ ];
188157
+ function canUseSharedLocator(actors, config) {
188158
+ if (actors.length === 0) return false;
188159
+ if (config.remote?.rpc) return false;
188160
+ const names = new Set(actors.map((a) => a.name));
188161
+ const fullCanary = SHARED_LOCATOR_SUPPORTED_ACTORS.every((name) => names.has(name));
188162
+ if (!fullCanary) return false;
188163
+ return actors.every((actor) => {
188164
+ if (!SHARED_LOCATOR_SUPPORTED_ACTORS.includes(actor.name)) return false;
188165
+ if (actor.name === "api") {
188166
+ const merged = ApiConfigZod.parse(deepMerge$1(config, actor));
188167
+ if (merged.stateless === true) return false;
188168
+ }
188169
+ return true;
188170
+ });
188171
+ }
188172
+ __name$8(canUseSharedLocator, "canUseSharedLocator");
188173
+ async function sharedLocatorFromConfig(context, actors, config, options = {}) {
188174
+ const unionNeeds2 = {
188175
+ required: dedupe(actors.flatMap((a) => ACTOR_MIRRORS[a.name]?.required ?? [])),
188176
+ optional: dedupe(actors.flatMap((a) => ACTOR_MIRRORS[a.name]?.optional ?? []))
188177
+ };
188178
+ const extraDescriptors = actors.flatMap((a) => ACTOR_EXTRA_DESCRIPTORS[a.name] ?? []);
188179
+ const commonContext = {
188180
+ ...context,
188181
+ config: {
188182
+ ...config,
188183
+ name: "_shared_common"
188184
+ }
188185
+ };
188186
+ const commonLocator = await commonLocatorFromConfig(commonContext);
188187
+ const sharedContext = {
188188
+ ...commonLocator.context,
188189
+ config: {
188190
+ ...config,
188191
+ name: "_shared"
188192
+ }
188193
+ };
188194
+ const result = await locatorFromActorNeeds(sharedContext, [
188195
+ unionNeeds2
188196
+ ], {
188197
+ extraDescriptors,
188198
+ onInsecureGenesisConfirm: options.onInsecureGenesisConfirm,
188199
+ validateDepsOnRegister: false
188200
+ });
188201
+ const sharedWithEvm = await initEvmProvidersIfAvailable(result.locator);
188202
+ sharedWithEvm.freeze();
188203
+ const perActor = {};
188204
+ for (const actor of actors) {
188205
+ const parser = ACTOR_CONFIG_PARSERS[actor.name];
188206
+ const actorConfig = parser ? parser.parse(deepMerge$1(config, actor)) : actor;
188207
+ const actorContext = {
188208
+ ...sharedWithEvm.context,
188209
+ config: actorConfig
188210
+ };
188211
+ const view = new ProviderFactoryLocator(actorContext, sharedWithEvm.registry);
188212
+ view.freeze();
188213
+ perActor[actor.name] = view;
188214
+ }
188215
+ return {
188216
+ shared: sharedWithEvm,
188217
+ perActor
188218
+ };
188219
+ }
188220
+ __name$8(sharedLocatorFromConfig, "sharedLocatorFromConfig");
188221
+ function dedupe(items) {
188222
+ return [
188223
+ ...new Set(items)
188224
+ ];
188225
+ }
188226
+ __name$8(dedupe, "dedupe");
187734
188227
 
187735
188228
  // src/node/config/locators/locatorsFromConfig.ts
187736
188229
  function allActorsAreSelfSufficient(actors, config) {
@@ -187744,6 +188237,12 @@ function allActorsAreSelfSufficient(actors, config) {
187744
188237
  __name$8(allActorsAreSelfSufficient, "allActorsAreSelfSufficient");
187745
188238
  async function locatorsFromConfig(context, { actors, ...config }, onInsecureGenesisConfirm) {
187746
188239
  const result = {};
188240
+ if (canUseSharedLocator(actors, config)) {
188241
+ const { perActor } = await sharedLocatorFromConfig(context, actors, config, {
188242
+ onInsecureGenesisConfirm
188243
+ });
188244
+ return perActor;
188245
+ }
187747
188246
  const skipRoot = allActorsAreSelfSufficient(actors, config);
187748
188247
  if (!skipRoot) {
187749
188248
  const rootContext = {
@@ -213763,7 +214262,7 @@ if (!nativeAccelerationDisabled) {
213763
214262
  }
213764
214263
  }
213765
214264
 
213766
- const version$1 = '5.76.6';
214265
+ const version$1 = '5.76.8';
213767
214266
 
213768
214267
  /**
213769
214268
  * Includes all the scripts needed by the queue and jobs.
@@ -217230,6 +217729,18 @@ local function getJobSchedulerEveryNextMillis(prevMillis, every, now, offset, st
217230
217729
  nextMillis = nextMillis > now and nextMillis or now
217231
217730
  else
217232
217731
  nextMillis = now
217732
+ -- For the first iteration with no startDate and an explicit
217733
+ -- offset, align nextMillis to the next offset slot strictly
217734
+ -- after now. Without this the user-supplied offset is
217735
+ -- recorded but ignored, and the first job fires at now
217736
+ -- instead of the next aligned timestamp (issue #3705).
217737
+ if offset and offset > 0 then
217738
+ local aligned = math.floor(nextMillis / every) * every + offset
217739
+ if aligned <= nextMillis then
217740
+ aligned = aligned + every
217741
+ end
217742
+ nextMillis = aligned
217743
+ end
217233
217744
  end
217234
217745
  else
217235
217746
  nextMillis = prevMillis + every
@@ -217872,7 +218383,7 @@ const addParentJob = {
217872
218383
  };
217873
218384
 
217874
218385
  const content$K = `--[[
217875
- Adds a priotitized job to the queue by doing the following:
218386
+ Adds a prioritized job to the queue by doing the following:
217876
218387
  - Increases the job counter if needed.
217877
218388
  - Creates a new job key with the job data.
217878
218389
  - Adds the job to the "added" list so that workers gets notified.
@@ -220076,7 +220587,7 @@ const content$D = `--[[
220076
220587
  ARGV[2] lock duration in milliseconds
220077
220588
  ARGV[3] jobid
220078
220589
  Output:
220079
- "1" if lock extented succesfully.
220590
+ "1" if lock extended successfully.
220080
220591
  ]]
220081
220592
  local rcall = redis.call
220082
220593
  if rcall("GET", KEYS[1]) == ARGV[1] then
@@ -221007,7 +221518,7 @@ if rcall("EXISTS", stalledCheckKey) == 1 then
221007
221518
  return {}
221008
221519
  end
221009
221520
  rcall("SET", stalledCheckKey, timestamp, "PX", maxCheckTime)
221010
- -- Trim events before emiting them to avoid trimming events emitted in this script
221521
+ -- Trim events before emitting them to avoid trimming events emitted in this script
221011
221522
  trimEvents(metaKey, eventStreamKey)
221012
221523
  -- Move all stalled jobs to wait
221013
221524
  local stalling = rcall('SMEMBERS', stalledKey)
@@ -221706,7 +222217,7 @@ const moveToDelayed = {
221706
222217
  };
221707
222218
 
221708
222219
  const content$k = `--[[
221709
- Move job from active to a finished status (completed o failed)
222220
+ Move job from active to a finished status (completed or failed)
221710
222221
  A job can only be moved to completed if it was active.
221711
222222
  The job must be locked before it can be moved to a finished status,
221712
222223
  and the lock must be released in this script.
@@ -221760,7 +222271,7 @@ local rcall = redis.call
221760
222271
  --- Includes
221761
222272
  --[[
221762
222273
  Functions to collect metrics based on a current and previous count of jobs.
221763
- Granualarity is fixed at 1 minute.
222274
+ Granularity is fixed at 1 minute.
221764
222275
  ]]
221765
222276
  --[[
221766
222277
  Function to loop in batches.
@@ -222640,7 +223151,7 @@ if rcall("EXISTS", jobIdKey) == 1 then -- Make sure job exists
222640
223151
  end
222641
223152
  local eventStreamKey = KEYS[4]
222642
223153
  local metaKey = KEYS[9]
222643
- -- Trim events before emiting them to avoid trimming events emitted in this script
223154
+ -- Trim events before emitting them to avoid trimming events emitted in this script
222644
223155
  trimEvents(metaKey, eventStreamKey)
222645
223156
  local prefix = ARGV[7]
222646
223157
  removeDeduplicationKeyIfNeededOnFinalization(prefix, jobAttributes[3], jobId)
@@ -222850,7 +223361,7 @@ const content$i = `--[[
222850
223361
  keys, consider that this call may be slow for very large queues.
222851
223362
  The queue needs to be "paused" or it will return an error
222852
223363
  If the queue has currently active jobs then the script by default will return error,
222853
- however this behaviour can be overrided using the 'force' option.
223364
+ however this behaviour can be overridden using the 'force' option.
222854
223365
  Input:
222855
223366
  KEYS[1] meta
222856
223367
  KEYS[2] base
@@ -223292,15 +223803,15 @@ const paginate = {
223292
223803
  };
223293
223804
 
223294
223805
  const content$g = `--[[
223295
- Pauses or resumes a queue globably.
223806
+ Pauses or resumes a queue globally.
223296
223807
  Input:
223297
- KEYS[1] 'wait' or 'paused''
223808
+ KEYS[1] 'wait' or 'paused'
223298
223809
  KEYS[2] 'paused' or 'wait'
223299
223810
  KEYS[3] 'meta'
223300
223811
  KEYS[4] 'prioritized'
223301
223812
  KEYS[5] events stream key
223302
223813
  KEYS[6] 'delayed'
223303
- KEYS|7] 'marker'
223814
+ KEYS[7] 'marker'
223304
223815
  ARGV[1] 'paused' or 'resumed'
223305
223816
  Event:
223306
223817
  publish paused or resumed event.
@@ -223469,7 +223980,7 @@ const content$e = `--[[
223469
223980
  ARGV[1] token
223470
223981
  ARGV[2] lock duration in milliseconds
223471
223982
  Output:
223472
- "OK" if lock extented succesfully.
223983
+ "OK" if lock extended successfully.
223473
223984
  ]]
223474
223985
  local rcall = redis.call
223475
223986
  if rcall("GET", KEYS[1]) == ARGV[1] then
@@ -225160,6 +225671,18 @@ local function getJobSchedulerEveryNextMillis(prevMillis, every, now, offset, st
225160
225671
  nextMillis = nextMillis > now and nextMillis or now
225161
225672
  else
225162
225673
  nextMillis = now
225674
+ -- For the first iteration with no startDate and an explicit
225675
+ -- offset, align nextMillis to the next offset slot strictly
225676
+ -- after now. Without this the user-supplied offset is
225677
+ -- recorded but ignored, and the first job fires at now
225678
+ -- instead of the next aligned timestamp (issue #3705).
225679
+ if offset and offset > 0 then
225680
+ local aligned = math.floor(nextMillis / every) * every + offset
225681
+ if aligned <= nextMillis then
225682
+ aligned = aligned + every
225683
+ end
225684
+ nextMillis = aligned
225685
+ end
225163
225686
  end
225164
225687
  else
225165
225688
  nextMillis = prevMillis + every
@@ -235752,7 +236275,7 @@ class JobScheduler extends QueueBase {
235752
236275
  // Check if we have a start date for the repeatable job
235753
236276
  const { immediately } = repeatOpts, filteredRepeatOpts = __rest(repeatOpts, ["immediately"]);
235754
236277
  let nextMillis;
235755
- const newOffset = null;
236278
+ const newOffset = every && offset ? offset : null;
235756
236279
  if (pattern) {
235757
236280
  nextMillis = await this.repeatStrategy(now, repeatOpts, jobName);
235758
236281
  if (nextMillis < now) {
@@ -236201,7 +236724,7 @@ class QueueGetters extends QueueBase {
236201
236724
  /**
236202
236725
  * Returns the time to live for a rate limited key in milliseconds.
236203
236726
  * @param maxJobs - max jobs to be considered in rate limit state. If not passed
236204
- * it will return the remaining ttl without considering if max jobs is excedeed.
236727
+ * it will return the remaining ttl without considering if max jobs is exceeded.
236205
236728
  * @returns -2 if the key does not exist.
236206
236729
  * -1 if the key exists but has no associated expire.
236207
236730
  * @see {@link https://redis.io/commands/pttl/}
@@ -248881,6 +249404,39 @@ var __name = (target, value) => __defProp(target, "name", { value, configurable:
248881
249404
  var deepMerge = createDeepMerge({
248882
249405
  arrayStrategy: "concat"
248883
249406
  });
249407
+ var REDACTED = "[REDACTED]";
249408
+ var REDACTED_KEY_NAMES = /* @__PURE__ */ new Set([
249409
+ "mnemonic",
249410
+ "connectionString"
249411
+ ]);
249412
+ var REDACTED_KEY_SUFFIX = /(PrivateKey|Secret|Password)$/i;
249413
+ function shouldRedactKey(key) {
249414
+ if (REDACTED_KEY_NAMES.has(key)) return true;
249415
+ return REDACTED_KEY_SUFFIX.test(key);
249416
+ }
249417
+ __name(shouldRedactKey, "shouldRedactKey");
249418
+ function redactConfig(config2) {
249419
+ const cloned = structuredClone(config2);
249420
+ redactInPlace(cloned);
249421
+ return cloned;
249422
+ }
249423
+ __name(redactConfig, "redactConfig");
249424
+ function redactInPlace(node) {
249425
+ if (Array.isArray(node)) {
249426
+ for (const item of node) redactInPlace(item);
249427
+ return;
249428
+ }
249429
+ if (node === null || typeof node !== "object") return;
249430
+ const obj = node;
249431
+ for (const key of Object.keys(obj)) {
249432
+ if (shouldRedactKey(key) && obj[key] !== void 0 && obj[key] !== null) {
249433
+ obj[key] = REDACTED;
249434
+ } else {
249435
+ redactInPlace(obj[key]);
249436
+ }
249437
+ }
249438
+ }
249439
+ __name(redactInPlace, "redactInPlace");
248884
249440
  function coerceActorsArray(argv) {
248885
249441
  const actors = argv.actors;
248886
249442
  if (actors === void 0 || Array.isArray(actors)) return argv;
@@ -248929,7 +249485,9 @@ async function configMiddleware(argv, setConfiguration) {
248929
249485
  assertNoActorMnemonics(finalConfig);
248930
249486
  setConfiguration(finalConfig);
248931
249487
  if (argv["dump-config"]) {
248932
- console.log(JSON.stringify(finalConfig, null, 2));
249488
+ const withSecrets = Boolean(argv["with-secrets"]);
249489
+ const output2 = withSecrets ? finalConfig : redactConfig(finalConfig);
249490
+ console.log(JSON.stringify(output2, null, 2));
248933
249491
  process.exit(0);
248934
249492
  }
248935
249493
  } catch (err) {
@@ -249078,6 +249636,176 @@ function withDeprecationWarning(module) {
249078
249636
  }
249079
249637
  __name(withDeprecationWarning, "withDeprecationWarning");
249080
249638
 
249639
+ // src/dumpProviders.ts
249640
+ var CANONICAL_ACTOR_ORDER = [
249641
+ "_root",
249642
+ "producer",
249643
+ "finalizer",
249644
+ "api",
249645
+ "mempool",
249646
+ "bridge",
249647
+ "rewardRedemption"
249648
+ ];
249649
+ function enumerateLocator(locator) {
249650
+ const collapsed = /* @__PURE__ */ new Map();
249651
+ const registry = locator.registry;
249652
+ for (const moniker of Object.keys(registry)) {
249653
+ const factories = registry[moniker];
249654
+ if (!factories) continue;
249655
+ for (const factory of factories) {
249656
+ const f = factory;
249657
+ const providerName = f.providerName ?? "<unknown>";
249658
+ const scope = f.scope ?? "<unknown>";
249659
+ const dependencies = f.dependencies ?? [];
249660
+ const fingerprint = `${moniker}|${providerName}|${scope}|${dependencies.join(",")}`;
249661
+ const existing = collapsed.get(fingerprint);
249662
+ if (existing) {
249663
+ existing.count += 1;
249664
+ } else {
249665
+ collapsed.set(fingerprint, {
249666
+ count: 1,
249667
+ dependencies,
249668
+ moniker,
249669
+ providerName,
249670
+ scope
249671
+ });
249672
+ }
249673
+ }
249674
+ }
249675
+ return [
249676
+ ...collapsed.values()
249677
+ ].toSorted((a, b) => a.moniker.localeCompare(b.moniker) || a.providerName.localeCompare(b.providerName));
249678
+ }
249679
+ __name(enumerateLocator, "enumerateLocator");
249680
+ function buildOwnerIndex(perActor) {
249681
+ const monikerOwners = /* @__PURE__ */ new Map();
249682
+ for (const [actorName, entries] of perActor) {
249683
+ for (const entry of entries) {
249684
+ let owners = monikerOwners.get(entry.moniker);
249685
+ if (!owners) {
249686
+ owners = /* @__PURE__ */ new Set();
249687
+ monikerOwners.set(entry.moniker, owners);
249688
+ }
249689
+ owners.add(actorName);
249690
+ }
249691
+ }
249692
+ return monikerOwners;
249693
+ }
249694
+ __name(buildOwnerIndex, "buildOwnerIndex");
249695
+ function renderDuplicatesSummary(monikerOwners) {
249696
+ const duplicates = [
249697
+ ...monikerOwners.entries()
249698
+ ].filter(([, owners]) => owners.size > 1).map(([moniker, owners]) => ({
249699
+ moniker,
249700
+ owners: [
249701
+ ...owners
249702
+ ].toSorted().map(formatGroupForDisplay)
249703
+ })).toSorted((a, b) => a.moniker.localeCompare(b.moniker));
249704
+ if (duplicates.length === 0) return [];
249705
+ const lines = [
249706
+ "Duplicate monikers (registered by more than one locator):"
249707
+ ];
249708
+ for (const { moniker, owners } of duplicates) {
249709
+ lines.push(` - ${moniker}: ${owners.join(", ")}`);
249710
+ }
249711
+ lines.push("");
249712
+ return lines;
249713
+ }
249714
+ __name(renderDuplicatesSummary, "renderDuplicatesSummary");
249715
+ function groupActorsBySharedRegistry(locators) {
249716
+ const groups = [];
249717
+ for (const actorName of Object.keys(locators)) {
249718
+ const locator = locators[actorName];
249719
+ const existing = groups.find((g) => g.registry === locator.registry);
249720
+ if (existing) {
249721
+ existing.actorNames.push(actorName);
249722
+ } else {
249723
+ groups.push({
249724
+ actorNames: [
249725
+ actorName
249726
+ ],
249727
+ locator,
249728
+ registry: locator.registry
249729
+ });
249730
+ }
249731
+ }
249732
+ return groups;
249733
+ }
249734
+ __name(groupActorsBySharedRegistry, "groupActorsBySharedRegistry");
249735
+ function groupId(actorNames) {
249736
+ return [
249737
+ ...actorNames
249738
+ ].toSorted().join(",");
249739
+ }
249740
+ __name(groupId, "groupId");
249741
+ function formatGroupForDisplay(groupKey) {
249742
+ const members = groupKey.split(",");
249743
+ return members.length === 1 ? members[0] : `(${members.join(", ")})`;
249744
+ }
249745
+ __name(formatGroupForDisplay, "formatGroupForDisplay");
249746
+ function renderGroupSection(actorNames, entries, monikerOwners) {
249747
+ const sortedActors = [
249748
+ ...actorNames
249749
+ ].toSorted();
249750
+ const ownGroupKey = groupId(sortedActors);
249751
+ const heading = sortedActors.length === 1 ? `Providers for actor: ${sortedActors[0]} (${entries.length} registered)` : `Providers shared by actors: ${sortedActors.join(", ")} (${entries.length} registered)`;
249752
+ const lines = [
249753
+ heading
249754
+ ];
249755
+ if (entries.length === 0) {
249756
+ lines.push(" (none)", "");
249757
+ return lines;
249758
+ }
249759
+ for (let i = 0; i < entries.length; i++) {
249760
+ const entry = entries[i];
249761
+ const isLast = i === entries.length - 1;
249762
+ const branch = isLast ? "\u2514\u2500\u2500" : "\u251C\u2500\u2500";
249763
+ const owners = monikerOwners.get(entry.moniker) ?? /* @__PURE__ */ new Set();
249764
+ const otherOwners = [
249765
+ ...owners
249766
+ ].filter((o) => o !== ownGroupKey).toSorted().map(formatGroupForDisplay);
249767
+ const dupNote = otherOwners.length > 0 ? ` \u26A0 also in: ${otherOwners.join(", ")}` : "";
249768
+ const depsNote = entry.dependencies.length > 0 ? `, deps: [${entry.dependencies.join(", ")}]` : "";
249769
+ const countNote = entry.count > 1 ? ` (\xD7${entry.count})` : "";
249770
+ lines.push(` ${branch} ${entry.moniker}${countNote} [impl: ${entry.providerName}, scope: ${entry.scope}${depsNote}]${dupNote}`);
249771
+ }
249772
+ lines.push("");
249773
+ return lines;
249774
+ }
249775
+ __name(renderGroupSection, "renderGroupSection");
249776
+ function formatProviderTree(locators) {
249777
+ const groups = groupActorsBySharedRegistry(locators);
249778
+ const groupKeys = groups.map((g) => ({
249779
+ ...g,
249780
+ key: groupId(g.actorNames)
249781
+ }));
249782
+ const perGroup = /* @__PURE__ */ new Map();
249783
+ for (const g of groupKeys) perGroup.set(g.key, enumerateLocator(g.locator));
249784
+ const monikerOwners = buildOwnerIndex(perGroup);
249785
+ const orderedGroups = [
249786
+ ...groupKeys
249787
+ ].toSorted((a, b) => {
249788
+ const aHasRoot = a.actorNames.includes("_root");
249789
+ const bHasRoot = b.actorNames.includes("_root");
249790
+ if (aHasRoot !== bHasRoot) return aHasRoot ? -1 : 1;
249791
+ const ai = CANONICAL_ACTOR_ORDER.indexOf(a.actorNames[0]);
249792
+ const bi = CANONICAL_ACTOR_ORDER.indexOf(b.actorNames[0]);
249793
+ if (ai !== bi) return (ai === -1 ? Number.MAX_SAFE_INTEGER : ai) - (bi === -1 ? Number.MAX_SAFE_INTEGER : bi);
249794
+ return a.key.localeCompare(b.key);
249795
+ });
249796
+ const lines = [
249797
+ "XL1 Provider Dump",
249798
+ "=================",
249799
+ ""
249800
+ ];
249801
+ for (const g of orderedGroups) {
249802
+ lines.push(...renderGroupSection(g.actorNames.toSorted(), perGroup.get(g.key) ?? [], monikerOwners));
249803
+ }
249804
+ lines.push(...renderDuplicatesSummary(monikerOwners));
249805
+ return lines.join("\n");
249806
+ }
249807
+ __name(formatProviderTree, "formatProviderTree");
249808
+
249081
249809
  // src/images.ts
249082
249810
  var XL1LogoColorizedAscii = `\x1B[38;2;128;128;128m\xA0\xA0\xA0\xA0\xA0\xA0\xA0\xA0\xA0\xA0\xA0\xA0\xA0\xA0\xA0\xA0\xA0\xA0\xA0\xA0\x1B[0m\x1B[38;2;118;111;144m_\x1B[0m
249083
249811
  \x1B[38;2;128;128;128m\xA0\xA0\xA0\xA0\xA0\xA0\xA0\xA0\xA0\xA0\xA0\xA0\xA0\xA0\xA0\xA0\xA0\xA0\xA0\x1B[0m\x1B[38;2;72;32;223m\u2560\x1B[0m\x1B[38;2;66;21;234m\u2560\x1B[0m
@@ -249121,7 +249849,8 @@ function defaultScrapePortForActors(actors) {
249121
249849
  __name(defaultScrapePortForActors, "defaultScrapePortForActors");
249122
249850
  var configuration;
249123
249851
  var skipInsecureConfirm = false;
249124
- var version = isDefined("1.22.0") ? "1.22.0" : "unknown";
249852
+ var dumpProviders = false;
249853
+ var version = isDefined("1.23.0") ? "1.23.0" : "unknown";
249125
249854
  function getConfiguration() {
249126
249855
  return configuration;
249127
249856
  }
@@ -249174,6 +249903,10 @@ async function getLocatorsFromConfig(actors, configuration2) {
249174
249903
  }
249175
249904
  const onInsecureGenesisConfirm = skipInsecureConfirm ? void 0 : async () => await promptForInsecureGenesisConfirmation(logger);
249176
249905
  const locators = await locatorsFromConfig(context, config2, onInsecureGenesisConfirm);
249906
+ if (dumpProviders) {
249907
+ console.log(formatProviderTree(locators));
249908
+ process.exit(0);
249909
+ }
249177
249910
  const healthCheckPort = configuration2.healthCheckPort ?? DEFAULT_HEALTH_CHECK_PORT;
249178
249911
  const healthServer = healthCheckPort > 0 && context.statusReporter !== void 0 ? await initHealthEndpoints({
249179
249912
  logger,
@@ -249218,6 +249951,7 @@ $0 <command> [options]`).parserConfiguration({
249218
249951
  configuration = config2;
249219
249952
  });
249220
249953
  skipInsecureConfirm = Boolean(argv2["skip-insecure-confirm"]);
249954
+ dumpProviders = Boolean(argv2["dump-providers"]);
249221
249955
  }).options(optionsFromGlobalZodRegistry()).wrap(y.terminalWidth()).command(withDeprecationWarning(apiCommand(getConfiguration, getLocatorsFromConfig))).command(withDeprecationWarning(bridgeCommand(getConfiguration, getLocatorsFromConfig))).command(withDeprecationWarning(finalizerCommand(getConfiguration, getLocatorsFromConfig))).command(withDeprecationWarning(mempoolCommand(getConfiguration, getLocatorsFromConfig))).command(withDeprecationWarning(producerCommand(getConfiguration, getLocatorsFromConfig))).command(withDeprecationWarning(rewardRedemptionCommand(getConfiguration, getLocatorsFromConfig))).command(startCommand(getConfiguration, getLocatorsFromConfig)).options({
249222
249956
  "config": {
249223
249957
  type: "string",
@@ -249230,7 +249964,17 @@ $0 <command> [options]`).parserConfiguration({
249230
249964
  },
249231
249965
  "dump-config": {
249232
249966
  type: "boolean",
249233
- description: "Just process the configuration and print the resolved config to stdout, then exit.",
249967
+ description: "Just process the configuration and print the resolved config to stdout, then exit. Secrets are redacted unless --with-secrets is also passed.",
249968
+ default: false
249969
+ },
249970
+ "with-secrets": {
249971
+ type: "boolean",
249972
+ description: 'When used with --dump-config, print raw secret values (mnemonic, private keys, passwords) instead of "[REDACTED]". Use only on a developer machine.',
249973
+ default: false
249974
+ },
249975
+ "dump-providers": {
249976
+ type: "boolean",
249977
+ description: "Run the normal command flow up to provider locator construction, print the per-actor provider tree (with duplicate detection), then exit.",
249234
249978
  default: false
249235
249979
  },
249236
249980
  "skip-insecure-confirm": {