### Component Proof of Personhood ### Priority P1 ### Summary **Full usernames registered after the migration exist only on Asset Hub / dotNS, but the mobile app can only resolve usernames by accountId (and subscribe to changes) via People Chain — which full usernames never reach.** Username management is migrating from People Chain (`pallet_resources`, written by the Identity Backend during `attest`) to dotNS on Asset Hub. The two flows now behave differently: - **Lite username (claim)**: the app calls the Identity Backend, which dual-writes — `attest` on People Chain **and** `pallet_dotns_gateway::reserve_name` on Asset Hub. Lite usernames therefore exist on **both** chains. - **Full username (registration/claim as full person)**: the app submits `pallet_dotns_gateway::register_name` **directly on Asset Hub** (V5 general transaction, `AsDotnsGateway` extension). The Identity Backend is not involved, and nothing writes the full username to People Chain. The app's only practical way to resolve usernames **by accountId** — and to *subscribe* to changes — is still People Chain: - `Resources::Consumers[accountId]` is a single storage read keyed by the account, returning `ConsumerInfo { liteUsername, fullUsername, identifierKey (chat ECDH key) }`, and it works with standard substrate storage subscriptions (no polling). - The app uses it for: own username status (with on-chain subscription), resolving contacts by accountId, chat identity (the ECDH `identifierKey`), incoming chat requests, backup/recovery of the username after reinstall or on a new device. On the Asset Hub side there is **no equivalent API**: - `pallet_dotns_gateway` storage offers only: - `LiteLabelOwner: label → owner` — the wrong direction for accountId lookup; - `AccountAlias: accountId → alias` — proves *that* the account has an alias (we use it as a "has full username" flag via subscription), but does **not** contain the username itself. - The dotNS contract can answer reverse lookups only via a chain of 5–6 sequential view calls (`controller → protocolRegistry() → get("storeFactory") → getLabelStore(h160) → getLabels()`, plus `pendingClaim(s)` for the cold path — reference implementation: `resolve_labels` in [paritytech/host-rust-core#426](https://github.com/paritytech/host-rust-core/pull/426)). These are one-shot `eth_call`-style reads: **not subscribable** through substrate storage subscriptions, so the app would have to poll. - Contract events (`NameRegistered`, `BaseNameClaimed`, `LiteNameReserved`, … in `IDotnsPopController.sol`, indexed by user + labelhash) would allow `eth_getLogs` polling, but that is still polling; `eth_subscribe` is only available from stable2606. **Consequences:** 1. **Full usernames are invisible to other users.** Contacts are resolved via People Chain, where a post-migration full username never lands. A full person is shown to peers by their lite (or stale legacy) name. 2. **The user's own full username string cannot be read back from chain.** The app currently merges: PC `ConsumerInfo` (name strings) + AH `AccountAlias` existence flag + a *locally stored* full-name string. On reinstall or a second device, recovery reads People Chain only — the full username is unrecoverable; the account degrades to its lite name. 3. **Chat identity depends on PC.** The ECDH chat key is read from PC `ConsumerInfo`, even though dotNS also stores it in the registry. 4. **Circular dependency on the "temporary" dual-write.** The spec says PC data duplication is temporary, "solely for backwards compatibility till the app is upgraded". But the app *cannot* be upgraded off People Chain until Asset Hub offers resolve-by-accountId (ideally subscribable); and People Chain writes cannot be decommissioned until the app migrates. If the backend stops the PC dual-write before this lands, username resolution and recovery break entirely. ### Proposal A queryable — and ideally *subscribable* — `accountId → username(s)` source on Asset Hub. Options, from our perspective: 1. **Pallet-level storage map** in `pallet_dotns_gateway` (accountId → lite/full labels, or at least the full label). This is the best fit for mobile: one storage key per account, works with existing substrate storage subscriptions, no polling. 2. **#216 / #217** (`names permanent & queryable by address`, `profileOf(address)` — lite/full names, chat key, link, tier in a single view call). This solves the *query* side in one call, but contract views are still not subscribable, so the app would need `eth_getLogs`/interval polling on top. Workable, strictly worse than (1) for battery/network. Concrete questions: - Is a pallet-side accountId → name mapping (option 1) feasible, or is the contract path (#216/#217) the only planned direction? - What is the timeline for #216/#217? - How long is the People Chain dual-write guaranteed to stay? Every release we ship in the meantime deepens the dependency on it (recovery, chats, contact resolution). Related: #214 (gateway cleanup umbrella), #215, #216 (names queryable by address), #217 (`profileOf(address)`); [paritytech/host-rust-core#426](https://github.com/paritytech/host-rust-core/pull/426) — the reference host is making the same PC → AH dotNS resolution move, so resolve-by-address is needed beyond mobile. App-side dependency points (Android): `RealUsernamesOfAccountUseCase` (own status: PC `ConsumerInfo` + AH `AccountAlias` flag + local string), `RealResolveUsernamesUseCase` (contacts via PC `resolveConsumers`), `RealRecoverUsernameUseCase` (recovery via PC only), `UsernameOnChainUpdater` (PC storage subscription), `AccountAliasUpdater` (AH `AccountAlias` subscription — flag only), chats (`SyncContactUsernameUseCase`, `IncomingChatRequestProcessor`, `StartChatDataUseCase`), backup (`BackupFoundInteractor`). ### Acceptance criteria - [ ] Given an accountId (or its h160), the app can obtain the account's lite and full usernames from Asset Hub without People Chain involvement. - [ ] Username changes for an account are observable without client-side interval polling (substrate storage subscription), or a documented polling contract (`eth_getLogs` on gateway events) is confirmed as the intended mechanism. - [ ] The chat ECDH key is obtainable from Asset Hub/dotNS by accountId as part of the same lookup. - [ ] A stated support window for the People Chain dual-write that the app team can plan releases against.