Skip to content

fix(rs-sdk): case-insensitive .dash suffix in DPNS name resolution#3914

Merged
lklimek merged 3 commits into
v3.1-devfrom
fix/dpns-case-insensitive-dash-v3.1-dev
Jun 16, 2026
Merged

fix(rs-sdk): case-insensitive .dash suffix in DPNS name resolution#3914
lklimek merged 3 commits into
v3.1-devfrom
fix/dpns-case-insensitive-dash-v3.1-dev

Conversation

@Claudius-Maginificent

@Claudius-Maginificent Claudius-Maginificent commented Jun 16, 2026

Copy link
Copy Markdown
Collaborator

Issue being fixed or feature implemented

  • Problem: DPNS name resolution matched the .dash suffix case-sensitively. resolve_dpns_name stripped the suffix only when it was exactly ".dash", so a name like "Alice.DASH" or "alice.Dash" was treated as a bare label including the suffix and normalized whole — producing the wrong normalizedLabel and failing to resolve a name that does exist.
  • What breaks without it: a user enters "alice.DASH" (or any mixed-case suffix) → the SDK queries normalizedLabel == "a11ce.dash" instead of "a11ce" → resolution returns "not found" for a registered name. Separately, empty/suffix-only input ("", ".dash") triggers a needless DPNS contract fetch + network round-trip before failing.

What was done?

Scoped entirely to packages/rs-sdk/src/platform/dpns_usernames/mod.rs:

  • Added extract_dpns_label — strips a trailing .dash suffix matched case-insensitively via eq_ignore_ascii_case, leaving non-.dash inputs untouched.
  • Added normalize_dpns_label — combines suffix extraction with convert_to_homograph_safe_chars, giving a single value suitable for matching the normalizedLabel field of domain documents.
  • resolve_dpns_name and is_dpns_name_available both route input through normalize_dpns_label, and both short-circuit on an empty normalized label before fetching the DPNS contract — no wasted network call for "" / ".dash". The two APIs agree on malformed input.
  • Unit tests test_extract_dpns_label and test_normalize_dpns_label_strips_dash_suffix_case_insensitively cover mixed-case suffixes, non-.dash suffixes, and empty/suffix-only inputs.

How Has This Been Tested?

  • cargo build -p dash-sdk — clean.
  • cargo test -p dash-sdk --lib -- platform::dpns_usernames::tests5 passed, 0 failed (2 new + 3 pre-existing, no regressions).
  • cargo fmt -p dash-sdk -- --check — clean.
  • cargo clippy -p dash-sdk — no new warnings.

Breaking Changes

None. The public parameter of is_dpns_name_available is named name (it accepts both a bare label and a full name.dash form). Rust has no named arguments, so this is not an API/ABI break.

Checklist:

  • I have performed a self-review of my own code
  • I have commented my code, particularly in hard-to-understand areas
  • I have added or updated relevant unit/integration/functional/e2e tests
  • I have added "!" to the title and described breaking changes in the corresponding section if my code contains any
  • I have made corresponding changes to the documentation if needed

For repository code-owners and collaborators only

  • I have assigned this pull request to a milestone

🤖 Co-authored by Claudius the Magnificent AI Agent

Summary by CodeRabbit

Release Notes

  • Bug Fixes

    • Improved DPNS name availability validation to properly handle edge cases such as empty labels and inputs containing only the domain suffix.
    • Enhanced DPNS name resolution with better input validation before processing.
  • Tests

    • Added comprehensive test coverage for DPNS name normalization and label extraction logic.

Extracted from PR #3585 (fix 1 of 2). resolve_dpns_name now compares the
.dash suffix case-insensitively via eq_ignore_ascii_case, and both
resolve_dpns_name and is_dpns_name_available skip the DPNS contract fetch
for empty/normalized-empty labels (treating them as not-found rather than
erroring or making a needless network call).

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@coderabbitai

coderabbitai Bot commented Jun 16, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: c1f5ef67-cf2d-41d2-8d26-9b20feea9def

📥 Commits

Reviewing files that changed from the base of the PR and between 60ee19f and 11666c6.

📒 Files selected for processing (1)
  • packages/rs-sdk/src/platform/dpns_usernames/mod.rs

📝 Walkthrough

Walkthrough

Two private helper functions (extract_dpns_label and normalize_dpns_label) are added to the DPNS usernames module. Sdk::is_dpns_name_available and Sdk::resolve_dpns_name are updated to use these helpers for label normalization, with early-return guards for empty normalized labels. Unit tests for both helpers are added.

Changes

DPNS Label Normalization

Layer / File(s) Summary
extract_dpns_label and normalize_dpns_label helpers
packages/rs-sdk/src/platform/dpns_usernames/mod.rs
Two new private functions: extract_dpns_label strips an optional case-insensitive .dash suffix; normalize_dpns_label applies homograph-safe character mapping to the extracted label.
Integration into is_dpns_name_available and resolve_dpns_name, with tests
packages/rs-sdk/src/platform/dpns_usernames/mod.rs
Both public SDK methods now delegate normalization to normalize_dpns_label. is_dpns_name_available returns Ok(false) and resolve_dpns_name returns Ok(None) when the normalized label is empty. Prior inline .dash suffix parsing in resolve_dpns_name is removed. Unit tests cover case-insensitive stripping, non-.dash suffixes, and empty/suffix-only inputs.

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~10 minutes

Suggested labels

ready for final review

Suggested reviewers

  • QuantumExplorer
  • shumkov

Poem

🐇 A dot-dash suffix, now stripped with care,
Homograph shadows banished from there.
Empty labels get a gentle "no,"
Resolved names return a tidy None so.
Normalization hops, neat and square! ✨

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately captures the main change: fixing case-insensitive .dash suffix matching in DPNS name resolution, which aligns with the primary objective and code modifications in the changeset.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/dpns-case-insensitive-dash-v3.1-dev

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@lklimek lklimek marked this pull request as ready for review June 16, 2026 09:07
@thepastaclaw

thepastaclaw commented Jun 16, 2026

Copy link
Copy Markdown
Collaborator

✅ Review complete (commit 371877a)

@thepastaclaw thepastaclaw left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

Small, well-scoped client-SDK fix that adds case-insensitive .dash suffix handling and aligns the empty-label short-circuit between is_dpns_name_available and resolve_dpns_name. No consensus or FFI surface area is touched. Two non-blocking suggestions remain around test coverage of the new short-circuit and of the updated fetch_many non-existence semantics.

🟡 2 suggestion(s)

1 additional finding(s) omitted (not in diff).

🤖 Prompt for all review comments with AI agents
These findings are from an automated code review. Verify each finding against the current code and only fix it if needed.

In `packages/rs-sdk/src/platform/dpns_usernames/mod.rs`:
- [SUGGESTION] packages/rs-sdk/src/platform/dpns_usernames/mod.rs:397-411: Add a test that locks in the empty-label short-circuit before the contract fetch
  The new guard makes `""` / `".dash"` / `".DASH"` return `Ok(false)` (and `Ok(None)` in `resolve_dpns_name`) without invoking `fetch_dpns_contract` or the network. This is the second observable contract change in this PR and is one of the motivations called out in the PR description. The existing tests only assert that `normalize_dpns_label("")` equals `""`; they don't pin down that the early return happens before `fetch_dpns_contract` requires a context provider. A small test (e.g. invoking `is_dpns_name_available` / `resolve_dpns_name` on a `Sdk` with no context provider and asserting `Ok(false)` / `Ok(None)`) would catch a future reordering that re-introduces the wasted round-trip.
- [SUGGESTION] packages/rs-sdk/src/platform/dpns_usernames/mod.rs:439-446: Pin down `fetch_many` non-existence semantics with a test
  The check changed from `is_empty()` to `documents.values().all(|d| d.is_none())`, and the new comment asserts that a proven non-existence comes back as a non-empty `BTreeMap` whose values are all `None`. `all()` on an empty iterator returns `true`, so if `fetch_many` can ever surface an empty map (e.g. via a transport path that doesn't error out), `is_dpns_name_available` would now report "available" — the opposite of the prior `is_empty()` behavior. The change is plausibly correct and matches the SDK's general proof-handling style, but there is no test in this PR that pins the contract down. A unit/mock test covering (a) empty map, (b) map with one `None`, (c) map with one `Some(doc)` would prevent silent regressions.

Comment thread packages/rs-sdk/src/platform/dpns_usernames/mod.rs

@thepastaclaw thepastaclaw left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

Cumulative re-review at fe79734. The latest push (since 11666c6) is only a v3.1-dev merge with no DPNS-relevant delta; the case-insensitive .dash stripping, shared normalize_dpns_label, empty-label short-circuit, and fetch_many semantics change carry over unchanged. No new latest-delta findings. Both prior findings are carried forward as STILL VALID — added unit tests cover only pure helpers (normalize_dpns_label, extract_dpns_label), not the public async behavior contracts that this PR newly establishes.

🟡 2 suggestion(s)

1 additional finding(s) omitted (not in diff).

🤖 Prompt for all review comments with AI agents
These findings are from an automated code review. Verify each finding against the current code and only fix it if needed.

In `packages/rs-sdk/src/platform/dpns_usernames/mod.rs`:
- [SUGGESTION] packages/rs-sdk/src/platform/dpns_usernames/mod.rs:397-411: Carried forward (STILL VALID): no test pins the empty-label short-circuit before contract fetch
  Verified against current head. The empty-label guard at lines 409-411 in `is_dpns_name_available` (and the mirror at lines 467-469 in `resolve_dpns_name`) is one of the two observable behavior changes this PR introduces — `""`, `".dash"`, and `".DASH"` now return `Ok(false)` / `Ok(None)` without invoking `fetch_dpns_contract` or any network round-trip. The newly added tests at lines 530-562 only exercise the pure `normalize_dpns_label` / `extract_dpns_label` helpers and never call the two public async methods, so a regression that moves the guard below `fetch_dpns_contract().await?` would silently re-introduce the wasted round-trip and slip through CI. Add a mock-SDK test that calls `is_dpns_name_available` / `resolve_dpns_name` with empty / suffix-only inputs and asserts the call resolves to `Ok(false)` / `Ok(None)` without the contract fetch path being reached.
- [SUGGESTION] packages/rs-sdk/src/platform/dpns_usernames/mod.rs:439-446: Carried forward (STILL VALID): fetch_many non-existence semantics still untested
  Verified against current head. The availability predicate was flipped from `documents.is_empty()` to `documents.values().all(|d| d.is_none())`, and the accompanying comment asserts that a proven non-existence comes back as a non-empty `BTreeMap` whose values are all `None`. The risk is asymmetric: if `fetch_many` ever returns an empty map (transport/proof edge case, mock SDK path, or future refactor), `all()` on an empty iterator returns `true` and the name is reported available — the inverse of the pre-PR behavior, and a security-relevant misclassification because callers may gate registration attempts on this. Conversely a `{id -> None}` response now correctly reports available where the old `is_empty()` check would have reported taken (this is the bug being fixed). Add focused unit/mock coverage for the three observable cases: (a) empty map → expected `false` (treat as proof not seen), (b) map containing only `None` → expected `true`, (c) map containing `Some(document)` → expected `false`. This locks the `fetch_many` contract in place so it can't silently regress.

Comment thread packages/rs-sdk/src/platform/dpns_usernames/mod.rs
Empty commit to start a fresh Tests run that reads the updated
UBUNTU_BACKUP_ENABLED repo variable (in-flight runs captured the old value).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

@thepastaclaw thepastaclaw left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

Verified at 371877a — implementation correctly fixes case-insensitive .dash suffix handling and short-circuits empty normalized labels. Latest delta is a CI-retrigger with no file changes, so two prior test-coverage suggestions are carried forward unchanged: the empty-label short-circuit and the fetch_many non-existence semantics still lack public-API tests pinning the new contracts.

🟡 1 suggestion(s)

1 additional finding(s) omitted (not in diff).

1 carried-forward finding(s) already raised on this PR; not re-posting as new inline comments.

🤖 Prompt for all review comments with AI agents
These findings are from an automated code review. Verify each finding against the current code and only fix it if needed.

In `packages/rs-sdk/src/platform/dpns_usernames/mod.rs`:
- [SUGGESTION] packages/rs-sdk/src/platform/dpns_usernames/mod.rs:439-446: Carried forward: `values().all(|d| d.is_none())` non-existence semantics are not pinned by a test
  Verified at HEAD 371877ae. The availability check moved from `documents.is_empty()` to `documents.values().all(|d| d.is_none())` and now depends on a non-obvious contract: `Document::fetch_many` returns a `BTreeMap<Identifier, Option<Document>>` where proven non-existence comes back as a non-empty map with `None` values. The inline comment at lines 441–445 captures this correctly, but no test exercises either shape (empty map vs. all-`None` map) against `is_dpns_name_available`. If the SDK/Drive proof-verifier layer ever changes the shape — e.g. returns an empty map for non-existence proofs — the predicate flips silently from "available" to "taken" with no regression signal. A mock-based test using the existing SDK mock-mode harness would lock in the contract this caller now relies on.

@lklimek

lklimek commented Jun 16, 2026

Copy link
Copy Markdown
Contributor

One test failing due to v3.1-dev issue, not related to our changes, fixed in #3900 .

@lklimek lklimek merged commit 59799aa into v3.1-dev Jun 16, 2026
20 of 22 checks passed
@lklimek lklimek deleted the fix/dpns-case-insensitive-dash-v3.1-dev branch June 16, 2026 10:52
@codecov

codecov Bot commented Jun 16, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 72.19%. Comparing base (e2039e5) to head (371877a).
⚠️ Report is 19 commits behind head on v3.1-dev.

Additional details and impacted files
@@             Coverage Diff              @@
##           v3.1-dev    #3914      +/-   ##
============================================
+ Coverage     71.20%   72.19%   +0.99%     
============================================
  Files            20       21       +1     
  Lines          2837     2946     +109     
============================================
+ Hits           2020     2127     +107     
- Misses          817      819       +2     
Components Coverage Δ
dpp ∅ <ø> (∅)
drive ∅ <ø> (∅)
drive-abci ∅ <ø> (∅)
sdk ∅ <ø> (∅)
dapi-client ∅ <ø> (∅)
platform-version ∅ <ø> (∅)
platform-value ∅ <ø> (∅)
platform-wallet ∅ <ø> (∅)
drive-proof-verifier ∅ <ø> (∅)
🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

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.

4 participants