bulletin-deploy 0.7.21 → 0.7.22-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 (56) hide show
  1. package/dist/bug-report.js +4 -4
  2. package/dist/chunk-74ETPOKH.js +284 -0
  3. package/dist/chunk-A5IQ5MKO.js +207 -0
  4. package/dist/{chunk-VTTN4BX7.js → chunk-CIXT75OF.js} +151 -2
  5. package/dist/chunk-EJ5TNGAY.js +24 -0
  6. package/dist/{chunk-RJAFD4LO.js → chunk-FSSKFXTZ.js} +1 -1
  7. package/dist/{chunk-ZY6QLNKQ.js → chunk-KFHGT4A2.js} +1 -1
  8. package/dist/{chunk-MJTQOXBC.js → chunk-LEYQOOWC.js} +3 -2
  9. package/dist/chunk-QHOZEY5X.js +231 -0
  10. package/dist/{chunk-BFXHVC23.js → chunk-SPWQNU3F.js} +21 -17
  11. package/dist/chunk-T7EEVWNU.js +32 -0
  12. package/dist/chunk-UPWEOGLQ.js +37 -0
  13. package/dist/{chunk-SA37SLYF.js → chunk-V5NUBOEX.js} +2 -2
  14. package/dist/{chunk-2VNGK2MU.js → chunk-WNUWAA6F.js} +26 -3
  15. package/dist/{chunk-4TS6R26J.js → chunk-WVCIU6WM.js} +22 -9
  16. package/dist/{chunk-G2P5UIPX.js → chunk-XWA3NPMX.js} +4 -3
  17. package/dist/chunk-ZYVGHDMU.js +117 -0
  18. package/dist/chunk-probe.js +3 -3
  19. package/dist/deploy.d.ts +4 -2
  20. package/dist/deploy.js +9 -9
  21. package/dist/dotns.d.ts +74 -1
  22. package/dist/dotns.js +8 -4
  23. package/dist/incremental-stats.d.ts +3 -1
  24. package/dist/incremental-stats.js +1 -1
  25. package/dist/index.d.ts +4 -0
  26. package/dist/index.js +9 -9
  27. package/dist/memory-report.js +2 -2
  28. package/dist/merkle.js +9 -9
  29. package/dist/personhood/bind-paid-alias.d.ts +43 -0
  30. package/dist/personhood/bind-paid-alias.js +10 -0
  31. package/dist/personhood/bind-personal-id.d.ts +55 -0
  32. package/dist/personhood/bind-personal-id.js +12 -0
  33. package/dist/personhood/bootstrap.d.ts +85 -0
  34. package/dist/personhood/bootstrap.js +245 -0
  35. package/dist/personhood/claim-pgas.d.ts +61 -0
  36. package/dist/personhood/claim-pgas.js +12 -0
  37. package/dist/personhood/constants.d.ts +23 -0
  38. package/dist/personhood/constants.js +22 -0
  39. package/dist/personhood/encoding.d.ts +49 -0
  40. package/dist/personhood/encoding.js +24 -0
  41. package/dist/personhood/hashing.d.ts +4 -0
  42. package/dist/personhood/hashing.js +8 -0
  43. package/dist/personhood/member-key.d.ts +12 -0
  44. package/dist/personhood/member-key.js +10 -0
  45. package/dist/personhood/people-client.d.ts +14 -0
  46. package/dist/personhood/people-client.js +48 -0
  47. package/dist/personhood/reprove.d.ts +43 -0
  48. package/dist/personhood/reprove.js +225 -0
  49. package/dist/pool.d.ts +7 -2
  50. package/dist/pool.js +3 -1
  51. package/dist/run-state.js +1 -1
  52. package/dist/telemetry.d.ts +7 -1
  53. package/dist/telemetry.js +8 -2
  54. package/dist/version-check.js +3 -3
  55. package/docs/e2e-bootstrap.md +36 -10
  56. package/package.json +4 -3
@@ -1,11 +1,11 @@
1
1
  import {
2
2
  isTestnetSpecName
3
- } from "./chunk-4TS6R26J.js";
3
+ } from "./chunk-WVCIU6WM.js";
4
4
  import {
5
5
  captureWarning,
6
6
  setDeployAttribute,
7
7
  withSpan
8
- } from "./chunk-2VNGK2MU.js";
8
+ } from "./chunk-WNUWAA6F.js";
9
9
 
10
10
  // src/dotns.ts
11
11
  import crypto from "crypto";
@@ -236,6 +236,7 @@ function formatWeight(weight) {
236
236
  if (!weight) return "unknown";
237
237
  return `ref_time=${weight.referenceTime.toString()} proof_size=${weight.proofSize.toString()}`;
238
238
  }
