@parity/product-deploy 0.11.0 → 0.12.0-rc.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (44) hide show
  1. package/bin/bulletin-bootstrap +23 -1
  2. package/dist/auth-config.js +3 -3
  3. package/dist/bug-report.js +4 -4
  4. package/dist/{chunk-JWMHCW74.js → chunk-2NF6QIDF.js} +1 -1
  5. package/dist/{chunk-53BU4X2R.js → chunk-52F2ETEG.js} +12 -2
  6. package/dist/{chunk-O4I37F5K.js → chunk-5BE5PQH6.js} +55 -12
  7. package/dist/{chunk-7RYEYWYO.js → chunk-BH7DJ5IW.js} +1 -1
  8. package/dist/{chunk-SES7CGNY.js → chunk-BXW5MBBY.js} +1 -1
  9. package/dist/{chunk-HLXRVFKL.js → chunk-FVPRBTHT.js} +38 -1
  10. package/dist/{chunk-TE2YKRYY.js → chunk-GGXK4DDN.js} +2 -2
  11. package/dist/{chunk-UUJFLJKY.js → chunk-OOGXTHVQ.js} +158 -61
  12. package/dist/{chunk-QDHLTSEO.js → chunk-PXEFWMJP.js} +2 -2
  13. package/dist/{chunk-7E33R3OQ.js → chunk-VIZ6GBQE.js} +1 -1
  14. package/dist/{chunk-HTZGBUZA.js → chunk-W2OSH4GD.js} +3 -3
  15. package/dist/{chunk-MRQPJLPS.js → chunk-XVQICFTB.js} +22 -1
  16. package/dist/chunk-probe.d.ts +43 -1
  17. package/dist/chunk-probe.js +11 -5
  18. package/dist/commands/login.js +11 -11
  19. package/dist/commands/logout.js +4 -4
  20. package/dist/commands/transfer.js +4 -4
  21. package/dist/commands/whoami.js +3 -3
  22. package/dist/deploy-actors.js +6 -6
  23. package/dist/deploy.d.ts +45 -1
  24. package/dist/deploy.js +17 -11
  25. package/dist/dotns.d.ts +33 -1
  26. package/dist/dotns.js +14 -4
  27. package/dist/index.d.ts +1 -1
  28. package/dist/index.js +18 -12
  29. package/dist/manifest/publish.js +12 -12
  30. package/dist/memory-report.js +2 -2
  31. package/dist/merkle.js +11 -11
  32. package/dist/personhood/bootstrap.js +4 -4
  33. package/dist/personhood/people-client.js +4 -4
  34. package/dist/personhood/reprove.js +19 -2
  35. package/dist/pool.d.ts +11 -1
  36. package/dist/pool.js +9 -1
  37. package/dist/run-state.js +1 -1
  38. package/dist/sss-allowance-cache.js +4 -4
  39. package/dist/storage-signer.d.ts +28 -3
  40. package/dist/storage-signer.js +13 -11
  41. package/dist/telemetry.js +2 -2
  42. package/dist/version-check.js +3 -3
  43. package/package.json +1 -1
  44. package/tools/release-retry-wrapper.mjs +6 -0
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  preflightSssAllowance
3
- } from "./chunk-7RYEYWYO.js";
3
+ } from "./chunk-BH7DJ5IW.js";
4
4
  import {
5
5
  statementSigningAccount
6
6
  } from "./chunk-GRPLHUYC.js";
@@ -31,16 +31,18 @@ import {
31
31
  } from "./chunk-S7EM5VMW.js";
32
32
  import {
33
33
  setDeployContext
34
- } from "./chunk-HTZGBUZA.js";
34
+ } from "./chunk-W2OSH4GD.js";
35
35
  import {
36
- probeChunks
37
- } from "./chunk-HLXRVFKL.js";
36
+ getBestBlockNumber,
37
+ probeChunks,
38
+ probeFinalityGap
39
+ } from "./chunk-FVPRBTHT.js";
38
40
  import {
39
41
  packSection
40
42
  } from "./chunk-C2TS5MER.js";
41
43
  import {
42
44
  resolveStorageSigner
43
- } from "./chunk-QDHLTSEO.js";
45
+ } from "./chunk-PXEFWMJP.js";
44
46
  import {
45
47
  createSlotAccountSigner,
46
48
  requestResourceAllocation
@@ -49,7 +51,7 @@ import {
49
51
  STALE_SESSION_MESSAGE,
50
52
  getPeopleChainEndpoints,
51
53
  hasPersistedSession
52
- } from "./chunk-JWMHCW74.js";
54
+ } from "./chunk-2NF6QIDF.js";
53
55
  import {
54
56
  CLI_NAME
55
57
  } from "./chunk-TSPERKUS.js";
@@ -62,7 +64,7 @@ import {
62
64
  parseDomainName,
63
65
  popStatusName,
64
66
  verifyNonceAdvanced
65
- } from "./chunk-O4I37F5K.js";
67
+ } from "./chunk-5BE5PQH6.js";
66
68
  import {
67
69
  derivePoolAccounts,
68
70
  detectTestnet,
@@ -70,7 +72,7 @@ import {
70
72
  fetchPoolAuthorizations,
71
73
  isAuthorizationSufficient,
72
74
  selectAccount
73
- } from "./chunk-MRQPJLPS.js";
75
+ } from "./chunk-XVQICFTB.js";
74
76
  import {
75
77
  VERSION,
76
78
  captureWarning,
@@ -84,7 +86,7 @@ import {
84
86
  truncateAddress,
85
87
  withDeploySpan,
86
88
  withSpan
87
- } from "./chunk-53BU4X2R.js";
89
+ } from "./chunk-52F2ETEG.js";
88
90
  import {
89
91
  DEFAULT_ENV_ID,
90
92
  getPopSelfServeConfig,
@@ -339,27 +341,58 @@ async function waitForBulletinAuthorization(ss58, opts = {}) {
339
341
  client.destroy();
340
342
  }
341
343
  }
342
- async function getSlotSignerProvider(signer, ss58) {
343
- const primary = BULLETIN_ENDPOINTS[0];
344
- console.log(` Connecting to Bulletin (slot signer): ${primary}`);
345
- const client = createPolkadotClient(getWsProvider(
346
- BULLETIN_ENDPOINTS,
347
- { heartbeatTimeout: WS_HEARTBEAT_TIMEOUT_MS, onStatusChanged: makeBulletinStatusHandler(primary) }
348
- ));
349
- const unsafeApi = client.getUnsafeApi();
350
- const [auth, currentBlock] = await Promise.all([
351
- unsafeApi.query.TransactionStorage.Authorizations.getValue(Enum("Account", ss58)),
352
- client.getFinalizedBlock()
353
- ]);
354
- const result = isBulletinAuthActive(auth, currentBlock.number);
355
- if (!result.active) {
356
- client.destroy();
357
- throw new BulletinSlotAuthError(result.reason, ss58, result.expiration);
344
+ async function withTransientRetry(attempt, opts = {}) {
345
+ const { retries = 2, delayMs = 1e3 } = opts;
346
+ const debug = Boolean(process.env.DOT_DEBUG);
347
+ let lastErr;
348
+ for (let i = 0; i <= retries; i++) {
349
+ try {
350
+ return await attempt();
351
+ } catch (e) {
352
+ if (e instanceof BulletinSlotAuthError) throw e;
353
+ lastErr = e;
354
+ if (i < retries) {
355
+ if (debug) {
356
+ console.error(
357
+ `[slot-signer] transient error (attempt ${i + 1}/${retries + 1}): ${e instanceof Error ? e.message : String(e)}`
358
+ );
359
+ }
360
+ await new Promise((r) => setTimeout(r, delayMs));
361
+ }
362
+ }
358
363
  }
359
- console.log(` Using slot signer: ${ss58} (authorized until block ${result.expiration})`);
360
- setDeployAttribute("deploy.signer.mode", "slot");
361
- setDeployAttribute("deploy.signer.address", truncateAddress(ss58));
362
- return { client, unsafeApi, signer, ss58 };
364
+ throw lastErr;
365
+ }
366
+ async function getSlotSignerProvider(signer, ss58) {
367
+ return withTransientRetry(async () => {
368
+ const primary = BULLETIN_ENDPOINTS[0];
369
+ console.log(` Connecting to Bulletin (slot signer): ${primary}`);
370
+ const client = createPolkadotClient(getWsProvider(
371
+ BULLETIN_ENDPOINTS,
372
+ { heartbeatTimeout: WS_HEARTBEAT_TIMEOUT_MS, onStatusChanged: makeBulletinStatusHandler(primary) }
373
+ ));
374
+ const unsafeApi = client.getUnsafeApi();
375
+ let auth;
376
+ let currentBlock;
377
+ try {
378
+ [auth, currentBlock] = await Promise.all([
379
+ unsafeApi.query.TransactionStorage.Authorizations.getValue(Enum("Account", ss58)),
380
+ client.getFinalizedBlock()
381
+ ]);
382
+ } catch (e) {
383
+ client.destroy();
384
+ throw e;
385
+ }
386
+ const result = isBulletinAuthActive(auth, currentBlock.number);
387
+ if (!result.active) {
388
+ client.destroy();
389
+ throw new BulletinSlotAuthError(result.reason, ss58, result.expiration);
390
+ }
391
+ console.log(` Using slot signer: ${ss58} (authorized until block ${result.expiration})`);
392
+ setDeployAttribute("deploy.signer.mode", "slot");
393
+ setDeployAttribute("deploy.signer.address", truncateAddress(ss58));
394
+ return { client, unsafeApi, signer, ss58 };
395
+ });
363
396
  }
364
397
 
365
398
  // src/deploy.ts
@@ -400,15 +433,28 @@ var MAX_RECONNECTIONS = parseInt(process.env.BULLETIN_MAX_RECONNECTIONS ?? "3",
400
433
  var CHUNK_TIMEOUT_MS = parseInt(process.env.BULLETIN_CHUNK_TIMEOUT_MS ?? "180000", 10);
401
434
  var CHUNK_MORTALITY_PERIOD = (() => {
402
435
  const v = parseInt(process.env.BULLETIN_CHUNK_MORTALITY_PERIOD ?? "", 10);
403
- return Number.isFinite(v) && v > 0 ? v : 16;
436
+ return Number.isFinite(v) && v > 0 ? v : 64;
404
437
  })();
438
+ var CHUNK_LIVENESS_MAX_WAIT_MS = parseInt(process.env.BULLETIN_CHUNK_LIVENESS_MAX_WAIT_MS ?? "60000", 10);
439
+ var CHUNK_LIVENESS_POLL_MS = 5e3;
405
440
  var RETRY_BASE_DELAY_MS = 2e3;
406
441
  var RETRY_MAX_DELAY_MS = 15e3;
407
442
  var WS_HEARTBEAT_TIMEOUT_MS = 3e5;
408
- var GRANDPA_NATURAL_WAIT_MS = parseInt(process.env.BULLETIN_GRANDPA_NATURAL_WAIT_MS ?? "90000", 10);
443
+ var GRANDPA_NATURAL_WAIT_MS = parseInt(process.env.BULLETIN_GRANDPA_NATURAL_WAIT_MS ?? "210000", 10);
409
444
  var GRANDPA_REUPLOAD_POLL_MS = 5e3;
410
- var GRANDPA_REUPLOAD_TIMEOUT_MS = 12e4;
445
+ var GRANDPA_REUPLOAD_TIMEOUT_MS = parseInt(process.env.BULLETIN_GRANDPA_REUPLOAD_TIMEOUT_MS ?? "120000", 10);
411
446
  var GRANDPA_REUPLOAD_MAX_ROUNDS = 3;
447
+ var GRANDPA_LAGGING_WAIT_MS = parseInt(process.env.BULLETIN_GRANDPA_LAGGING_WAIT_MS ?? "90000", 10);
448
+ async function pollUntilFinalized(cids, timeoutMs, client) {
449
+ const start = Date.now();
450
+ while (Date.now() - start < timeoutMs && cids.size > 0) {
451
+ await new Promise((r) => setTimeout(r, GRANDPA_REUPLOAD_POLL_MS));
452
+ const poll = await probeChunks([...cids], { client, atFinalized: true });
453
+ for (const r of poll) {
454
+ if (r.present === true) cids.delete(r.cid);
455
+ }
456
+ }
457
+ }
412
458
  var RETRY_BUDGET_MAX_EVENTS = parseInt(process.env.BULLETIN_RETRY_BUDGET_MAX ?? "5", 10);
413
459
  var RETRY_BUDGET_WINDOW_MS = parseInt(process.env.BULLETIN_RETRY_BUDGET_WINDOW_MS ?? "30000", 10);
414
460
  function retryBudgetExhausted(history, maxEvents, windowMs, now = Date.now()) {
@@ -621,6 +667,12 @@ function formatTransferModeStorageSignerLine(workerAddress) {
621
667
  function formatTransferModeDotnsLine(alreadyOwned, dotName, recipient) {
622
668
  return alreadyOwned ? ` DotNS: you already own ${dotName} \u2014 content update needs your phone signature (no transfer)` : ` DotNS: will register ${dotName} and transfer it to your account ${recipient}`;
623
669
  }
670
+ function describeSlotFallbackReason(e) {
671
+ if (e instanceof BulletinSlotAuthError) {
672
+ return e.reason === "expired" && e.expiration != null ? `expired at block ${e.expiration}` : "no on-chain authorization found";
673
+ }
674
+ return e instanceof Error ? e.message : String(e);
675
+ }
624
676
  function selectStorageReconnect(options) {
625
677
  if (options.storageSigner && options.storageSignerAddress) {
626
678
  let useSlot = true;
@@ -631,12 +683,8 @@ function selectStorageReconnect(options) {
631
683
  } catch (e) {
632
684
  useSlot = false;
633
685
  setDeployAttribute("deploy.signer.mode", "pool-fallback");
634
- let reason;
635
- if (e instanceof BulletinSlotAuthError) {
636
- reason = e.reason === "expired" && e.expiration != null ? `expired at block ${e.expiration}` : "no on-chain authorization found";
637
- } else {
638
- reason = e instanceof Error ? e.message : String(e);
639
- }
686
+ const reason = describeSlotFallbackReason(e);
687
+ setDeployAttribute("deploy.signer.fallback_reason", reason);
640
688
  console.warn(
641
689
  `\u26A0 Bulletin allowance slot not usable: ${reason}
642
690
  Falling back to the shared pool account for storage (fine on testnet).
@@ -713,6 +761,26 @@ function watchTransaction(tx, signer, txOpts, onSuccess, { label = "transaction"
713
761
  });
714
762
  });
715
763
  }
764
+ function reconcileTimedOutChunk(opts) {
765
+ const { originalNonce, currentNonce, nonceHeuristicValid, cidPresentAtBest } = opts;
766
+ if (cidPresentAtBest === true) return true;
767
+ if (nonceHeuristicValid && originalNonce !== void 0 && originalNonce < currentNonce) return true;
768
+ return false;
769
+ }
770
+ async function waitForChainLiveness(client, lastHeight, timeoutMs, pollMs = CHUNK_LIVENESS_POLL_MS) {
771
+ if (lastHeight == null) return getBestBlockNumber(client);
772
+ const deadline = Date.now() + timeoutMs;
773
+ let height = lastHeight;
774
+ while (Date.now() < deadline) {
775
+ const h = await getBestBlockNumber(client);
776
+ if (h == null) return height;
777
+ height = h;
778
+ if (height > lastHeight) return height;
779
+ await new Promise((r) => setTimeout(r, pollMs));
780
+ }
781
+ return height;
782
+ }
783
+ var __waitForChainLivenessForTest = waitForChainLiveness;
716
784
  async function storeChunk(unsafeApi, signer, chunkBytes, nonce, ss58, opts = {}) {
717
785
  const cid = createCID(chunkBytes, CID_CONFIG.codec, CID_CONFIG.hashCode);
718
786
  const tx = unsafeApi.tx.TransactionStorage.store_with_cid_config({ cid: { codec: BigInt(CID_CONFIG.codec), hashing: toHashingEnum(CID_CONFIG.hashCode) }, data: chunkBytes });
@@ -1062,8 +1130,14 @@ async function storeChunkedContent(chunks, { client: existingClient, unsafeApi:
1062
1130
  try {
1063
1131
  const currentNonce = await _fetchNonce(BULLETIN_ENDPOINTS, ss58);
1064
1132
  const originalNonce = assignedNonces.get(fail.index);
1065
- if (!perRetryChanged && originalNonce !== void 0 && originalNonce < currentNonce) {
1066
- console.log(` Chunk ${fail.index}: nonce ${originalNonce} consumed (current=${currentNonce}), treating as included`);
1133
+ let cidPresentAtBest = null;
1134
+ try {
1135
+ const [probe] = await probeChunks([failCid.toString()], { client });
1136
+ cidPresentAtBest = probe.present === true ? true : null;
1137
+ } catch {
1138
+ }
1139
+ if (reconcileTimedOutChunk({ originalNonce, currentNonce, nonceHeuristicValid: !perRetryChanged, cidPresentAtBest })) {
1140
+ console.log(` Chunk ${fail.index}: reconcile found it already included (nonce ${originalNonce}\u2192${currentNonce}${cidPresentAtBest ? ", CID present at best-block" : ""}) \u2014 skipping resubmit`);
1067
1141
  stored[fail.index] = { cid: createCID(fail.chunkData, CID_CONFIG.codec, 18), len: fail.chunkData.length, viaFallback: true };
1068
1142
  nonceAdvanceIndices.add(fail.index);
1069
1143
  assignedNonces.delete(fail.index);
@@ -1071,6 +1145,11 @@ async function storeChunkedContent(chunks, { client: existingClient, unsafeApi:
1071
1145
  retried = true;
1072
1146
  break;
1073
1147
  }
1148
+ const heightBefore = await getBestBlockNumber(client);
1149
+ const heightAfter = await waitForChainLiveness(client, heightBefore, CHUNK_LIVENESS_MAX_WAIT_MS);
1150
+ if (heightBefore != null && heightAfter != null && heightAfter <= heightBefore) {
1151
+ console.log(` Chunk ${fail.index}: chain still frozen at block ${heightBefore} after ${(CHUNK_LIVENESS_MAX_WAIT_MS / 1e3).toFixed(0)}s wait \u2014 resubmitting anyway`);
1152
+ }
1074
1153
  const retryNonce = originalNonce ?? currentNonce;
1075
1154
  const result2 = await storeChunk(unsafeApi, signer, fail.chunkData, retryNonce, ss58, { fetchNonce: fetchNonceOverride });
1076
1155
  stored[fail.index] = result2;
@@ -1662,23 +1741,27 @@ async function storeDirectoryV2(directoryPath, opts = {}) {
1662
1741
  let missingCids = new Set(finalityResults.filter((r) => r.present === false).map((r) => r.cid));
1663
1742
  setDeployAttribute("deploy.probe.finality_miss_count", missingCids.size);
1664
1743
  let reuploadCount = 0;
1744
+ let laggingFinalityCount = 0;
1665
1745
  if (missingCids.size === 0) {
1666
1746
  console.log(` \u2713 All ${grandpaCids.length} chunks finalised`);
1667
1747
  } else {
1668
1748
  console.log(` ${missingCids.size} chunks not yet finalised \u2014 waiting up to ${GRANDPA_NATURAL_WAIT_MS / 1e3}s for natural finalisation`);
1669
1749
  for (const cid of missingCids) console.log(` ${cid.slice(0, 20)}\u2026`);
1670
1750
  const waitStart = Date.now();
1671
- while (Date.now() - waitStart < GRANDPA_NATURAL_WAIT_MS && missingCids.size > 0) {
1672
- await new Promise((r) => setTimeout(r, GRANDPA_REUPLOAD_POLL_MS));
1673
- const poll = await probeChunks([...missingCids], { client: phaseALiveProvider.client, atFinalized: true });
1674
- for (const r of poll) {
1675
- if (r.present === true) missingCids.delete(r.cid);
1676
- }
1677
- }
1751
+ await pollUntilFinalized(missingCids, GRANDPA_NATURAL_WAIT_MS, phaseALiveProvider.client);
1678
1752
  if (missingCids.size === 0) {
1679
1753
  const elapsed = Math.round((Date.now() - waitStart) / 1e3);
1680
1754
  console.log(` \u2713 All ${grandpaCids.length} chunks finalised (waited ${elapsed}s)`);
1681
1755
  } else {
1756
+ const stillMissing = [...missingCids];
1757
+ const { reallyMissing, lagging } = await probeFinalityGap(stillMissing, { client: phaseALiveProvider.client });
1758
+ const laggingCids = new Set(lagging);
1759
+ laggingFinalityCount = laggingCids.size;
1760
+ missingCids = new Set(reallyMissing);
1761
+ setDeployAttribute("deploy.probe.finality_lagging_count", laggingFinalityCount);
1762
+ if (laggingCids.size > 0) {
1763
+ console.log(` ${laggingCids.size} chunk(s) present in best-block but finality-lagging \u2014 will NOT re-upload, waiting for GRANDPA (bounded)`);
1764
+ }
1682
1765
  const rootHashCode = 18;
1683
1766
  const rootChunkLinks = phaseB.chunks.map((c) => ({
1684
1767
  cid: createCID(c, CID_CONFIG.codec, rootHashCode),
@@ -1737,14 +1820,7 @@ async function storeDirectoryV2(directoryPath, opts = {}) {
1737
1820
  }
1738
1821
  throw e;
1739
1822
  }
1740
- const reuploadStart = Date.now();
1741
- while (Date.now() - reuploadStart < GRANDPA_REUPLOAD_TIMEOUT_MS && missingCids.size > 0) {
1742
- await new Promise((r) => setTimeout(r, GRANDPA_REUPLOAD_POLL_MS));
1743
- const poll = await probeChunks([...missingCids], { client: phaseALiveProvider.client, atFinalized: true });
1744
- for (const r of poll) {
1745
- if (r.present === true) missingCids.delete(r.cid);
1746
- }
1747
- }
1823
+ await pollUntilFinalized(missingCids, GRANDPA_REUPLOAD_TIMEOUT_MS, phaseALiveProvider.client);
1748
1824
  }
1749
1825
  if (missingCids.size > 0) {
1750
1826
  const stuck = [...missingCids][0];
@@ -1752,7 +1828,21 @@ async function storeDirectoryV2(directoryPath, opts = {}) {
1752
1828
  `Deploy verification failed: ${missingCids.size} chunk(s) not finalised after ${GRANDPA_REUPLOAD_MAX_ROUNDS} re-upload round(s) (first: ${stuck.slice(0, 20)}\u2026). The chain may have dropped chunks due to a persistent fork. Re-run deploy.`
1753
1829
  );
1754
1830
  }
1755
- console.log(` \u2713 All ${grandpaCids.length} chunks finalised after re-upload`);
1831
+ if (reuploadCount > 0) {
1832
+ console.log(` \u2713 All ${grandpaCids.length - laggingFinalityCount} chunks finalised after re-upload`);
1833
+ } else {
1834
+ console.log(` \u2713 No chunks genuinely missing from best-block \u2014 skipped re-upload entirely`);
1835
+ }
1836
+ if (laggingCids.size > 0) {
1837
+ await pollUntilFinalized(laggingCids, GRANDPA_LAGGING_WAIT_MS, phaseALiveProvider.client);
1838
+ if (laggingCids.size === 0) {
1839
+ console.log(` \u2713 finality-lagging chunk(s) caught up`);
1840
+ } else {
1841
+ console.warn(
1842
+ ` \u26A0 ${laggingCids.size} chunk(s) still not finalised after extended wait, but confirmed present in best-block \u2014 deploy succeeds; finality pending asynchronously`
1843
+ );
1844
+ }
1845
+ }
1756
1846
  }
1757
1847
  }
1758
1848
  setDeployAttribute("deploy.probe.finality_miss_reupload_count", reuploadCount);
@@ -1833,11 +1923,14 @@ async function storeDirectoryV2(directoryPath, opts = {}) {
1833
1923
  console.log(` Final root check: ${storageCid}`);
1834
1924
  const rootProbe = await probeChunks([storageCid], { client: phaseALiveProvider.client, atFinalized: true });
1835
1925
  if (rootProbe[0]?.present === false) {
1836
- throw new Error(
1837
- `Deploy verification failed: DAG-PB root ${storageCid.slice(0, 20)}\u2026 not finalised. The chain may have evicted the root extrinsic. Re-run deploy.`
1838
- );
1839
- }
1840
- if (rootProbe[0]?.present === true) {
1926
+ const { reallyMissing } = await probeFinalityGap([storageCid], { client: phaseALiveProvider.client });
1927
+ if (reallyMissing.length > 0) {
1928
+ throw new Error(
1929
+ `Deploy verification failed: DAG-PB root ${storageCid.slice(0, 20)}\u2026 not finalised and not present in best-block. The chain may have evicted the root extrinsic. Re-run deploy.`
1930
+ );
1931
+ }
1932
+ console.log(` Root confirmed present in best-block (finality-lagging, #1049) \u2014 treating as success.`);
1933
+ } else if (rootProbe[0]?.present === true) {
1841
1934
  console.log(` \u2713 Root finalised on chain`);
1842
1935
  } else {
1843
1936
  console.log(` Root re-check inconclusive (RPC error) \u2014 GRANDPA probe above already verified; continuing.`);
@@ -3008,6 +3101,7 @@ export {
3008
3101
  isBulletinAuthActive,
3009
3102
  pollUntilBulletinAuthorized,
3010
3103
  waitForBulletinAuthorization,
3104
+ withTransientRetry,
3011
3105
  getSlotSignerProvider,
3012
3106
  friendlyChainError,
3013
3107
  DEFAULT_BULLETIN_RPC,
@@ -3039,6 +3133,9 @@ export {
3039
3133
  formatStorageSignerLine,
3040
3134
  formatTransferModeStorageSignerLine,
3041
3135
  formatTransferModeDotnsLine,
3136
+ describeSlotFallbackReason,
3137
+ reconcileTimedOutChunk,
3138
+ __waitForChainLivenessForTest,
3042
3139
  storeFile,
3043
3140
  __assignDenseNoncesForTest,
3044
3141
  storeChunkedContent,
@@ -4,10 +4,10 @@ import {
4
4
  } from "./chunk-5FLTDWWP.js";
5
5
  import {
6
6
  DOT_PRODUCT_ID
7
- } from "./chunk-JWMHCW74.js";
7
+ } from "./chunk-2NF6QIDF.js";
8
8
  import {
9
9
  DEFAULT_MNEMONIC
10
- } from "./chunk-O4I37F5K.js";
10
+ } from "./chunk-5BE5PQH6.js";
11
11
 
12
12
  // src/deploy-actors.ts
13
13
  var DEFAULT_WORKER_SURI = DEFAULT_MNEMONIC;
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  VERSION
3
- } from "./chunk-53BU4X2R.js";
3
+ } from "./chunk-52F2ETEG.js";
4
4
 
5
5
  // src/version-check.ts
6
6
  import { execSync, execFileSync } from "child_process";
@@ -2,15 +2,15 @@ import {
2
2
  classifyErrorArea,
3
3
  isInteractive,
4
4
  promptYesNo
5
- } from "./chunk-7E33R3OQ.js";
5
+ } from "./chunk-VIZ6GBQE.js";
6
6
  import {
7
7
  VERSION,
8
8
  getCurrentSentryTraceId,
9
9
  resolveIssueRepoSlug
10
- } from "./chunk-53BU4X2R.js";
10
+ } from "./chunk-52F2ETEG.js";
11
11
  import {
12
12
  package_default
13
- } from "./chunk-SES7CGNY.js";
13
+ } from "./chunk-BXW5MBBY.js";
14
14
 
15
15
  // src/bug-report.ts
16
16
  import { execSync, execFileSync } from "child_process";
@@ -37,6 +37,15 @@ function isAuthorizationSufficient(auth, currentBlock) {
37
37
  function accountsNeedingAuthorization(auths, currentBlock) {
38
38
  return auths.filter((a) => !isAuthorizationSufficient(a, currentBlock));
39
39
  }
40
+ var BULLETIN_BLOCK_TIME_SECS = 6;
41
+ var BULLETIN_BLOCKS_PER_DAY = Math.floor(86400 / BULLETIN_BLOCK_TIME_SECS);
42
+ function accountsNeedingReauthorization(auths, currentBlock, bufferBlocks = BULLETIN_BLOCKS_PER_DAY) {
43
+ const deadline = currentBlock + bufferBlocks;
44
+ return auths.filter((a) => !isAuthorizationSufficient(a, deadline));
45
+ }
46
+ function isAutoReauthorizeAllowed(env) {
47
+ return env?.network !== "mainnet" && env?.bulletinAutoAuthorize === true;
48
+ }
40
49
  function selectAccount(authorizations, random = Math.random, pinnedIndex) {
41
50
  if (pinnedIndex != null) {
42
51
  const pinned = authorizations.find((a) => a.index === pinnedIndex);
@@ -164,13 +173,21 @@ async function bootstrapPool(bulletinRpc, poolSize = 10, mnemonic, opts = {}) {
164
173
  printAuthStatus(a, currentBlock);
165
174
  }
166
175
  console.log("");
167
- const needsAuth = accountsNeedingAuthorization(auths, currentBlock);
176
+ const reauthBufferBlocks = opts.reauthBufferBlocks ?? 0;
177
+ const needsAuth = accountsNeedingReauthorization(auths, currentBlock, reauthBufferBlocks);
168
178
  if (needsAuth.length === 0) {
169
179
  console.log("All pool accounts are authorized. Nothing to do.");
170
180
  return;
171
181
  }
172
182
  console.log(`${needsAuth.length} account(s) need authorization.
173
183
  `);
184
+ if (reauthBufferBlocks > 0 && !opts.allowAutoReauthorize) {
185
+ const details = needsAuth.map((a) => ` [${a.index}] ${a.address} \u2014 expires @${a.expiration} (current block ${currentBlock})`).join("\n");
186
+ throw new Error(
187
+ `Auto-reauthorize is testnet-only (see isAutoReauthorizeAllowed / environments.json bulletinAutoAuthorize) and this call was not cleared to write. ${needsAuth.length} account(s) need reauthorization within the ${reauthBufferBlocks}-block buffer:
188
+ ${details}`
189
+ );
190
+ }
174
191
  let authorizerSigner;
175
192
  const keyring = new Keyring({ type: "sr25519" });
176
193
  if (opts.authorizerMnemonic) {
@@ -228,6 +245,10 @@ export {
228
245
  derivePoolAccounts,
229
246
  isAuthorizationSufficient,
230
247
  accountsNeedingAuthorization,
248
+ BULLETIN_BLOCK_TIME_SECS,
249
+ BULLETIN_BLOCKS_PER_DAY,
250
+ accountsNeedingReauthorization,
251
+ isAutoReauthorizeAllowed,
231
252
  selectAccount,
232
253
  fetchPoolAuthorizations,
233
254
  isTestnetSpecName,
@@ -29,9 +29,51 @@ declare function _decodeStorageValue(hex: string | null | undefined): {
29
29
  index: number;
30
30
  } | null;
31
31
  declare function probeChunks(cids: string[], options: ChainProbeOptions): Promise<ChunkProbeResult[]>;
32
+ /**
33
+ * Splits cids that are missing at finalised head into two buckets, using a
34
+ * best-block probe result for each cid:
35
+ * - `lagging`: present at best-block. GRANDPA just hasn't caught up yet
36
+ * (#1049) — these were never lost and must NEVER be re-uploaded.
37
+ * - `reallyMissing`: absent at best-block too (or the best-block probe
38
+ * itself failed, `present === null`) — genuinely dropped, or we
39
+ * couldn't determine presence at all. Either way, re-upload is the
40
+ * safe default: treating an indeterminate result as "lagging" risks
41
+ * silently skipping a chunk that's actually gone.
42
+ *
43
+ * Pure function — no chain I/O — so it's directly unit-testable against a
44
+ * mocked ChunkProbeResult array without a client.
45
+ */
46
+ declare function classifyFinalityGap(missingAtFinalized: string[], bestBlockResults: ChunkProbeResult[]): {
47
+ reallyMissing: string[];
48
+ lagging: string[];
49
+ };
50
+ /**
51
+ * Composite, chain-touching version of classifyFinalityGap (#1049): probes
52
+ * `missingAtFinalized` at best-block, retries once on an indeterminate
53
+ * result (present === null), then classifies. Any caller that finds cids
54
+ * missing at finalised head should route through this — not re-implement
55
+ * the probe/retry/classify sequence inline — so the "finalised-head absence
56
+ * is not proof of loss" policy stays consistent everywhere it's checked
57
+ * (the GRANDPA finality-check phase and the pre-setContenthash root
58
+ * re-check both use it).
59
+ */
60
+ declare function probeFinalityGap(missingAtFinalized: string[], options: ChainProbeOptions): Promise<{
61
+ reallyMissing: string[];
62
+ lagging: string[];
63
+ }>;
64
+ /**
65
+ * Best (non-finalised) block height, via `chain_getHeader`. Used by the
66
+ * initial chunk-upload retry loop (#1051) to detect a frozen chain — no new
67
+ * blocks since the last check — so it can wait instead of resubmitting into
68
+ * a stall (which just manufactures same-nonce collisions once the chain
69
+ * resumes). Returns `null` on any RPC failure or malformed response; callers
70
+ * must treat `null` as "can't tell" and fail open (proceed as if live)
71
+ * rather than blocking forever on a single bad peer.
72
+ */
73
+ declare function getBestBlockNumber(client: any): Promise<number | null>;
32
74
  /** Reset session-level caches. Used in tests only. */
33
75
  declare function _resetProbeSession(): void;
34
76
  /** Pre-set metadataChecked so tests don't need a real metadata RPC mock. Used in tests only. */
35
77
  declare function _bypassMetadataCheckForTest(): void;
36
78
 
37
- export { ChainProbeCrossValidationError, ChainProbeMetadataError, type ChainProbeOptions, type ChunkProbeFailureReason, type ChunkProbeResult, _bypassMetadataCheckForTest, _decodeStorageValue, _resetProbeSession, probeChunks };
79
+ export { ChainProbeCrossValidationError, ChainProbeMetadataError, type ChainProbeOptions, type ChunkProbeFailureReason, type ChunkProbeResult, _bypassMetadataCheckForTest, _decodeStorageValue, _resetProbeSession, classifyFinalityGap, getBestBlockNumber, probeChunks, probeFinalityGap };
@@ -4,15 +4,21 @@ import {
4
4
  _bypassMetadataCheckForTest,
5
5
  _decodeStorageValue,
6
6
  _resetProbeSession,
7
- probeChunks
8
- } from "./chunk-HLXRVFKL.js";
9
- import "./chunk-53BU4X2R.js";
10
- import "./chunk-SES7CGNY.js";
7
+ classifyFinalityGap,
8
+ getBestBlockNumber,
9
+ probeChunks,
10
+ probeFinalityGap
11
+ } from "./chunk-FVPRBTHT.js";
12
+ import "./chunk-52F2ETEG.js";
13
+ import "./chunk-BXW5MBBY.js";
11
14
  export {
12
15
  ChainProbeCrossValidationError,
13
16
  ChainProbeMetadataError,
14
17
  _bypassMetadataCheckForTest,
15
18
  _decodeStorageValue,
16
19
  _resetProbeSession,
17
- probeChunks
20
+ classifyFinalityGap,
21
+ getBestBlockNumber,
22
+ probeChunks,
23
+ probeFinalityGap
18
24
  };
@@ -3,10 +3,10 @@ import {
3
3
  } from "../chunk-J7CYVTAW.js";
4
4
  import {
5
5
  waitForBulletinAuthorization
6
- } from "../chunk-UUJFLJKY.js";
6
+ } from "../chunk-OOGXTHVQ.js";
7
7
  import {
8
8
  preflightSssAllowance
9
- } from "../chunk-7RYEYWYO.js";
9
+ } from "../chunk-BH7DJ5IW.js";
10
10
  import {
11
11
  statementSigningAccount
12
12
  } from "../chunk-GRPLHUYC.js";
@@ -15,11 +15,11 @@ import "../chunk-IW3X2MJF.js";
15
15
  import "../chunk-KOSF5FDO.js";
16
16
  import "../chunk-J3NIXHZZ.js";
17
17
  import "../chunk-S7EM5VMW.js";
18
- import "../chunk-HTZGBUZA.js";
19
- import "../chunk-7E33R3OQ.js";
20
- import "../chunk-HLXRVFKL.js";
18
+ import "../chunk-W2OSH4GD.js";
19
+ import "../chunk-VIZ6GBQE.js";
20
+ import "../chunk-FVPRBTHT.js";
21
21
  import "../chunk-C2TS5MER.js";
22
- import "../chunk-QDHLTSEO.js";
22
+ import "../chunk-PXEFWMJP.js";
23
23
  import "../chunk-JQKKMUCT.js";
24
24
  import {
25
25
  BULLETIN_RESOURCE,
@@ -36,15 +36,15 @@ import {
36
36
  getAuthClient,
37
37
  getPeopleChainEndpoints,
38
38
  resolveBulletinEndpoints
39
- } from "../chunk-JWMHCW74.js";
39
+ } from "../chunk-2NF6QIDF.js";
40
40
  import {
41
41
  CLI_NAME
42
42
  } from "../chunk-TSPERKUS.js";
43
- import "../chunk-O4I37F5K.js";
43
+ import "../chunk-5BE5PQH6.js";
44
44
  import "../chunk-SI2ZUOYD.js";
45
- import "../chunk-MRQPJLPS.js";
46
- import "../chunk-53BU4X2R.js";
47
- import "../chunk-SES7CGNY.js";
45
+ import "../chunk-XVQICFTB.js";
46
+ import "../chunk-52F2ETEG.js";
47
+ import "../chunk-BXW5MBBY.js";
48
48
  import {
49
49
  loadEnvironments
50
50
  } from "../chunk-JSYQ3JQS.js";
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  clearSssAllowanceCache
3
- } from "../chunk-7RYEYWYO.js";
3
+ } from "../chunk-BH7DJ5IW.js";
4
4
  import "../chunk-GRPLHUYC.js";
5
5
  import "../chunk-JQKKMUCT.js";
6
6
  import "../chunk-5FLTDWWP.js";
@@ -9,10 +9,10 @@ import {
9
9
  } from "../chunk-RIRDBSBG.js";
10
10
  import {
11
11
  getAuthClient
12
- } from "../chunk-JWMHCW74.js";
12
+ } from "../chunk-2NF6QIDF.js";
13
13
  import "../chunk-TSPERKUS.js";
14
- import "../chunk-53BU4X2R.js";
15
- import "../chunk-SES7CGNY.js";
14
+ import "../chunk-52F2ETEG.js";
15
+ import "../chunk-BXW5MBBY.js";
16
16
  import "../chunk-JSYQ3JQS.js";
17
17
  import "../chunk-ZOC4GITL.js";
18
18
 
@@ -4,11 +4,11 @@ import {
4
4
  import {
5
5
  DEFAULT_MNEMONIC,
6
6
  DotNS
7
- } from "../chunk-O4I37F5K.js";
7
+ } from "../chunk-5BE5PQH6.js";
8
8
  import "../chunk-SI2ZUOYD.js";
9
- import "../chunk-MRQPJLPS.js";
10
- import "../chunk-53BU4X2R.js";
11
- import "../chunk-SES7CGNY.js";
9
+ import "../chunk-XVQICFTB.js";
10
+ import "../chunk-52F2ETEG.js";
11
+ import "../chunk-BXW5MBBY.js";
12
12
  import {
13
13
  getPopSelfServeConfig,
14
14
  loadEnvironments,
@@ -2,12 +2,12 @@ import {
2
2
  STALE_SESSION_MESSAGE,
3
3
  getAuthClient,
4
4
  hasPersistedSession
5
- } from "../chunk-JWMHCW74.js";
5
+ } from "../chunk-2NF6QIDF.js";
6
6
  import {
7
7
  CLI_NAME
8
8
  } from "../chunk-TSPERKUS.js";
9
- import "../chunk-53BU4X2R.js";
10
- import "../chunk-SES7CGNY.js";
9
+ import "../chunk-52F2ETEG.js";
10
+ import "../chunk-BXW5MBBY.js";
11
11
  import "../chunk-JSYQ3JQS.js";
12
12
  import "../chunk-ZOC4GITL.js";
13
13