Skip to content

fix(truapi): resolve dotNS usernames from old and legacy gateway claims - #1282

Open
tommyldev wants to merge 1 commit into
mainfrom
fix/dotns-username-lapsed-claims
Open

tommyldev wants to merge 1 commit into
mainfrom
fix/dotns-username-lapsed-claims

Conversation

@tommyldev

Copy link
Copy Markdown
Contributor

Fixes paritytech/platform-bugs#39: pairing hosts (Polkadot Desktop, dot.li) return "No primary username for this session" from getUserId.

Problem

Users lose their username in pairing hosts a week after onboarding. Users onboarded before about 2026-09-08 have none at all. iOS isn't affected because it reads the name from People Resources.Consumers.

Root cause

Full analysis: https://github.com/paritytech/platform-bugs/issues/39#issuecomment-6034324294

  1. pending_claim_labels dropped claims with mintedAt + reservationDuration < now. That mirrored the controller at Person's usernames: read from Asset Hub dotNS instead of the People Chain (rebase of #349) #426, but dotns 31dc7544 removed expirePendingClaim, and _settlePending now settles a claim of any age. On the deployed controller 0xCC93…8a4b, expirePendingClaim reverts the same way an unknown selector does. Nothing calls claimLabelStore, so names stay pending and dropped out of resolution after 7 days.
  2. Accounts minted before the dotted-label switch hold an undotted pending claim (tommyio01). isPopIssued is false for both tommyio01 and tommyio.01, so classify_labels dropped it. The gateway pallet itself records the dotted name in DotnsGateway.AccountNames[account].lite.

Fix

  • Removed the lapse filter and its reservationDuration and Timestamp.Now reads from pending_claim_labels. Docs no longer say claims lapse.
  • New gateway_lite_username: reads DotnsGateway.AccountNames[account] (Blake2_128Concat, AccountNameRecord { lite: Option<NameEntry { label: BaseLabel, chat: Option<ChatKey> }>, full }; layout checked against paseo-next-v2 metadata). It decodes the lite label and accepts it only if it is is_dotted_lite_username and LiteLabelOwner[label] == account. Only the gateway writes either map.
  • New resolve_identity = resolve_labels → classify_labels, falling back to gateway_lite_username when no lite username came from the contracts. The in-core lookup (runtime/identity.rs) and the CLI AssetHubReader::dotns_identity both use it, so they still resolve the same way. In the in-core lookup, an account with no username at all now counts as "no record", so the root-key fallback runs.

Tests

  • dotns_gateway unit tests:
    • a claim minted at the epoch still resolves
    • a legacy undotted, non-pop-issued claim resolves through AccountNames
    • a LiteLabelOwner mismatch or missing entry is rejected
    • an undotted gateway record is rejected
    • the contract lite name wins over the gateway record
    • a decode of the live AccountNames bytes for 0x4caa…1a78
    • account_names_key layout
  • runtime::identity scripted-node tests:
    • the existing end-to-end test now uses an ancient claim, with no reservationDuration or timestamp scripted; it makes 10 views instead of 11
    • new: a legacy account resolves legacy.01 from AccountNames over the chainHead_v1 follow
  • cargo test -p truapi: 1290 lib tests plus the integration tests pass. cargo clippy -p truapi -p truapi-host-cli --all-targets is clean. cargo fmt applied.

Live evidence (paseo-next-v2)

New ignored test live_asset_hub_resolves_a_legacy_lite_name_through_the_gateway_record:

live DotnsPopController: 0xcc932348606cc1f3318cadec5a5cd2ca447f8a4b
live legacy account 0x4caa74c50849af0e9069b6bfae893057ef54f19ea2f820b6e451d2404df91a78: labels=["tommyio01"] contract_only=DotnsIdentity { lite_username: None, full_username: None } resolved=DotnsIdentity { lite_username: Some("tommyio.01"), full_username: None }
test live_asset_hub_resolves_a_legacy_lite_name_through_the_gateway_record ... ok

Before this change the same account resolved nothing: its claim is past reservationDuration, and even without the lapse filter the contract path gives contract_only = None.