239
+ var BARE_REVERT_DIAGNOSTIC_FUNCTIONS = /* @__PURE__ */ new Set(["register", "commit", "setContenthash", "setSubnodeOwner", "setResolver"]);
239
240
  function formatContractDryRunFailure(gasEstimate, context) {
240
241
  const functionName = context.functionName ?? "unknown";
241
242
  const contractName = dotnsContractName(context.contractAddress, context.contracts);
@@ -253,8 +254,23 @@ function formatContractDryRunFailure(gasEstimate, context) {
253
254
  lines.push(` calldata: ${context.encodedData}`);
254
255
  if (context.args) lines.push(` args: ${stringifyDebugValue(context.args)}`);
255
256
  }
257
+ const revertData = gasEstimate.revertData;
258
+ const isBareRevert = (revertData === void 0 || revertData.trim() === "0x") && gasEstimate.revertFlags === 1n;
259
+ if (isBareRevert && BARE_REVERT_DIAGNOSTIC_FUNCTIONS.has(functionName)) {
260
+ lines.push(
261
+ ` diagnostic: bare-revert (empty 0x). Most common causes:`,
262
+ ` 1. Origin not actually mapped on-chain. Try BULLETIN_DEPLOY_DEBUG_DOTNS_TX=1 to log calldata, then run`,
263
+ ` \`node tools/dotns-dry-run.mjs <label>\` against the env's Asset Hub RPC to reproduce in isolation.`,
264
+ ` 2. Contract precondition (e.g. commitment too new). dotns-dry-run.mjs will surface the contract-side state.`,
265
+ ` hint: prepend --fresh to dotns-dry-run.mjs to use a brand-new unmapped substrate origin \u2014`,
266
+ ` if the revert ONLY reproduces with --fresh, it's the account-mapping bug.`
267
+ );
268
+ }
256
269
  return lines.join("\n");
257
270
  }
271
+ function __formatContractDryRunFailureForTest(gasEstimate, context) {
272
+ return formatContractDryRunFailure(gasEstimate, context);
273
+ }
258
274
  function unwrapExecutionResult(rawResult) {
259
275
  if (!rawResult) return { ok: null, err: null, successFlag: null };
260
276
  if (typeof rawResult.success === "boolean") {
@@ -579,6 +595,17 @@ var ReviveClientWrapper = class _ReviveClientWrapper {
579
595
  async submitTransaction(contractAddress, value, encodedData, signerSubstrateAddress, signer, statusCallback, { rpcs, useNoncePolling, functionName, args, contracts }) {
580
596
  await this.ensureAccountMapped(signerSubstrateAddress, signer);
581
597
  const signerEvmAddress = await this.getEvmAddress(signerSubstrateAddress);
598
+ if (functionName === "register") {
599
+ try {
600
+ const stillMapped = await this.checkIfAccountMapped(signerSubstrateAddress);
601
+ if (!stillMapped) {
602
+ captureWarning("account mapping not confirmed on-chain immediately before register dry-run", {
603
+ signer: signerSubstrateAddress
604
+ });
605
+ }
606
+ } catch {
607
+ }
608
+ }
582
609
  const gasEstimate = await this.estimateGasForCall(signerSubstrateAddress, contractAddress, value, encodedData);
583
610
  if (!gasEstimate.success) {
584
611
  throw new Error(formatContractDryRunFailure(gasEstimate, {
@@ -608,6 +635,22 @@ var ReviveClientWrapper = class _ReviveClientWrapper {
608
635
  return await this.signAndSubmitWithRetry(buildExtrinsic, signer, statusCallback, "Revive.call", { nonceFallback });
609
636
  }
610
637
  };
638
+ var DOTNS_CONTEXT_HEX_LOWER = "0x646f746e73000000000000000000000000000000000000000000000000000000";
639
+ function formatPersonhoodRemediation(state, environmentId) {
640
+ if (environmentId !== "paseo-next-v2") {
641
+ return "Self-attestation is no longer available. Contact the DotNS team for whitelisting / Personhood status help.";
642
+ }
643
+ switch (state.state) {
644
+ case "not-bound":
645
+ return "Your account has no DotNS alias binding on paseo-next-v2. Visit https://sudo.personhood.dev/dotns-bootstrap to complete the recognition and binding steps, or run `tools/personhood-bootstrap.mjs --mnemonic <your-mnemonic>` once Stage 2 of the bootstrap tooling lands.";
646
+ case "bound-likely-stale":
647
+ return "Your alias binding exists but may have a stale ring revision. Run `node tools/reprove-alias.mjs --mnemonic <your-mnemonic> --env paseo-next-v2` to refresh the proof, then retry the registration.";
648
+ case "wrong-context":
649
+ return "Your alias binding exists but is for a different application context" + (state.storedContextHex ? ` (context: ${state.storedContextHex})` : "") + ". Re-bind under the 'dotns' context using the bootstrap flow: https://sudo.personhood.dev/dotns-bootstrap";
650
+ case "bound-fresh":
651
+ return "Self-attestation is no longer available. Contact the DotNS team for whitelisting / Personhood status help.";
652
+ }
653
+ }
611
654
  var DotNS = class {
612
655
  client;
613
656
  clientWrapper;
@@ -623,6 +666,7 @@ var DotNS = class {
623
666
  _usesExternalSigner = false;
624
667
  _contracts = CONTRACTS;
625
668
  _nativeToEthRatio = NATIVE_TO_ETH_RATIO;
669
+ _environmentId = null;
626
670
  constructor() {
627
671
  this.client = null;
628
672
  this.clientWrapper = null;
@@ -640,6 +684,9 @@ var DotNS = class {
640
684
  if (options.contracts && Object.keys(options.contracts).length > 0) {
641
685
  this._contracts = { ...CONTRACTS, ...options.contracts };
642
686
  }
687
+ if (options.environmentId) {
688
+ this._environmentId = options.environmentId;
689
+ }
643
690
  const rpc = options.rpc || process.env.DOTNS_RPC || this.assetHubEndpoints[0];
644
691
  this.rpc = rpc;
645
692
  this._usesExternalSigner = Boolean(options.signer && options.signerAddress);
@@ -788,6 +835,31 @@ var DotNS = class {
788
835
  this._testnetCache = isTestnetSpecName(rpc) || rpc.includes("paseo") || rpc.includes("westend") || rpc.includes("rococo");
789
836
  return this._testnetCache;
790
837
  }
838
+ /**
839
+ * Classify the AliasAccounts state for a substrate address.
840
+ * Only called on paseo-next-v2 testnets inside the preflight's NoStatus branch.
841
+ * Returns "not-bound" if the chain is unreachable (safe fallback to generic advice).
842
+ */
843
+ async classifyAliasAccountState(ss58) {
844
+ if (!this.clientWrapper) return { state: "not-bound" };
845
+ try {
846
+ const api = this.clientWrapper.client;
847
+ const row = await api.query.AliasAccounts.AccountToAlias.getValue(ss58, { at: "best" });
848
+ if (!row) return { state: "not-bound" };
849
+ const contextHex = typeof row.ca?.context === "string" ? row.ca.context.toLowerCase() : "";
850
+ const paid = Boolean(row.paid);
851
+ const revision = Number(row.revision ?? 0);
852
+ if (paid && contextHex === DOTNS_CONTEXT_HEX_LOWER) {
853
+ return { state: "bound-likely-stale", storedContextHex: contextHex, paid, revision };
854
+ }
855
+ if (!paid || contextHex !== DOTNS_CONTEXT_HEX_LOWER) {
856
+ return { state: "wrong-context", storedContextHex: contextHex, paid, revision };
857
+ }
858
+ return { state: "bound-fresh", storedContextHex: contextHex, paid, revision };
859
+ } catch {
860
+ return { state: "not-bound" };
861
+ }
862
+ }
791
863
  // Free PAS balance for a substrate address on the connected DotNS chain.
792
864
  // Used by preflight to gate the deploy on whether the signer can pay tx
793
865
  // fees before the chunk upload runs. Returns 0n if the account doesn't
@@ -1342,6 +1414,30 @@ var DotNS = class {
1342
1414
  signerFreeBalance
1343
1415
  };
1344
1416
  }
1417
+ if (userStatus === ProofOfPersonhoodStatus.NoStatus && isTestnet && this._environmentId === "paseo-next-v2" && this.substrateAddress) {
1418
+ const aliasState = await this.classifyAliasAccountState(this.substrateAddress);
1419
+ const remediationMessage = formatPersonhoodRemediation(aliasState, this._environmentId);
1420
+ const currentName2 = popStatusName(userStatus);
1421
+ const requiredName2 = popStatusName(classification.status);
1422
+ return {
1423
+ label: validated,
1424
+ classification,
1425
+ userStatus,
1426
+ trailingDigits,
1427
+ baselength,
1428
+ isAvailable: true,
1429
+ existingOwner: null,
1430
+ isBaseNameReserved: isReserved,
1431
+ reservationOwner,
1432
+ isTestnet,
1433
+ canProceed: false,
1434
+ reason: `${validated}.dot requires ${requiredName2}, but this signer is ${currentName2}. ${remediationMessage}`,
1435
+ plannedAction: "abort",
1436
+ needsPopUpgrade: false,
1437
+ targetPopStatus,
1438
+ signerFreeBalance
1439
+ };
1440
+ }
1345
1441
  const currentName = popStatusName(userStatus);
1346
1442
  const requiredName = popStatusName(classification.status);
1347
1443
  return {
@@ -1471,6 +1567,57 @@ var DotNS = class {
1471
1567
  return { label, owner: this.evmAddress };
1472
1568
  });
1473
1569
  }
1570
+ /**
1571
+ * Reprove a stale DotNS alias binding.
1572
+ * Opens a People-chain client internally, builds the ring proof, and submits
1573
+ * reprove_alias_account on AH. Use when the alias exists but the ring root
1574
+ * has advanced past the stored revision.
1575
+ *
1576
+ * Requires a mnemonic — the DotNS instance must have been connected with one.
1577
+ */
1578
+ async reprove(mnemonic) {
1579
+ this.ensureConnected();
1580
+ if (!this.substrateAddress || !this.signer) {
1581
+ throw new Error("reprove: DotNS must be connected with a signer");
1582
+ }
1583
+ const envId = this._environmentId ?? "paseo-next-v2";
1584
+ const { connectPeopleClient } = await import("./personhood/people-client.js");
1585
+ const { reproveAliasToAccount } = await import("./personhood/reprove.js");
1586
+ const { deriveMemberEntropy, deriveMemberKey } = await import("./personhood/member-key.js");
1587
+ const verifiable = await import("verifiablejs/nodejs");
1588
+ const memberEntropy = deriveMemberEntropy(mnemonic);
1589
+ const memberKey = deriveMemberKey(mnemonic);
1590
+ const peopleConn = await connectPeopleClient(envId);
1591
+ try {
1592
+ const result = await reproveAliasToAccount({
1593
+ peopleUnsafeApi: peopleConn.unsafeApi,
1594
+ ahUnsafeApi: this.clientWrapper.client,
1595
+ account: this.substrateAddress,
1596
+ memberKey,
1597
+ signCall: this.signer,
1598
+ buildRingProof: async ({ members, context, msg }) => {
1599
+ const r = verifiable.one_shot(memberEntropy, members, context, msg);
1600
+ return { proof: r.proof, alias: r.alias };
1601
+ }
1602
+ });
1603
+ return result;
1604
+ } finally {
1605
+ peopleConn.disconnect();
1606
+ }
1607
+ }
1608
+ /**
1609
+ * Run the personhood bootstrap flow for this DotNS signer.
1610
+ * Idempotent: each step is gated on chain state being "still needs doing".
1611
+ * Does NOT auto-run from preflight — call explicitly.
1612
+ *
1613
+ * Throws RecognizeRequiredError if the account hasn't been recognized by the
1614
+ * personhood faucet (https://sudo.personhood.dev/personhood-faucet).
1615
+ */
1616
+ async bootstrap(mnemonic) {
1617
+ const { runBootstrap } = await import("./personhood/bootstrap.js");
1618
+ const envId = this._environmentId ?? "paseo-next-v2";
1619
+ return runBootstrap({ mnemonic, environmentId: envId });
1620
+ }
1474
1621
  disconnect() {
1475
1622
  if (this.client) {
1476
1623
  this.client.destroy();
@@ -1503,6 +1650,7 @@ export {
1503
1650
  verifyNonceAdvanced,
1504
1651
  ProofOfPersonhoodStatus,
1505
1652
  convertToHexString,
1653
+ __formatContractDryRunFailureForTest,
1506
1654
  DOT_NODE,
1507
1655
  convertWeiToNative,
1508
1656
  computeDomainTokenId,
@@ -1517,6 +1665,7 @@ export {
1517
1665
  parseDomainName,
1518
1666
  parseProofOfPersonhoodStatus,
1519
1667
  popStatusName,
1668
+ formatPersonhoodRemediation,
1520
1669
  DotNS,
1521
1670
  dotns
1522
1671
  };
@@ -0,0 +1,24 @@
1
+ import {
2
+ blake2b256Keyed
3
+ } from "./chunk-UPWEOGLQ.js";
4
+ import {
5
+ MEMBER_ENTROPY_KEY
6
+ } from "./chunk-T7EEVWNU.js";
7
+
8
+ // src/personhood/member-key.ts
9
+ import { mnemonicToEntropy } from "@polkadot-labs/hdkd-helpers";
10
+ import * as verifiable from "verifiablejs/nodejs";
11
+ function deriveMemberEntropy(mnemonic) {
12
+ const normalized = mnemonic.trim().split(/\s+/).join(" ");
13
+ const bip39Entropy = mnemonicToEntropy(normalized);
14
+ return blake2b256Keyed(bip39Entropy, MEMBER_ENTROPY_KEY);
15
+ }
16
+ function deriveMemberKey(mnemonic) {
17
+ const entropy = deriveMemberEntropy(mnemonic);
18
+ return verifiable.member_from_entropy(entropy);
19
+ }
20
+
21
+ export {
22
+ deriveMemberEntropy,
23
+ deriveMemberKey
24
+ };
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  VERSION
3
- } from "./chunk-2VNGK2MU.js";
3
+ } from "./chunk-WNUWAA6F.js";
4
4
 
5
5
  // src/version-check.ts
6
6
  import { execSync, execFileSync } from "child_process";
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  captureWarning
3
- } from "./chunk-2VNGK2MU.js";
3
+ } from "./chunk-WNUWAA6F.js";
4
4
 
5
5
  // src/chunk-probe.ts
6
6
  import { Twox128, Blake2128Concat, decAnyMetadata, unifyMetadata } from "@polkadot-api/substrate-bindings";
@@ -6,7 +6,6 @@ function countByReason(probe, reason) {
6
6
  function computeStats(input) {
7
7
  const present = input.probeResults.filter((r) => r.present === true);
8
8
  const failed = input.probeResults.filter((r) => r.present === null);
9
- const presentInPrev = present.filter((r) => input.prevChunks[r.cid] != null);
10
9
  const recycled = present.filter((r) => input.prevChunks[r.cid] == null);
11
10
  return {
12
11
  manifestSource: input.manifestSource,
@@ -25,6 +24,7 @@ function computeStats(input) {
25
24
  probeFailedMetadata: countByReason(input.probeResults, "metadata_error"),
26
25
  recycledCids: recycled.length,
27
26
  retentionPeriodBlocks: input.retentionPeriodBlocks,
27
+ bytesProbePresent: input.bytesProbePresent,
28
28
  bytesSkipped: input.bytesSkipped,
29
29
  bytesUploaded: input.bytesUploaded,
30
30
  chunksTotal: input.chunksTotal,
@@ -34,7 +34,7 @@ function computeStats(input) {
34
34
  section0Bytes: input.sectionSizes.section0,
35
35
  section1Bytes: input.sectionSizes.section1,
36
36
  section2Bytes: input.sectionSizes.section2,
37
- estimatedSecondsSaved: Math.round(SECONDS_PER_PROBE_SKIP * presentInPrev.length),
37
+ estimatedSecondsSaved: Math.round(SECONDS_PER_PROBE_SKIP * present.length),
38
38
  tier2VerifiedCount: input.tier2VerifiedCount,
39
39
  tier2InconclusiveCount: input.tier2InconclusiveCount,
40
40
  tier2FallbackCount: input.tier2FallbackCount
@@ -63,6 +63,7 @@ function telemetryAttributes(s) {
63
63
  "deploy.cache.chunks_total": String(s.chunksTotal),
64
64
  "deploy.cache.chunks_uploaded": String(s.chunksUploaded),
65
65
  "deploy.cache.chunks_skipped": String(s.chunksSkipped),
66
+ "deploy.cache.bytes_probe_present": String(s.bytesProbePresent),
66
67
  "deploy.cache.bytes_skipped": String(s.bytesSkipped),
67
68
  "deploy.cache.bytes_uploaded": String(s.bytesUploaded),
68
69
  "deploy.cache.car_bytes": String(s.carBytes),
@@ -0,0 +1,231 @@
1
+ import {
2
+ blake2_256,
3
+ bytesToHex,
4
+ concatBytes,
5
+ encodeMembers,
6
+ hexToBytes
7
+ } from "./chunk-ZYVGHDMU.js";
8
+ import {
9
+ PAID_PROOF_TAG,
10
+ PEOPLE_MEMBER_IDENTIFIER_HEX,
11
+ PGAS_ASSET_ID,
12
+ PGAS_ASSET_LOCATION,
13
+ PROOF_BYTES
14
+ } from "./chunk-T7EEVWNU.js";
15
+
16
+ // src/personhood/bind-paid-alias.ts
17
+ import { getSs58AddressInfo } from "polkadot-api";
18
+ var PaidAliasBindingError = class extends Error {
19
+ kind;
20
+ dispatchError;
21
+ constructor(message, options = {}) {
22
+ super(message, options);
23
+ this.name = "PaidAliasBindingError";
24
+ this.kind = options.kind ?? "Unknown";
25
+ this.dispatchError = options.dispatchError;
26
+ }
27
+ };
28
+ var bindPaidAliasToAccount = async ({
29
+ peopleUnsafeApi,
30
+ ahUnsafeApi,
31
+ account,
32
+ memberKey,
33
+ contextBytes,
34
+ signCall,
35
+ buildRingProof,
36
+ progress
37
+ }) => {
38
+ const people = peopleUnsafeApi;
39
+ const ah = ahUnsafeApi;
40
+ if (memberKey.length !== 32) {
41
+ throw new PaidAliasBindingError("memberKey must be 32 bytes", {
42
+ kind: "ClientError"
43
+ });
44
+ }
45
+ if (contextBytes.length !== 32) {
46
+ throw new PaidAliasBindingError("contextBytes must be 32 bytes", {
47
+ kind: "ClientError"
48
+ });
49
+ }
50
+ const fee = await ah.query.AliasAccounts.PaidAliasFee.getValue({ at: "best" });
51
+ if (fee === void 0) {
52
+ throw new PaidAliasBindingError(
53
+ "AliasAccounts.PaidAliasFee is unset \u2014 needs sudo `set_paid_alias_fee`",
54
+ { kind: "PaidAliasFeeUnset" }
55
+ );
56
+ }
57
+ const pgas = await ah.query.Assets.Account.getValue(
58
+ PGAS_ASSET_ID,
59
+ account,
60
+ { at: "best" }
61
+ );
62
+ const pgasBalance = pgas?.balance ?? 0n;
63
+ if (pgasBalance < fee) {
64
+ throw new PaidAliasBindingError(
65
+ `signer has ${pgasBalance.toString()} PGAS but PaidAliasFee is ${fee.toString()} \u2014 claim PGAS first`,
66
+ { kind: "InsufficientPgas" }
67
+ );
68
+ }
69
+ const position = await people.query.Members.Members.getValue(
70
+ PEOPLE_MEMBER_IDENTIFIER_HEX,
71
+ bytesToHex(memberKey),
72
+ { at: "best" }
73
+ );
74
+ if (!position || position.type !== "Included") {
75
+ throw new PaidAliasBindingError(
76
+ `member position is '${position?.type ?? "absent"}', expected 'Included'`,
77
+ { kind: "NotARecognizedPerson" }
78
+ );
79
+ }
80
+ const ringIndex = position.value.ring_index;
81
+ const allEntries = await people.query.Members.RingKeys.getEntries({
82
+ at: "best"
83
+ });
84
+ const pages = [];
85
+ const identHex = PEOPLE_MEMBER_IDENTIFIER_HEX.toLowerCase();
86
+ for (const entry of allEntries) {
87
+ if (entry.keyArgs[0].toLowerCase() !== identHex) continue;
88
+ if (Number(entry.keyArgs[1]) !== ringIndex) continue;
89
+ pages.push([Number(entry.keyArgs[2]), [...entry.value]]);
90
+ }
91
+ pages.sort((a, b) => a[0] - b[0]);
92
+ const ringKeys = pages.flatMap(([, ks]) => ks);
93
+ if (ringKeys.length === 0) {
94
+ throw new PaidAliasBindingError("ring has no members on People", {
95
+ kind: "ClientError"
96
+ });
97
+ }
98
+ const membersBytes = encodeMembers(ringKeys.map((k) => hexToBytes(k)));
99
+ const collectionId = await ah.constants.AliasAccounts.PeopleCollectionIdentifier();
100
+ const ringExp = await ah.constants.AliasAccounts.PeopleRingExponent();
101
+ const ringExponent = ringExp.type === "R2e9" ? 9 : ringExp.type === "R2e10" ? 10 : 14;
102
+ const ringRoots = await ah.query.MembersSubscriber.RingRoots.getValue(
103
+ collectionId,
104
+ ringIndex,
105
+ { at: "best" }
106
+ );
107
+ if (!ringRoots || ringRoots.length === 0) {
108
+ throw new PaidAliasBindingError(
109
+ "AH has no RingRoots for this (collection, ring_index)",
110
+ { kind: "RingRootNotFound" }
111
+ );
112
+ }
113
+ const latest = ringRoots[ringRoots.length - 1];
114
+ const revision = latest.revision;
115
+ const ss58Info = getSs58AddressInfo(account);
116
+ if (!ss58Info.isValid) {
117
+ throw new PaidAliasBindingError(`invalid SS58: ${account}`, {
118
+ kind: "ClientError"
119
+ });
120
+ }
121
+ const paidMsg = blake2_256(concatBytes(PAID_PROOF_TAG, ss58Info.publicKey));
122
+ const { proof, alias } = await buildRingProof({
123
+ ringExponent,
124
+ members: membersBytes,
125
+ context: contextBytes,
126
+ msg: paidMsg
127
+ });
128
+ if (proof.length !== PROOF_BYTES) {
129
+ throw new PaidAliasBindingError(
130
+ `ring proof must be ${PROOF_BYTES} bytes, got ${proof.length}`,
131
+ { kind: "ClientError" }
132
+ );
133
+ }
134
+ const tx = ah.tx.AliasAccounts.set_paid_alias_account({
135
+ proof: bytesToHex(proof),
136
+ collection: collectionId,
137
+ ring_index: ringIndex,
138
+ ring_revision: revision,
139
+ context: bytesToHex(contextBytes)
140
+ });
141
+ const submitOptions = {
142
+ customSignedExtensions: {
143
+ ChargeAssetTxPayment: {
144
+ value: { tip: 0n, asset_id: PGAS_ASSET_LOCATION }
145
+ }
146
+ }
147
+ };
148
+ const blockHash = await new Promise((resolve, reject) => {
149
+ let settled = false;
150
+ const fail = (err) => {
151
+ if (settled) return;
152
+ settled = true;
153
+ reject(err);
154
+ };
155
+ const succeed = (h) => {
156
+ if (settled) return;
157
+ settled = true;
158
+ resolve(h);
159
+ };
160
+ tx.signSubmitAndWatch(signCall, submitOptions).subscribe({
161
+ next: (event) => {
162
+ if (settled) return;
163
+ const ev = event;
164
+ if (ev.type === "broadcasted") {
165
+ progress?.onBroadcasted?.();
166
+ return;
167
+ }
168
+ if (ev.type === "txBestBlocksState" && ev.found) {
169
+ if (ev.ok === false) {
170
+ fail(
171
+ new PaidAliasBindingError(
172
+ "set_paid_alias_account dispatched but failed in-block",
173
+ {
174
+ kind: narrowDispatchError(ev.dispatchError),
175
+ dispatchError: ev.dispatchError
176
+ }
177
+ )
178
+ );
179
+ return;
180
+ }
181
+ if (ev.block) progress?.onBestBlock?.(ev.block.hash);
182
+ return;
183
+ }
184
+ if (ev.type === "finalized") {
185
+ if (ev.ok === false) {
186
+ fail(
187
+ new PaidAliasBindingError(
188
+ "set_paid_alias_account failed at finalization",
189
+ {
190
+ kind: narrowDispatchError(ev.dispatchError),
191
+ dispatchError: ev.dispatchError
192
+ }
193
+ )
194
+ );
195
+ return;
196
+ }
197
+ if (ev.block) succeed(ev.block.hash);
198
+ }
199
+ },
200
+ error: (err) => {
201
+ fail(
202
+ err instanceof PaidAliasBindingError ? err : new PaidAliasBindingError(
203
+ err instanceof Error ? `RPC rejected extrinsic: ${err.message}` : "RPC error during submitAndWatch",
204
+ { cause: err, kind: "RpcError" }
205
+ )
206
+ );
207
+ }
208
+ });
209
+ });
210
+ return { blockHash, alias };
211
+ };
212
+ var narrowDispatchError = (dispatchError) => {
213
+ if (typeof dispatchError === "object" && dispatchError !== null && "type" in dispatchError) {
214
+ const d = dispatchError;
215
+ if (d.type === "Module" && typeof d.value === "object" && d.value !== null) {
216
+ const v = d.value;
217
+ if (v.type === "AliasAccounts" && v.value?.type === "BadProof") {
218
+ return "BadProof";
219
+ }
220
+ if (v.type === "AliasAccounts" && v.value?.type === "PaidAliasFeeUnset") {
221
+ return "PaidAliasFeeUnset";
222
+ }
223
+ }
224
+ }
225
+ return "DispatchError";
226
+ };
227
+
228
+ export {
229
+ PaidAliasBindingError,
230
+ bindPaidAliasToAccount
231
+ };
@@ -2,7 +2,7 @@ import {
2
2
  computeStats,
3
3
  renderSummary,
4
4
  telemetryAttributes
5
- } from "./chunk-MJTQOXBC.js";
5
+ } from "./chunk-LEYQOOWC.js";
6
6
  import {
7
7
  finaliseEmbeddedManifest,
8
8
  writeEmbeddedManifestPlaceholder
@@ -20,10 +20,10 @@ import {
20
20
  } from "./chunk-S7EM5VMW.js";
21
21
  import {
22
22
  setDeployContext
23
- } from "./chunk-SA37SLYF.js";
23
+ } from "./chunk-V5NUBOEX.js";
24
24
  import {
25
25
  probeChunks
26
- } from "./chunk-ZY6QLNKQ.js";
26
+ } from "./chunk-KFHGT4A2.js";
27
27
  import {
28
28
  packSection
29
29
  } from "./chunk-C2TS5MER.js";
@@ -34,7 +34,7 @@ import {
34
34
  parseDomainName,
35
35
  popStatusName,
36
36
  verifyNonceAdvanced
37
- } from "./chunk-VTTN4BX7.js";
37
+ } from "./chunk-CIXT75OF.js";
38
38
  import {
39
39
  derivePoolAccounts,
40
40
  detectTestnet,
@@ -42,7 +42,7 @@ import {
42
42
  fetchPoolAuthorizations,
43
43
  selectAccount,
44
44
  topUpBy
45
- } from "./chunk-4TS6R26J.js";
45
+ } from "./chunk-WVCIU6WM.js";
46
46
  import {
47
47
  VERSION,
48
48
  captureWarning,
@@ -56,7 +56,7 @@ import {
56
56
  truncateAddress,
57
57
  withDeploySpan,
58
58
  withSpan
59
- } from "./chunk-2VNGK2MU.js";
59
+ } from "./chunk-WNUWAA6F.js";
60
60
  import {
61
61
  DEFAULT_ENV_ID,
62
62
  loadEnvironments,
@@ -395,7 +395,7 @@ function assignDenseNonces(stored, startNonce) {
395
395
  return nonces;
396
396
  }
397
397
  var __assignDenseNoncesForTest = assignDenseNonces;
398
- async function storeChunkedContent(chunks, { client: existingClient, unsafeApi: existingApi, signer: existingSigner, ss58: existingSS58, reconnect, fetchNonce: fetchNonceOverride, skipCids, probeFailedCids, gateway: providerGateway } = {}) {
398
+ async function storeChunkedContent(chunks, { client: existingClient, unsafeApi: existingApi, signer: existingSigner, ss58: existingSS58, reconnect, fetchNonce: fetchNonceOverride, skipCids, probeFailedCids, gateway: providerGateway, bulletinAuthorizeV2 } = {}) {
399
399
  const _fetchNonce = fetchNonceOverride ?? fetchNonce;
400
400
  console.log(`
401
401
  Chunks: ${chunks.length}`);
@@ -461,11 +461,13 @@ async function storeChunkedContent(chunks, { client: existingClient, unsafeApi:
461
461
  const bytesRemaining = uploadAuth ? BigInt(uploadAuth.extent.bytes_allowance) - BigInt(uploadAuth.extent.bytes) : 0n;
462
462
  const isAuthorized = uploadAuth !== void 0 && Number(uploadAuth.expiration ?? 0) > currentBlockNum;
463
463
  if (!isAuthorized || txsRemaining < requiredTxs || bytesRemaining < requiredBytes) {
464
+ const txsRemainingDisplay = txsRemaining < 0n ? 0n : txsRemaining;
465
+ const bytesRemainingDisplay = bytesRemaining < 0n ? 0n : bytesRemaining;
464
466
  console.log(`
465
- Account needs re-authorization (authorized=${isAuthorized}, need ${requiredTxs} txs / ${(totalBytes / 1e6).toFixed(1)}MB, have ${txsRemaining} txs / ${Number(bytesRemaining) / 1e6}MB)`);
467
+ Account needs re-authorization (authorized=${isAuthorized}, need ${requiredTxs} txs / ${(totalBytes / 1e6).toFixed(1)}MB, have ${txsRemainingDisplay} txs / ${(Number(bytesRemainingDisplay) / 1e6).toFixed(2)}MB)`);
466
468
  console.log(` Attempting to re-authorize with Alice...`);
467
469
  try {
468
- await ensureAuthorized(unsafeApi, ss58);
470
+ await ensureAuthorized(unsafeApi, ss58, void 0, bulletinAuthorizeV2, { txs: requiredTxs, bytes: requiredBytes });
469
471
  console.log(` Re-authorization successful`);
470
472
  } catch (e) {
471
473
  throw new NonRetryableError(`Account ${ss58} has insufficient Bulletin authorization and auto-authorization via Alice failed (${e.message}). Authorize the account on-chain.`);
@@ -1024,11 +1026,11 @@ async function storeDirectoryV2(directoryPath, opts = {}) {
1024
1026
  const probe = carChunkCidsA.length > 0 ? await withSpan("deploy.chain-probe", "2. chain probe", { "deploy.probe.total": carChunkCidsA.length }, () => probeChunks(carChunkCidsA, { client: provider.client })) : [];
1025
1027
  const skipCids = /* @__PURE__ */ new Set();
1026
1028
  const probeFailedCidsA = /* @__PURE__ */ new Set();
1027
- let bytesSkipped = 0;
1029
+ let bytesProbePresent = 0;
1028
1030
  for (let i = 0; i < probe.length; i++) {
1029
1031
  if (probe[i].present === true) {
1030
1032
  skipCids.add(carChunkCidsA[i]);
1031
- bytesSkipped += carChunksA[i].length;
1033
+ bytesProbePresent += carChunksA[i].length;
1032
1034
  } else if (probe[i].present === null) {
1033
1035
  probeFailedCidsA.add(carChunkCidsA[i]);
1034
1036
  }
@@ -1165,6 +1167,7 @@ async function storeDirectoryV2(directoryPath, opts = {}) {
1165
1167
  probeResults: probe,
1166
1168
  prevChunks: prevManifest?.chunks ?? {},
1167
1169
  retentionPeriodBlocks,
1170
+ bytesProbePresent,
1168
1171
  bytesSkipped: bytesSkippedB,
1169
1172
  bytesUploaded: bytesUploadedB,
1170
1173
  chunksTotal: phaseB.chunks.length,
@@ -1182,15 +1185,16 @@ async function storeDirectoryV2(directoryPath, opts = {}) {
1182
1185
  console.log("\n" + renderSummary(stats));
1183
1186
  return { storageCid, ipfsCid: phaseB.cid, carBytes: phaseB.carBytes };
1184
1187
  }
1185
- function resolveDotnsConnectOptions(options, assetHubEndpoints, autoAccountMapping, contracts, nativeToEthRatio) {
1188
+ function resolveDotnsConnectOptions(options, assetHubEndpoints, autoAccountMapping, contracts, nativeToEthRatio, environmentId) {
1186
1189
  const tail = assetHubEndpoints && assetHubEndpoints.length > 0 ? { assetHubEndpoints } : {};
1187
1190
  const mappingTail = autoAccountMapping ? { autoAccountMapping } : {};
1188
1191
  const contractsTail = contracts && Object.keys(contracts).length > 0 ? { contracts } : {};
1189
1192
  const ratioTail = nativeToEthRatio ? { nativeToEthRatio } : {};
1193
+ const envTail = environmentId ? { environmentId } : {};
1190
1194
  if (options.signer && options.signerAddress) {
1191
- return { signer: options.signer, signerAddress: options.signerAddress, ...tail, ...mappingTail, ...contractsTail, ...ratioTail };
1195
+ return { signer: options.signer, signerAddress: options.signerAddress, ...tail, ...mappingTail, ...contractsTail, ...ratioTail, ...envTail };
1192
1196
  }
1193
- return { mnemonic: options.mnemonic, derivationPath: options.derivationPath, ...tail, ...mappingTail, ...contractsTail, ...ratioTail };
1197
+ return { mnemonic: options.mnemonic, derivationPath: options.derivationPath, ...tail, ...mappingTail, ...contractsTail, ...ratioTail, ...envTail };
1194
1198
  }
1195
1199
  async function estimateUploadBytes(content) {
1196
1200
  try {
@@ -1328,7 +1332,7 @@ async function deploy(content, domainName = null, options = {}) {
1328
1332
  }
1329
1333
  }
1330
1334
  provider = await reconnect();
1331
- const providerWithReconnect = { ...provider, reconnect };
1335
+ const providerWithReconnect = { ...provider, reconnect, bulletinAuthorizeV2: envBulletinAuthorizeV2 };
1332
1336
  const [isTestnet, estimated] = await Promise.all([
1333
1337
  detectTestnet(provider.unsafeApi),
1334
1338
  estimateUploadBytes(content)
@@ -1337,7 +1341,7 @@ async function deploy(content, domainName = null, options = {}) {
1337
1341
  const uploadBytes = Math.ceil(estimated * 1.2);
1338
1342
  const chunksNeeded = Math.max(1, Math.ceil(uploadBytes / CHUNK_SIZE));
1339
1343
  const needs = { txs: BigInt(chunksNeeded + 2), bytes: BigInt(uploadBytes) };
1340
- await topUpBy(provider.unsafeApi, provider.ss58, needs, "uploader");
1344
+ await topUpBy(provider.unsafeApi, provider.ss58, needs, "uploader", envBulletinAuthorizeV2);
1341
1345
  }
1342
1346
  console.log("\n" + "=".repeat(60));
1343
1347
  console.log("Storage");
@@ -1502,7 +1506,7 @@ async function deploy(content, domainName = null, options = {}) {
1502
1506
  console.log("=".repeat(60));
1503
1507
  await withSpan("deploy.dotns", "2. dotns", { "deploy.domain": name, "deploy.subdomain": String(parsed?.isSubdomain ?? false) }, async () => {
1504
1508
  const dotns = new DotNS();
1505
- await dotns.connect(resolveDotnsConnectOptions(options, envAssetHub, envAutoAccountMapping, envContracts, envNativeToEthRatio));
1509
+ await dotns.connect(resolveDotnsConnectOptions(options, envAssetHub, envAutoAccountMapping, envContracts, envNativeToEthRatio, envId));
1506
1510
  if (parsed?.isSubdomain) {
1507
1511
  const { owned, owner } = await dotns.checkSubdomainOwnership(parsed.sublabel, parsed.parentLabel);
1508
1512
  if (owned) {