Skip to content

fix(dot init): bulletin allowance resilience + QrLogin setState warning#189

Merged
UtkarshBhardwaj007 merged 2 commits into
mainfrom
fix/dot-init-bulletin-allowance-resilience
May 19, 2026
Merged

fix(dot init): bulletin allowance resilience + QrLogin setState warning#189
UtkarshBhardwaj007 merged 2 commits into
mainfrom
fix/dot-init-bulletin-allowance-resilience

Conversation

@UtkarshBhardwaj007
Copy link
Copy Markdown
Member

Summary

Two issues observed in dot init v0.22.3:

  1. A Bulletin allowance key returned but not authorized failure followed by setup complete (some account setup steps failed).
  2. A React warning Cannot update a component (InitScreen) while rendering a different component (QrLogin).

This PR fixes both, plus a related slot-key-clobber race surfaced during the investigation.

Root cause for (1) — verified across mobile + RFC

Cross-checked against polkadot-app-android-v2, triangle-js-sdks/host-papp, polkadot-desktop, w3s-conference-app, RFC-0010, and PR #186.

  • The mobile (NIGHTLY build → paseo-next-v2 endpoints) submits Resources::claim_long_term_storage(period, counter, account_id) on the People parachain (not Bulletin) — polkadot-app-android-v2/feature/transaction-storage/impl/.../RealTransactionStorageSlotAllocator.kt:53-114. Signer is TransactionSignerSource.None plus a Bandersnatch ring-VRF People-membership proof transaction extension. The Bulletin-side authorization propagates from People via on-chain runtime mechanics.
  • After submitting, mobile waits 30 s for visibility on knownChains.bulletIn. That wait is wrapped in .logFailure(...) — failures are swallowed, and the slot key is returned to the host regardless.
  • The CLI then polls Bulletin via checkAuthorization for 75 s. If People→Bulletin propagation lags, the poll times out and the previous code aborted the whole init flow.

There is no host-side on-chain step the CLI is missing — RFC-0010 places all chain work on the mobile. The only correct host behaviour is to be resilient to the propagation lag.

What this PR changes

a. BULLETIN_AUTHORIZATION_URL + bulletinAuthorizationHelp(slotSS58) (config.ts, bulletin.ts). The timeout error now reads Mobile returned Bulletin allowance key <slotSS58>, but it is not authorized on Bulletin yet. Open <faucet URL> and authorize that address, then re-run \dot init`.` Same hint on the cached-key-not-authorized branch. Pattern lifted from PR #186.

b. Persist the slot key BEFORE the propagation wait (requestAndStoreBulletinAllowanceSigner). A 75 s timeout no longer throws away a valid mobile-derived key — next run / dot deploy will see the cached key and only re-poll Bulletin.

c. Non-fatal Bulletin timeout in AccountSetup. The slot key + marker get persisted regardless; the Bulletin row shows the help string; funding + Revive::map_account continue to run because they depend on Asset Hub, not Bulletin. The setup complete some account setup steps failed summary surfaces correctly.

d. Batched storeSlotAccountKeysFromOutcomes. Was Promise.all([...storeSlotAccountKey]) where each call did its own read-modify-write of allowance-keys.json — interleaved writes would drop sibling slot keys (BulletInAllowance + StatementStoreAllowance returned in the same call). Now a single load → mutate → save.

QrLogin setState warning

Introduced in #188. The code did:

```tsx
waitForLogin(login, setStatus).then(() => {
setStatus((current) => {
onDone(current.step === "success" ? current.addresses : null);
return current;
});
});
```

The setStatus updater runs during React's render phase, so onDone(...) calling the parent's setAddresses / setAuthResolved triggered a "Cannot update a component while rendering a different component" warning.

Fix: snapshot the resolved addresses in a useRef inside the onStatus callback, then call onDone outside any updater.

Why CI didn't catch it. vitest.config.ts only picks up *.test.ts, not *.tsx, so React-strict-mode warnings never fire in the suite.

What this PR does NOT do

  • Doesn't fix the root cause. Propagation timing / silent LongTermStorageAllocationFailed is a mobile + chain issue. A separate root-cause investigation is running.
  • Doesn't bypass the mobile flow. No //Alice funding, no host-side Bulletin::authorize extrinsic — RFC-0010 doesn't allow that.
  • Doesn't widen the 75 s timeout (deferred — propose separately if real-world data justifies it).

Test plan

  • pnpm format:check (145 files)
  • pnpm lint:license (172/172 files)
  • pnpm test — 538 passed, 1 skipped (was 536). Added: bulletinAuthorizationHelp returns SS58 + URL + "re-run dot init"; storeSlotAccountKeysFromOutcomes preserves siblings.
  • Manual dot init post-merge: confirm Bulletin row shows warning + faucet URL, funding/mapping still completes, and no React warning at top of output.

The mobile app (Polkadot Android NIGHTLY) submits the RFC-0010
`Resources::claim_long_term_storage` extrinsic on the People parachain
and then waits 30 s for the authorization to propagate to Bulletin
Chain. That wait is .logFailure()-wrapped — failures are swallowed and
the slot key is returned regardless. The CLI then polls Bulletin via
`checkAuthorization` from @parity/product-sdk-bulletin for 75 s; if
propagation lags, the poll throws "Mobile returned Bulletin allowance
key X, but it is not authorized on Bulletin yet" and `dot init`
aborts before even running funding/mapping.

Verified end-to-end against the polkadot-app-android-v2 source
(`RealTransactionStorageSlotAllocator.kt:53-114`, AWAIT_BULLETIN_TIMEOUT
= 30s, `.logFailure(...)` at the `awaitAllocationVisibleOnBulletIn`
call site) and RFC-0010's "Bulletin submission — implicit allocation"
flow. The host has no on-chain step to add here — RFC-0010 puts all
the chain-side work on the mobile. The CLI's only correct move is to
be resilient to the propagation lag.

Four fixes in this PR, plus a React warning from #188:

1. `BULLETIN_AUTHORIZATION_URL` + `bulletinAuthorizationHelp(slot)`
   in `src/config.ts` and `src/utils/allowances/bulletin.ts`. Both
   `waitForBulletinSlotAuthorization`'s timeout error and the
   cached-key-not-authorized error now print the slot SS58 + the
   manual-faucet URL
   (`https://paritytech.github.io/polkadot-bulletin-chain/authorizations`).
   Pattern lifted from PR #186.

2. `requestAndStoreBulletinAllowanceSigner` now persists the slot
   key + marker BEFORE the propagation wait. A 75 s timeout no
   longer throws away a perfectly valid key the mobile derived
   from `mnemonic + //allowance//bulletin//<productId>` — the
   key will still be valid the moment the chain catches up.

3. `AccountSetup` makes a Bulletin propagation timeout a SOFT
   failure rather than aborting `dot init`. The row shows the
   help string + slot SS58; funding + Revive auto-map mapping
   still run because they depend on the product account on
   Asset Hub, not on Bulletin authorization. The final
   `setup complete some account setup steps failed` warning
   surfaces correctly.

4. `storeSlotAccountKeysFromOutcomes` is now a single
   read-modify-write so two slot keys returned in one
   `requestResourceAllocation` call (e.g. `BulletInAllowance` +
   `StatementStoreAllowance`) can't race-clobber each other.
   The previous `Promise.all(...storeSlotAccountKey)` had each
   call load the JSON, mutate one resource, save the JSON;
   interleaved writes would drop the second sibling's key.

Also fixes a React "Cannot update a component while rendering a
different component" warning introduced in #188 (the account-
derivation PR). `QrLogin` was calling `onDone(...)` from inside a
`setStatus(updater)` callback — which runs during React's render
phase, so the parent's `setAddresses` / `setAuthResolved` triggered
mid-render of QrLogin. Now snapshots the resolved `SessionAddresses`
in a `useRef` inside the `onStatus` callback and calls `onDone` after
the promise resolves, outside any updater.

Why this slipped past CI: `vitest.config.ts` only picks up
`*.test.ts`, not `*.tsx`, so React-strict-mode warnings never fire
in the test suite.

Tests: 538 pass + 1 skipped (was 536). Two new regression tests:
the help helper formats SS58 + URL correctly, and
`storeSlotAccountKeysFromOutcomes` preserves sibling keys.
@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented May 19, 2026

Dev build ready — try this branch:

curl -fsSL https://raw.githubusercontent.com/paritytech/playground-cli/main/install.sh | VERSION=dev/fix/dot-init-bulletin-allowance-resilience bash

@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented May 19, 2026

E2E Test Pass · ✅ PASS

Tag: e2e-ci-pr · Branch: fix/dot-init-bulletin-allowance-resilience · Commit: 37eb5db · Run logs

Cell Result Time
pr-deploy-cdm ✅ PASS 2m19s
pr-init-session ✅ PASS 3m43s
pr-deploy-foundry ✅ PASS 1m06s
pr-deploy-frontend ✅ PASS 4m11s
pr-preflight ✅ PASS 1m41s
pr-install ✅ PASS 0m45s
pr-mod ✅ PASS 1m12s
${{ matrix.cell }} ⏭️ SKIP 0m00s
${{ matrix.cell }} ⏭️ SKIP 0m00s

Sentry traces: view spans for this run

- Move `bulletinAuthorizationUrl` into `ChainConfig` so the Summit
  devnet / mainnet swap is a single-config-row change (was a
  top-level constant that violated the "all chain URLs live in
  CONFIGS" CLAUDE.md invariant). Populated for paseo-next-v2;
  `null` on envs without a public faucet.
- `bulletinAuthorizationHelp(slot, url?)` reads the active env's
  `bulletinAuthorizationUrl` by default; renders a generic
  "propagation pending" message when no faucet is configured.
- Fix misleading `dot init` soft-failure row copy: it was
  "approve on your Polkadot mobile app…" — but the user has just
  successfully approved, the failure is People→Bulletin
  propagation, not a pending mobile prompt. Now reads
  "Bulletin authorization pending" and the error line carries the
  full diagnostic via `describe(waitErr)`.
- Drop the dead-code `waitErr instanceof Error ? msg : help(...)`
  branch — `waitForBulletinSlotAuthorization` always throws Error.
- Single timestamp per `storeSlotAccountKeysFromOutcomes` batch
  instead of one per slot (these come from the same
  `requestResourceAllocation` round-trip, semantically one cohort).
- Add the persist-before-wait invariant to CLAUDE.md under
  "Allowances / session" with the upstream root cause callout
  (XCM-relayed Bulletin allocation can fail silently as
  `LongTermStorageAllocationFailed`, same class as Mobile PR #582's
  PGAS fix not applied to storage).
- Bulletin help tests cover both explicit-URL and no-URL branches
  and pin the literal faucet path so a future config rename can't
  silently produce a wrong user-facing URL.

CLAUDE.md is 39,990 chars (back under the 40k Claude Code warning).
540 tests pass, format + license clean.
@UtkarshBhardwaj007 UtkarshBhardwaj007 merged commit df8f013 into main May 19, 2026
19 checks passed
@UtkarshBhardwaj007 UtkarshBhardwaj007 deleted the fix/dot-init-bulletin-allowance-resilience branch May 19, 2026 20:31
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant