@parity/product-deploy 0.10.0-rc.2 → 0.10.0-rc.4

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 (47) hide show
  1. package/DEPLOYMENT.md +61 -83
  2. package/bin/bulletin-bootstrap +24 -9
  3. package/dist/auth/index.js +2 -1
  4. package/dist/auth/vendor/index.js +2 -1
  5. package/dist/auth-config.js +4 -3
  6. package/dist/bug-report.js +4 -4
  7. package/dist/{chunk-SXWFDMFT.js → chunk-3CUIQTSD.js} +5 -2
  8. package/dist/{chunk-4PVJ2JBZ.js → chunk-4IUTMHVB.js} +73 -41
  9. package/dist/{chunk-5OKB3TEB.js → chunk-5FLTDWWP.js} +4 -1
  10. package/dist/{chunk-UJJQME5K.js → chunk-5V2RCZXO.js} +1 -1
  11. package/dist/{chunk-4D6STP5G.js → chunk-76CVW4DE.js} +1 -1
  12. package/dist/{chunk-XX6LNB74.js → chunk-BZRRNX7T.js} +5 -4
  13. package/dist/{chunk-WM5R4O33.js → chunk-CCTQO2Q4.js} +1 -1
  14. package/dist/{chunk-C74YSAWC.js → chunk-CJHVSSSR.js} +50 -21
  15. package/dist/{chunk-I7UEBFP5.js → chunk-GRWWVYSV.js} +2 -2
  16. package/dist/chunk-TSPERKUS.js +6 -0
  17. package/dist/{chunk-LYWIW6WU.js → chunk-W2VFJGBC.js} +1 -1
  18. package/dist/{chunk-RI3ZLNPN.js → chunk-WIMRJK32.js} +1 -1
  19. package/dist/{chunk-V5VD5CIC.js → chunk-XOR2CEKA.js} +2 -2
  20. package/dist/{chunk-EHQPRWGC.js → chunk-ZHRQDZIK.js} +1 -1
  21. package/dist/chunk-probe.js +3 -3
  22. package/dist/commands/login.js +27 -18
  23. package/dist/commands/logout.js +6 -5
  24. package/dist/commands/transfer.js +8 -5
  25. package/dist/commands/whoami.js +7 -4
  26. package/dist/deploy-actors.js +6 -5
  27. package/dist/deploy.d.ts +19 -2
  28. package/dist/deploy.js +17 -10
  29. package/dist/dotns.js +4 -4
  30. package/dist/index.js +13 -12
  31. package/dist/manifest/byte-budget.d.ts +4 -5
  32. package/dist/manifest/byte-budget.js +1 -1
  33. package/dist/manifest/publish.js +13 -12
  34. package/dist/memory-report.js +2 -2
  35. package/dist/merkle.js +11 -10
  36. package/dist/personhood/bootstrap.js +4 -4
  37. package/dist/personhood/people-client.js +4 -4
  38. package/dist/pool.d.ts +7 -2
  39. package/dist/pool.js +3 -1
  40. package/dist/run-state.js +1 -1
  41. package/dist/sss-allowance-cache.js +5 -4
  42. package/dist/storage-signer.js +11 -10
  43. package/dist/telemetry.js +2 -2
  44. package/dist/version-check.js +3 -3
  45. package/docs/bootstrap.md +47 -13
  46. package/docs/e2e-bootstrap.md +5 -0
  47. package/package.json +1 -1
package/DEPLOYMENT.md CHANGED
@@ -1,124 +1,102 @@
1
- # Deploying your first app
1
+ # Setting up an environment
2
2
 
3
- This guide walks you from zero to a deployed, viewable web app on the [Polkadot Bulletin Chain](https://github.com/paritytech/polkadot-bulletin-chain) testnet, using `bulletin-deploy`.
3
+ This guide is for the **operator / chain admin** standing up `bulletin-deploy` against a Polkadot environment. It covers what you configure and authorize so that deploys work against *your* chain.
4
4
 
5
- > [!NOTE]
6
- > This targets a **testnet** (`paseo-next-v2`) by default. It is reference/proof-of-concept tooling — see the security notice in the [README](README.md#security).
7
-
8
- ## Prerequisites
5
+ For **using** the tool once an environment is set up — building and shipping an app — see the [README](README.md). This document is the setup side.
9
6
 
10
- 1. **Node.js ≥ 22.**
11
- ```sh
12
- node --version # must print v22 or higher
13
- ```
14
- 2. **The CLI**, installed globally:
15
- ```sh
16
- npm install -g bulletin-deploy
17
- ```
18
- 3. **A built static site** — a directory of static files (HTML/CSS/JS), for example `./dist`. Build your app however you normally would (`npm run build`, etc.); `bulletin-deploy` uploads the resulting directory as-is.
19
- 4. **An identity to own the name** — choose one:
20
- - **Mobile Polkadot wallet (recommended).** No mnemonic on disk; you sign in once by scanning a QR code. See [Sign in](#1-sign-in-recommended) below.
21
- - **A testnet mnemonic.** Pass `--mnemonic "..."` (or set the `MNEMONIC` env var) instead of signing in.
7
+ > [!NOTE]
8
+ > `bulletin-deploy` is reference / proof-of-concept tooling — see the security notice in the [README](README.md#security). The defaults target Polkadot testnets.
22
9
 
23
- That is all you need on testnet. You do **not** need testnet funds for the recommended path: a worker account registers the name and uploads the content, then transfers ownership to your account as the final step.
10
+ **Documentation map read in order:**
11
+ 1. **[DEPLOYMENT.md](DEPLOYMENT.md)** (this doc) — set up `bulletin-deploy` for your environment.
12
+ 2. **[docs/bootstrap.md](docs/bootstrap.md)** — the `bulletin-bootstrap` reference (Bulletin storage authorization).
13
+ 3. **[docs/e2e-bootstrap.md](docs/e2e-bootstrap.md)** — a fully worked setup, end to end, for the E2E test environment.
24
14
 
25
- ### Optional: IPFS / Kubo
15
+ (Using the tool to ship an app — not setting it up — is the [README](README.md).)
26
16
 
27
- By default the CLI uses the [IPFS Kubo](https://docs.ipfs.tech/install/) binary to merkleize your site if it is on your `PATH`, and otherwise falls back to a pure-JavaScript implementation. You can force the pure-JS path (no binary required) with `--js-merkle`. If you see `IPFS CLI not installed`, either install Kubo or add `--js-merkle`.
17
+ ## What an "environment" is
28
18
 
29
- ## Choosing a name
19
+ A `bulletin-deploy` environment is three things, which it expects to already exist for your network:
30
20
 
31
- Apps are addressed by a DotNS name like `myapp.dot`. A few rules:
21
+ - an **Asset Hub** (PolkaVM / `pallet-revive`) with the **DotNS** contracts deployed names live here;
22
+ - a **Bulletin Chain** — content (your site, chunked + content-addressed) is stored here;
23
+ - an **IPFS gateway** — serves the stored content over HTTP.
32
24
 
33
- - The base label must be **at least 6 characters** (shorter labels are reserved).
34
- - Pick something not already registered. The CLI tells you during a deploy whether the name is available or already owned by you.
25
+ Setup is: tell `bulletin-deploy` where those are, authorize the accounts that will write to the Bulletin Chain, and (if your DotNS gates registration) grant the registering account personhood. The steps below.
35
26
 
36
- ## Deploy
27
+ ## 1. Define the environment
37
28
 
38
- ### 1. Sign in (recommended)
29
+ `bulletin-deploy` resolves environments from `environments.json`. The built-in presets ship in `assets/environments.json`:
39
30
 
40
31
  ```sh
41
- bulletin-deploy login # scan the QR code with your Polkadot wallet app
42
- bulletin-deploy whoami # confirm the signed-in address
32
+ bulletin-deploy --list-environments # list the built-in environment IDs
43
33
  ```
44
34
 
45
- If you would rather sign with a mnemonic, skip this and pass `--mnemonic` on the deploy command instead.
46
-
47
- ### 2. Deploy your build directory
35
+ Add your network either by adding an entry to `assets/environments.json`, or by supplying one at runtime:
48
36
 
49
37
  ```sh
50
- bulletin-deploy ./dist myapp.dot
51
- # or, without an IPFS binary installed:
52
- bulletin-deploy ./dist myapp.dot --js-merkle
38
+ bulletin-deploy ./dist myapp.dot --environment-file ./my-env.json
39
+ bulletin-deploy ./dist myapp.dot --env <id> --contract DOTNS_REGISTRAR=0x... # override single fields
53
40
  ```
54
41
 
55
- The CLI will:
42
+ An environment entry provides:
56
43
 
57
- 1. Merkleize your directory into content-addressed chunks.
58
- 2. Upload the chunks to the Bulletin Chain and wait for finality.
59
- 3. Register `myapp.dot` in DotNS and point it at your content.
60
- 4. Transfer ownership of the name to your signed-in account.
44
+ - **Chain endpoints** the `wss://` RPCs for at least the **bulletin** and **asset-hub** chains.
45
+ - **DotNS contract addresses** the contract set deployed on your Asset Hub (`DOTNS_REGISTRAR`, `POP_RULES`, the resolvers, `STORE_FACTORY`, …).
46
+ - **`nativeToEthRatio`** `10^(18 native_decimals)`; e.g. a 10-decimal native token → `100000000`.
47
+ - **`registerStorageDeposit`** the DotNS registration deposit on your chain.
48
+ - **`autoAccountMapping`** — whether the chain maps accounts to H160 automatically on first tx.
49
+ - **`ipfs`** — the gateway that serves deployed content.
61
50
 
62
- On success it prints a summary:
51
+ Use the `paseo-next-v2` and `summit` entries in `assets/environments.json` as worked examples to copy.
63
52
 
64
- ```
65
- ============================================================
66
- DEPLOYMENT COMPLETE!
67
- ============================================================
68
- - Polkadot Desktop: myapp.dot
69
- - Polkadot Browser: https://myapp.dot.li
70
- ```
53
+ ## 2. Authorize Bulletin storage
71
54
 
72
- ### 3. View it
55
+ Writing content to the Bulletin Chain requires each **storage account** to hold a `TransactionStorage` **authorization** — a quota of transactions and bytes. Storage is gated by this quota, **not by balance** (Bulletin has no fee model), so the storage accounts need no funds. `bulletin-deploy` never grants the authorization itself; the chain's **authorizer** must.
73
56
 
74
- - Open **`https://myapp.dot.li`** in any browser.
75
- - Or open **`myapp.dot`** directly in a Polkadot-aware browser/extension.
57
+ The storage accounts are an upload **pool**, derived as `//deploy/0…N` from `BULLETIN_POOL_MNEMONIC` (default: the well-known `DEV_PHRASE`). The deploy path derives the **same** pool from that env var — so if you use a custom pool, set `BULLETIN_POOL_MNEMONIC` identically for both bootstrap and deploy, or they won't line up.
76
58
 
77
- ## If the transfer step fails
78
-
79
- If your content uploads but the final ownership transfer fails, the name is registered by the worker and you can claim it separately:
59
+ `bulletin-bootstrap` reports each pool account's authorization status, and — given an authorizer key — grants authorization to the ones that lack it:
80
60
 
81
61
  ```sh
82
- bulletin-deploy transfer myapp.dot # your signed-in account
83
- bulletin-deploy transfer myapp.dot --to 0x... # an explicit recipient
62
+ bulletin-bootstrap --env <id> # list pool accounts + their authorization status
63
+ bulletin-bootstrap --env <id> --authorizer "<seed>" # grant authorization with the authorizer key
64
+ bulletin-bootstrap --env <id> --pool-size 20 # inspect/authorize a larger pool
84
65
  ```
85
66
 
86
- ## Choosing a different network
67
+ On a **testnet** the authorizer defaults to `//Alice` (it holds authorization authority there), so no `--authorizer` is needed. On a **production** chain, pass `--authorizer` with the key that actually holds authorization authority — if it can't grant, the tool reports that rather than pretending. See [`docs/bootstrap.md`](docs/bootstrap.md) for the full reference.
87
68
 
88
- ```sh
89
- bulletin-deploy --list-environments # show available environment IDs
90
- bulletin-deploy ./dist myapp.dot --env <id>
91
- ```
69
+ ## 3. Personhood (if your DotNS gates registration)
92
70
 
93
- The default is `paseo-next-v2`. Override individual fields with `--environment-file <path>` or `--contract KEY=0x...`.
71
+ If your `POP_RULES` contract requires Proof-of-Personhood to register a base name, the **registering account** needs a PoP status granted by the `POP_RULES` owner. (NoStatus-eligible label shapes skip this — see the rules in your DotNS deployment.)
94
72
 
95
- ## Bootstrapping your own storage pool
73
+ - On Parity's testnets, request a grant by opening an issue on [paritytech/dotns](https://github.com/paritytech/dotns/issues).
74
+ - On your own chain, your `POP_RULES` owner grants it out of band — `bulletin-deploy` cannot upgrade a signer.
96
75
 
97
- On `paseo-next-v2` and the other shared Parity testnets the upload pool is **already authorized** — you don't need to bootstrap anything to deploy. You only need this if you run your own setup: a custom pool mnemonic, your own Bulletin chain, or a testnet that was just reset (a wipe clears all authorizations).
76
+ ## 4. Fund the signing accounts
98
77
 
99
- Uploading content to the Bulletin Chain requires the storage account to hold a `TransactionStorage` authorization a quota of transactions and bytes. `bulletin-deploy` never grants this itself; the account must be authorized by the chain's authorizer first. The separate `bulletin-bootstrap` CLI does that for a pool:
78
+ The accounts that register and transfer names need a balance on your Asset Hub for DotNS fees:
100
79
 
101
- ```sh
102
- bulletin-bootstrap --env <id> # authorize the default pool on an environment
103
- bulletin-bootstrap --env <id> --pool-size 20 # authorize a larger pool
104
- bulletin-bootstrap --mnemonic "<phrase>" # authorize a custom pool's accounts
105
- ```
80
+ - **Public testnets** — use the faucet (e.g. [faucet.polkadot.io](https://faucet.polkadot.io/)).
81
+ - **Restricted networks** fund the accounts out of band; there is no public faucet.
82
+
83
+ (These are the DotNS signing accounts on the Asset Hub — distinct from the Bulletin storage pool in step 2, which needs authorization, not funds.)
84
+
85
+ ## 5. Content gateway
86
+
87
+ Deployed content is content-addressed (a CID) on the Bulletin Chain and served over HTTP by the IPFS gateway you set as `ipfs` in step 1. The `.dot` name resolves to that CID via DotNS. Parity's public testnets resolve a deployed name at `https://<name>.dot.li`; your environment uses whatever gateway / resolver you point it at.
88
+
89
+ ## 6. Verify the setup
106
90
 
107
- It derives the pool accounts, authorizes each one for storage, and funds them. On a testnet the authorizer is the dev account (`//Alice`); on a production chain the network's own authorizer must grant authorization — `bulletin-bootstrap` cannot self-authorize there. It's a one-time setup step, not part of a routine deploy. See [`docs/bootstrap.md`](docs/bootstrap.md) for the full operator reference.
91
+ A **test deploy** is the real check run one (see the [README](README.md)) and confirm it completes and resolves. Two failures map straight back to the steps above:
108
92
 
109
- ## Signing modes, in short
93
+ - `not authorized to upload` → the storage account lacks authorization (step 2).
94
+ - a personhood / registration gate → the registering account needs PoP (step 3).
110
95
 
111
- - **Signed in (default):** a worker registers and uploads, then hands the name to your account zero wallet signatures on testnet.
112
- - **`--no-transfer-to-signedin-user`:** sign every DotNS transaction with your mobile session instead.
113
- - **`--mnemonic "..."` / `MNEMONIC`:** use a mnemonic instead of a session.
96
+ If you're working from a **clone of the repository** (not just the npm package), `tools/` has read-only diagnostics that take `--env <id>`: `check-bulletin-auth.mjs` (storage quotas), `check-balances.mjs` (balances + quota), `check-pop-status.mjs` (personhood), `probe-env-health.mjs` (RPC reachability). These ship with the repo, not the published package.
114
97
 
115
- ## Troubleshooting
98
+ A concrete, fully worked instance of this setup — for the E2E test environment — lives in [`docs/e2e-bootstrap.md`](docs/e2e-bootstrap.md).
116
99
 
117
- | Symptom | Cause / fix |
118
- |---|---|
119
- | `IPFS CLI not installed` | Install [Kubo](https://docs.ipfs.tech/install/), or add `--js-merkle`. |
120
- | Name rejected as reserved / too short | Use a base label of **6+ characters**. |
121
- | `Login session unavailable or expired` | Re-run `bulletin-deploy login`. |
122
- | Not authorized to upload | The storage account lacks a Bulletin `TransactionStorage` authorization. On `paseo-next-v2` the shared pool is already authorized; on your own setup, see [Bootstrapping your own storage pool](#bootstrapping-your-own-storage-pool). On a production chain, request authorization from the network's authorizer. |
100
+ ---
123
101
 
124
- For the full option reference, run `bulletin-deploy --help`.
102
+ Once the environment is set up, day-to-day use is in the [README](README.md): build your site, `bulletin-deploy ./dist myapp.dot`, done.
@@ -13,6 +13,7 @@ const flags = {};
13
13
  for (let i = 0; i < args.length; i++) {
14
14
  if (args[i] === "--pool-size") { flags.poolSize = parseInt(args[++i], 10); }
15
15
  else if (args[i] === "--mnemonic") { flags.mnemonic = args[++i]; }
16
+ else if (args[i] === "--authorizer") { flags.authorizer = args[++i]; }
16
17
  else if (args[i] === "--rpc") { flags.rpc = args[++i]; }
17
18
  else if (args[i] === "--env") { flags.env = args[++i]; }
18
19
  else if (args[i] === "--version" || args[i] === "-V") { flags.version = true; }
@@ -32,17 +33,27 @@ if (flags.help) {
32
33
  console.log(`bulletin-bootstrap v${VERSION}
33
34
 
34
35
  Usage:
35
- bulletin-bootstrap
36
+ bulletin-bootstrap [options]
37
+
38
+ Reports the authorization status of each pool account and, when an authorizer
39
+ key is available, grants authorization to any account that needs it.
40
+
41
+ The Bulletin chain has no fee model — storage access is gated by TransactionStorage
42
+ authorization quota, not account balance. This tool manages that quota.
36
43
 
37
44
  Options:
38
- --mnemonic "..." Pool root mnemonic (or set BULLETIN_POOL_MNEMONIC / MNEMONIC env var)
39
- --rpc wss://... Bulletin RPC (or set BULLETIN_RPC env var)
40
- --env <id> Load environment by id from environments.json (sets default RPC and V2 flag)
41
- --pool-size N Number of pool accounts to initialize (default: 10)
42
- --version Show version
43
- --help Show this help
44
-
45
- Initialize pool accounts for Bulletin storage authorization.`);
45
+ --mnemonic "..." Pool root mnemonic used to derive pool accounts
46
+ (also readable from BULLETIN_POOL_MNEMONIC or MNEMONIC env var).
47
+ Default: the well-known dev phrase (same key the deploy path uses).
48
+ --authorizer "..." Seed/mnemonic of the key that holds authorization authority
49
+ on this chain (e.g. "//Alice", a full mnemonic, or a hex seed).
50
+ On testnets, defaults to //Alice if omitted.
51
+ On non-testnets, required to grant — omit to get status only.
52
+ --rpc wss://... Bulletin RPC endpoint (or set BULLETIN_RPC env var)
53
+ --env <id> Load environment by id from environments.json
54
+ --pool-size N Number of pool accounts to check/initialize (default: 10)
55
+ --version Show version
56
+ --help Show this help`);
46
57
  process.exit(0);
47
58
  }
48
59
 
@@ -52,6 +63,10 @@ const mnemonic = flags.mnemonic ?? process.env.BULLETIN_POOL_MNEMONIC ?? process
52
63
 
53
64
  const bootstrapOpts = {};
54
65
 
66
+ if (flags.authorizer) {
67
+ bootstrapOpts.authorizerMnemonic = flags.authorizer;
68
+ }
69
+
55
70
  if (flags.env) {
56
71
  const envJsonPath = fileURLToPath(new URL("../assets/environments.json", import.meta.url));
57
72
  let envDoc;
@@ -8,12 +8,13 @@ import {
8
8
  requestResourceAllocation,
9
9
  resolveSigner,
10
10
  summarizeOutcomes
11
- } from "../chunk-5OKB3TEB.js";
11
+ } from "../chunk-5FLTDWWP.js";
12
12
  import {
13
13
  renderLoginStatus,
14
14
  renderLogoutStatus,
15
15
  renderQrCode
16
16
  } from "../chunk-RIRDBSBG.js";
17
+ import "../chunk-TSPERKUS.js";
17
18
  import "../chunk-ZOC4GITL.js";
18
19
  export {
19
20
  BULLETIN_RESOURCE,
@@ -12,7 +12,8 @@ import {
12
12
  resolveSigner,
13
13
  sessionRootPublicKey,
14
14
  summarizeOutcomes
15
- } from "../../chunk-5OKB3TEB.js";
15
+ } from "../../chunk-5FLTDWWP.js";
16
+ import "../../chunk-TSPERKUS.js";
16
17
  import "../../chunk-ZOC4GITL.js";
17
18
  export {
18
19
  BULLETIN_RESOURCE,
@@ -9,9 +9,10 @@ import {
9
9
  getPeopleChainEndpoints,
10
10
  hasPersistedSession,
11
11
  resolveBulletinEndpoints
12
- } from "./chunk-SXWFDMFT.js";
13
- import "./chunk-4D6STP5G.js";
14
- import "./chunk-WM5R4O33.js";
12
+ } from "./chunk-3CUIQTSD.js";
13
+ import "./chunk-TSPERKUS.js";
14
+ import "./chunk-76CVW4DE.js";
15
+ import "./chunk-CCTQO2Q4.js";
15
16
  import "./chunk-QRKI6MMK.js";
16
17
  import "./chunk-ZOC4GITL.js";
17
18
  export {
@@ -9,10 +9,10 @@ import {
9
9
  offerBugReport,
10
10
  scrubSecrets,
11
11
  setDeployContext
12
- } from "./chunk-V5VD5CIC.js";
13
- import "./chunk-EHQPRWGC.js";
14
- import "./chunk-4D6STP5G.js";
15
- import "./chunk-WM5R4O33.js";
12
+ } from "./chunk-XOR2CEKA.js";
13
+ import "./chunk-ZHRQDZIK.js";
14
+ import "./chunk-76CVW4DE.js";
15
+ import "./chunk-CCTQO2Q4.js";
16
16
  export {
17
17
  buildCliFlagsSummary,
18
18
  buildLabels,
@@ -1,6 +1,9 @@
1
+ import {
2
+ CLI_NAME
3
+ } from "./chunk-TSPERKUS.js";
1
4
  import {
2
5
  VERSION
3
- } from "./chunk-4D6STP5G.js";
6
+ } from "./chunk-76CVW4DE.js";
4
7
  import {
5
8
  loadEnvironments
6
9
  } from "./chunk-QRKI6MMK.js";
@@ -13,7 +16,7 @@ var DOT_DAPP_ID = "polkadot-app-deploy";
13
16
  var DOT_PRODUCT_ID = DOT_DAPP_ID;
14
17
  var DOT_DERIVATION_INDEX = 0;
15
18
  var DOT_HOST_NAME = "polkadot-app-deploy";
16
- var STALE_SESSION_MESSAGE = 'Stored login session could not be read \u2014 it may have been written by an older version. Run "bulletin-deploy logout", then "bulletin-deploy login" to pair again.';
19
+ var STALE_SESSION_MESSAGE = `Stored login session could not be read \u2014 it may have been written by an older version. Run "${CLI_NAME} logout", then "${CLI_NAME} login" to pair again.`;
17
20
  function hasPersistedSession() {
18
21
  const dir = join(homedir(), ".polkadot-apps");
19
22
  if (!existsSync(dir)) return false;
@@ -34,6 +34,9 @@ function isAuthorizationSufficient(auth, currentBlock) {
34
34
  if (Number(auth.expiration ?? 0) <= currentBlock) return false;
35
35
  return true;
36
36
  }
37
+ function accountsNeedingAuthorization(auths, currentBlock) {
38
+ return auths.filter((a) => !isAuthorizationSufficient(a, currentBlock));
39
+ }
37
40
  function selectAccount(authorizations, random = Math.random, pinnedIndex) {
38
41
  if (pinnedIndex != null) {
39
42
  const pinned = authorizations.find((a) => a.index === pinnedIndex);
@@ -114,8 +117,16 @@ async function ensureAuthorized(api, address, label) {
114
117
  `Bulletin storage account ${who} is not authorized to store. On production the storage account must already carry its own authorization/allowance \u2014 bulletin-deploy cannot grant it.`
115
118
  );
116
119
  }
117
- async function bootstrapPool(bulletinRpc, poolSize = 10, mnemonic) {
118
- console.log(`Bootstrapping ${poolSize} pool accounts on ${bulletinRpc}...
120
+ function printAuthStatus(a, currentBlock) {
121
+ if (isAuthorizationSufficient(a, currentBlock)) {
122
+ const mb = (Number(a.bytes) / 1e6).toFixed(1);
123
+ console.log(` [${a.index}] ${a.address} AUTHORIZED \u2014 ${a.transactions} txs / ${mb}MB remaining, expires @${a.expiration}`);
124
+ } else {
125
+ console.log(` [${a.index}] ${a.address} NOT AUTHORIZED`);
126
+ }
127
+ }
128
+ async function bootstrapPool(bulletinRpc, poolSize = 10, mnemonic, opts = {}) {
129
+ console.log(`Checking ${poolSize} pool accounts on ${bulletinRpc}...
119
130
  `);
120
131
  await cryptoWaitReady();
121
132
  const accounts = derivePoolAccounts(poolSize, mnemonic);
@@ -124,57 +135,78 @@ async function bootstrapPool(bulletinRpc, poolSize = 10, mnemonic) {
124
135
  { heartbeatTimeout: WS_HEARTBEAT_TIMEOUT_MS }
125
136
  ));
126
137
  const api = client.getUnsafeApi();
127
- const keyring = new Keyring({ type: "sr25519" });
128
- const alice = keyring.addFromUri("//Alice");
129
- const aliceSigner = getPolkadotSigner(alice.publicKey, "Sr25519", (data) => alice.sign(data));
130
- const aliceAccount = await api.query.System.Account.getValue(alice.address);
131
- const aliceBalance = BigInt(aliceAccount.data.free);
132
- console.log(`Alice balance: ${formatPasBalance(aliceBalance)} PAS
138
+ try {
139
+ const currentBlock = await api.query.System.Number.getValue();
140
+ const auths = await fetchPoolAuthorizations(api, accounts);
141
+ console.log("Pool authorization status:");
142
+ for (const a of auths) {
143
+ printAuthStatus(a, currentBlock);
144
+ }
145
+ console.log("");
146
+ const needsAuth = accountsNeedingAuthorization(auths, currentBlock);
147
+ if (needsAuth.length === 0) {
148
+ console.log("All pool accounts are authorized. Nothing to do.");
149
+ return;
150
+ }
151
+ console.log(`${needsAuth.length} account(s) need authorization.
133
152
  `);
134
- console.log(`Authorizing accounts (${TOPUP_TRANSACTIONS} txs / ${Number(TOPUP_BYTES) / 1e6}MB)
153
+ let authorizerSigner;
154
+ const keyring = new Keyring({ type: "sr25519" });
155
+ if (opts.authorizerMnemonic) {
156
+ const authKey = keyring.addFromUri(opts.authorizerMnemonic);
157
+ authorizerSigner = getPolkadotSigner(authKey.publicKey, "Sr25519", (data) => authKey.sign(data));
158
+ console.log(`Using provided authorizer: ${authKey.address}
135
159
  `);
136
- for (const account of accounts) {
137
- console.log(`Account ${account.index}: ${account.address}`);
138
- try {
139
- const tx = api.tx.TransactionStorage.authorize_account({
140
- who: account.address,
141
- transactions: clampU32(BigInt(TOPUP_TRANSACTIONS), "transactions"),
142
- bytes: TOPUP_BYTES
143
- });
144
- const result = await tx.signAndSubmit(aliceSigner);
145
- if (!result.ok) throw new Error("dispatch failed");
146
- console.log(` Authorized: ${TOPUP_TRANSACTIONS} txs / ${Number(TOPUP_BYTES) / 1e6}MB`);
147
- } catch (e) {
148
- console.log(` Authorization failed: ${e.message?.slice(0, 80)}`);
160
+ } else {
161
+ const isTestnet = await detectTestnet(api);
162
+ if (isTestnet) {
163
+ const alice = keyring.addFromUri("//Alice");
164
+ authorizerSigner = getPolkadotSigner(alice.publicKey, "Sr25519", (data) => alice.sign(data));
165
+ console.log(`Testnet detected \u2014 defaulting to //Alice as authorizer (${alice.address})
166
+ `);
167
+ } else {
168
+ console.log(
169
+ 'Authorization is needed but no authorizer key was provided.\nRe-run with --authorizer "<seed>" to grant authorization.'
170
+ );
171
+ return;
172
+ }
149
173
  }
150
- try {
151
- const transfer = api.tx.Balances.transfer_allow_death({
152
- dest: Enum("Id", account.address),
153
- value: 100000000000n
154
- // 0.1 PAS
155
- });
156
- const result = await transfer.signAndSubmit(aliceSigner);
157
- if (!result.ok) throw new Error("dispatch failed");
158
- console.log(` Transferred 0.1 PAS`);
159
- } catch (e) {
160
- console.log(` Transfer failed: ${e.message?.slice(0, 80)}`);
174
+ console.log(`Authorizing ${needsAuth.length} account(s) (${TOPUP_TRANSACTIONS} txs / ${Number(TOPUP_BYTES) / 1e6}MB each):
175
+ `);
176
+ for (const account of needsAuth) {
177
+ console.log(` [${account.index}] ${account.address}`);
178
+ try {
179
+ const tx = api.tx.TransactionStorage.authorize_account({
180
+ who: account.address,
181
+ transactions: clampU32(BigInt(TOPUP_TRANSACTIONS), "transactions"),
182
+ bytes: TOPUP_BYTES
183
+ });
184
+ const result = await tx.signAndSubmit(authorizerSigner);
185
+ if (!result.ok) throw new Error("dispatch failed");
186
+ console.log(` granted: ${TOPUP_TRANSACTIONS} txs / ${Number(TOPUP_BYTES) / 1e6}MB`);
187
+ } catch (e) {
188
+ console.log(` could not grant \u2014 is this key the chain's authorizer? (${e.message?.slice(0, 80)})`);
189
+ }
161
190
  }
162
191
  console.log("");
192
+ console.log("=".repeat(60));
193
+ console.log("Final pool authorization status:");
194
+ console.log("=".repeat(60));
195
+ const finalBlock = await api.query.System.Number.getValue();
196
+ const finalAuths = await fetchPoolAuthorizations(api, accounts);
197
+ for (const a of finalAuths) {
198
+ printAuthStatus(a, finalBlock);
199
+ }
200
+ } finally {
201
+ client.destroy();
163
202
  }
164
- console.log("=".repeat(60));
165
- console.log("Pool Summary");
166
- console.log("=".repeat(60));
167
- const auths = await fetchPoolAuthorizations(api, accounts);
168
- for (const a of auths) {
169
- console.log(` ${a.index}: ${a.address} | txs: ${a.transactions} | bytes: ${Number(a.bytes) / 1e6}MB`);
170
- }
171
- client.destroy();
172
203
  }
173
204
 
174
205
  export {
175
206
  formatPasBalance,
176
207
  derivePoolAccounts,
177
208
  isAuthorizationSufficient,
209
+ accountsNeedingAuthorization,
178
210
  selectAccount,
179
211
  fetchPoolAuthorizations,
180
212
  isTestnetSpecName,
@@ -1,3 +1,6 @@
1
+ import {
2
+ CLI_NAME
3
+ } from "./chunk-TSPERKUS.js";
1
4
  import {
2
5
  NonRetryableError
3
6
  } from "./chunk-ZOC4GITL.js";
@@ -66,7 +69,7 @@ function createSessionSigner(session, ref) {
66
69
  clearPoll = setInterval(() => {
67
70
  if (noAllowanceMsg) {
68
71
  reject(new NonRetryableError(
69
- "Session signing allowance has expired (~2-3 days after login). Run `bulletin-deploy login` to renew."
72
+ `Session signing allowance has expired (~2-3 days after login). Run \`${CLI_NAME} login\` to renew.`
70
73
  ));
71
74
  }
72
75
  }, 200);
@@ -3,7 +3,7 @@ import {
3
3
  } from "./chunk-GRPLHUYC.js";
4
4
  import {
5
5
  DOT_DAPP_ID
6
- } from "./chunk-SXWFDMFT.js";
6
+ } from "./chunk-3CUIQTSD.js";
7
7
 
8
8
  // src/sss-allowance-cache.ts
9
9
  import { mkdir, readFile, writeFile, unlink } from "fs/promises";
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  package_default,
3
3
  writeRunState
4
- } from "./chunk-WM5R4O33.js";
4
+ } from "./chunk-CCTQO2Q4.js";
5
5
 
6
6
  // src/memory-report.ts
7
7
  import * as fs2 from "fs";
@@ -1,15 +1,16 @@
1
1
  import {
2
2
  pessimisticSizePreflight
3
- } from "./chunk-RI3ZLNPN.js";
3
+ } from "./chunk-WIMRJK32.js";
4
4
  import {
5
+ BLAKE2B_256_MULTIHASH_CODE,
5
6
  encodeContenthash,
6
7
  resolveDotnsConnectOptions,
7
8
  storeDirectory,
8
9
  storeFile
9
- } from "./chunk-C74YSAWC.js";
10
+ } from "./chunk-CJHVSSSR.js";
10
11
  import {
11
12
  DotNS
12
- } from "./chunk-I7UEBFP5.js";
13
+ } from "./chunk-GRWWVYSV.js";
13
14
  import {
14
15
  getPopSelfServeConfig,
15
16
  loadEnvironments,
@@ -43,7 +44,7 @@ async function publishManifest(opts) {
43
44
  Manifest publish \u2014 ${config.domain}`);
44
45
  console.log(` Loaded config: ${sourcePath}`);
45
46
  console.log(` Uploading icon (${iconBytes.length} B)\u2026`);
46
- const iconCid = await storeFile(iconBytes);
47
+ const iconCid = await storeFile(iconBytes, { hashCode: BLAKE2B_256_MULTIHASH_CODE });
47
48
  console.log(` Icon CID: ${iconCid}`);
48
49
  const executableCids = {};
49
50
  for (const exec of config.executables) {
@@ -6,7 +6,7 @@ import * as path from "path";
6
6
  // package.json
7
7
  var package_default = {
8
8
  name: "@parity/product-deploy",
9
- version: "0.10.0-rc.2",
9
+ version: "0.10.0-rc.4",
10
10
  private: false,
11
11
  repository: {
12
12
  type: "git",