Note: the existing ignored live_asset_hub_resolves_a_settled_store_over_dotns_discovery fails its final classify_labels assertion against the current chain. None of the e2e pool's public-registrar labels are pop-issued. That test uses only classify_labels, which this PR doesn't change.

@tommyldev
tommyldev requested a review from a team October 7, 2026 09:02
@github-actions github-actions Bot added the rust Pull requests that update rust code label Oct 7, 2026
@github-actions

github-actions Bot commented Oct 7, 2026 •

Copy link
Copy Markdown
Contributor

Bundle size report

Compared with main at 10a36af.

Raw Gzip Brotli
truapi-host 7.87 MiB (+1.2 KiB) 5.65 MiB (+680 B) 5.38 MiB (+824 B)
truapi-provider 4.03 MiB 1.27 MiB 960.2 KiB
truapi 6.29 MiB 890.4 KiB 729.2 KiB
Total 18.19 MiB (+1.2 KiB) 7.80 MiB (+680 B) 7.03 MiB (+824 B)

WebAssembly modules

Raw Gzip Brotli
truapi-host/wasm/web/truapi_server_bg.wasm 2.76 MiB (+1.2 KiB) 984.2 KiB (+681 B, +0.1%) 741.8 KiB (+809 B, +0.1%)
truapi-host/wasm/web/truapi_verifiable_bg.wasm 4.89 MiB 4.64 MiB 4.61 MiB
truapi-provider/truapi_provider_bg.wasm 3.99 MiB 1.26 MiB 952.4 KiB
Changed files (1)
Raw Gzip Brotli
truapi-host/wasm/web/truapi_server_bg.wasm 2.76 MiB (+1.2 KiB) 984.2 KiB (+681 B, +0.1%) 741.8 KiB (+809 B, +0.1%)

Commit: 7bcb41e

@github-actions

github-actions Bot commented Oct 7, 2026 •

Copy link
Copy Markdown
Contributor

CI Status: not green, 1 item needs attention.

  • wasm-provider: failure
All job results
job result
android-bindings success
bundle-size success
changes success
changeset-guard success
cli-package success
codegen success
e2e skipped
explorer success
headless-install success
host-android-bindings success
host-android-detekt success
host-wasm success
ios-bindings success
ios-swift success
licenses success
playground success
provider-android-bindings skipped
release-guard success
rust success
ts-client success
ts-debugger success
ts-host success
wasm-provider failure
workflow-lint skipped

Signing credentials: failure as of 2026-10-14, a release may fail

Commit 7bcb41e3 · run log

@tommyldev

Copy link
Copy Markdown
Contributor Author

Seems to work on latest DotLI version

Pending claims no longer lapse after the controller's reservationDuration:
the deployed DotnsPopController removed expirePendingClaim and settles a
claim of any age, so dropping week-old claims lost every user's name.

When the contract labels yield no lite username, fall back to the gateway
pallet's DotnsGateway.AccountNames[account].lite.label, accepted only as a
dotted lite username whose DotnsGateway.LiteLabelOwner is the same account.
This covers accounts onboarded before dotted labels, whose undotted claim
fails isPopIssued in both spellings.

Both the in-core lookup and the CLI reader go through resolve_identity.

Fixes paritytech/platform-bugs#39
@peetzweg
peetzweg force-pushed the fix/dotns-username-lapsed-claims branch from da6e5fe to 7bcb41e Compare October 14, 2026 09:04
@peetzweg

Copy link
Copy Markdown
Member

Rebased onto main. The conflicts were only the formatting/comment pass in #1356; the fix itself is unchanged. fmt, clippy and cargo test -p truapi --lib pass.

This is now biting outside the pairing hosts, so asking for a review:

Could this get a review and go out in the next truapi-host / Desktop / dot.li release?

@pgherveou

Copy link
Copy Markdown
Collaborator

do we even need to support these legacy gateway claim?

@peetzweg

Copy link
Copy Markdown
Member

Split the part everyone needs into #1419: it only stops dropping pending claims after 7 days, which is what breaks every lite name today. The legacy undotted-name fallback stays here, so whether to support it doesn't block that fix.

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

rust Pull requests that update rust code

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants