Skip to content

feat(sdk)!: pure DPNS and DashPay document builders shared with embedders - #4632

Open
PastaPastaPasta wants to merge 2 commits into
v4.2-devfrom
feat/shared-dpns-dashpay-builders
Open

feat(sdk)!: pure DPNS and DashPay document builders shared with embedders#4632
PastaPastaPasta wants to merge 2 commits into
v4.2-devfrom
feat/shared-dpns-dashpay-builders

Conversation

@PastaPastaPasta

@PastaPastaPasta PastaPastaPasta commented Sep 8, 2026

Copy link
Copy Markdown
Member

Issue being fixed or feature implemented

Carries forward the pure-builder half of #4619, without that PR's request-driven FromProof<GetDocumentsRequest> verifier, which the SDK-first C++ embedding (next PR in this series) no longer needs: the SDK retains the rich query it built and verifies against it, so no wire request is reconstructed from bytes.

dash-sdk's register_dpns_name and create_contact_request assemble DPNS and DashPay documents inline. An embedder that signs with a wallet-held key (Dash Core's platform GUI) needs the same assembly as a pure function over caller-supplied entropy, salt and ciphertexts, and must not reimplement it in C++.

What was done?

Pure document builders, in dash-platform-queries:

  • dpns_usernames::{build_dpns_preorder_document, build_dpns_domain_document, salted_domain_hash} and dashpay::build_contact_request_document, the assembly halves of dash-sdk's register_dpns_name / create_contact_request as pure functions. dash-sdk's networked flows now call them.
  • They reuse what exists rather than re-deriving it: normalization is dpp's consensus convert_to_homograph_safe_chars (the crate's ASCII-only copy is replaced by a re-export); the preorder commitment uses dpp::util::hash::hash_double; property names come from the dpns-contract / dashpay-contract constants; the DashPay byte bounds are read from the contract schema's DocumentPropertyType sizes instead of being hard-coded to the same numbers.

This is a move, not a rewrite. The documents the builders produce are byte for byte what the inline SDK code produced. The normalization swap is the only substitution, and the two implementations agree on every label the contract's ASCII-only pattern admits — they differ only on non-ASCII input, which consensus rejects anyway. The re-export additionally makes the crate agree with the DPNS data trigger.

Deliberately not in this PR

Two behaviour changes were dropped to keep this reviewable as a pure extraction. Both are worth doing on their own:

  • No new label validation. An earlier revision rejected labels via is_valid_username before the preorder was paid for. That helper is stricter than the DPNS contract — it also refuses consecutive hyphens, which the contract's ^[a-zA-Z0-9][a-zA-Z0-9-]{0,61}[a-zA-Z0-9]$ pattern admits — so it would have refused names Platform accepts, on paths (wasm-sdk, platform-wallet) that previously had no local check at all. Failing early on a bad label is a real improvement, but it should check the contract's actual pattern, and it belongs in its own PR alongside a fix to is_valid_username's docstring, which currently claims the pattern forbids consecutive hyphens.
  • No entropy/document-id check in dpp. dash-sdk keeps its existing private ensure_entropy_matches_document_id. Hoisting it into DocumentCreateTransitionV0::from_document so every caller (SDK, wasm, FFI, embedders) inherits it is a good change — it cannot change any outcome, since it only refuses transitions Drive would reject after the nonce bump — but it adds an error path to a shared crate and is separable from this move.

packages/rs-dpp and put_document.rs are therefore byte-identical to v4.2-dev in this PR.

The autoAcceptProof bound is still checked in create_contact_request before the recipient lookup. The shared builder re-checks it against the schema, but that field is raw caller input and the lookup is a network round trip, so the early rejection is preserved. The two checks on the SDK's own encryption output are dropped as unreachable code — the pre-existing COMPACT_XPUB_LEN guard forces the encrypted xpub to 96 bytes, and fit_account_label bounds the encrypted label to 48-80 — and the builder covers both for embedders doing their own encryption.

How Has This Been Tested?

  • dash-platform-queries: 60 lib tests pass, including builder tests against the real DPNS/DashPay system contracts (preorder commitment matches the domain document's salt+label, id derivation, schema byte-bound enforcement).
  • dash-sdk --lib: 187 pass. cargo check clean for platform-wallet, rs-sdk-ffi, strategy-tests, rs-scripts. cargo fmt --check clean.
  • Byte-parity check: a scratch test reproduced the pre-PR inline assembly verbatim (including the old ASCII normalizer and the old sha256d helper) and asserted document equality against the new builders, across several DPNS labels — including alice--bob, -bad and ab, which the dropped gate used to reject — and a contact request with every optional field populated. All equal. Not committed; it exists only to prove the extraction.
  • Not done here: a live-network run of register_dpns_name / send_contact_request. The on-wire behaviour is byte-identical by construction and the unit tests pin the property maps.

Breaking Changes

API shape on unreleased v4.2-dev (not on crates.io): dash_sdk::platform::dashpay::ContactRequestResult now carries the assembled document plus entropy instead of id / owner_id / properties. No consumer outside rs-sdk uses it. No behavioural breaking changes.

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

Summary by CodeRabbit

  • New Features

    • Added helpers for creating DashPay contact request documents with encrypted-field validation.
    • Added helpers for creating DPNS preorder and domain documents, including label normalization and salted hash generation.
    • Exposed document-building utilities through the DashPay and DPNS SDK modules.
  • Improvements

    • Contact request and DPNS registration workflows now share document construction and validation.
    • Improved handling of data contract validation errors.
  • Tests

    • Expanded automated coverage for contact requests and DPNS document creation.

@coderabbitai

coderabbitai Bot commented Sep 8, 2026

Copy link
Copy Markdown
Contributor

Review Change StackReview Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Advanced

Run ID: 4ad0b256-5ee5-493c-95b0-9993ddf64813

📥 Commits

Reviewing files that changed from the base of the PR and between cfb93ac and 796a86b.

📒 Files selected for processing (2)
  • .github/workflows/tests-rs-workspace.yml
  • packages/dash-platform-queries/src/dashpay.rs

Included review availability: Your plan provides up to 1 included review per hour; 0 remain after this review.


📝 Walkthrough

Walkthrough

The change adds transport-free DashPay contact request and DPNS document builders. The SDK now uses these builders for document creation, validation, identifier generation, and submission. The builders are publicly re-exported through the platform SDK modules.

Changes

Document builder foundation

Layer / File(s) Summary
DPNS document builders
packages/dash-platform-queries/src/dpns_usernames.rs, packages/dash-platform-queries/Cargo.toml, packages/rs-sdk/src/platform/dpns_usernames/mod.rs, .github/workflows/tests-rs-workspace.yml
The shared DPNS helpers build preorder and domain documents, normalize labels through dpp, compute salted domain hashes, and derive document IDs from entropy. The workflow now runs tests for dash-platform-queries.
DashPay contact request builder
packages/dash-platform-queries/src/dashpay.rs, packages/dash-platform-queries/src/error.rs, packages/dash-platform-queries/src/lib.rs, packages/rs-sdk/src/platform/dashpay/mod.rs
The shared DashPay helper validates encrypted fields against contract schema bounds and assembles contact request documents with entropy-derived IDs. The helper and parameter type are publicly re-exported.
DashPay SDK integration
packages/rs-sdk/src/platform/dashpay/contact_request.rs
Contact request creation returns the assembled document and entropy. Submission reuses the document directly.
DPNS SDK integration
packages/rs-sdk/src/platform/dpns_usernames/mod.rs
DPNS registration delegates document construction to the shared helpers and re-exports the new APIs.

Priority: ➖ Normal

Estimated code review effort: 4 (Complex) | ~45 minutes

Sequence Diagram(s)

sequenceDiagram
  participant DashPaySDK
  participant ContactRequestBuilder
  participant DashpayContract
  participant Platform
  DashPaySDK->>ContactRequestBuilder: Build contact request parameters
  ContactRequestBuilder->>DashpayContract: Resolve contactRequest schema
  ContactRequestBuilder-->>DashPaySDK: Return validated document and entropy
  DashPaySDK->>Platform: Submit the document with entropy
  Platform-->>DashPaySDK: Return submission result
Loading
sequenceDiagram
  participant DPNSSDK
  participant DPNSBuilders
  participant DPNSContract
  participant Platform
  DPNSSDK->>DPNSBuilders: Build preorder and domain documents
  DPNSBuilders->>DPNSContract: Resolve preorder and domain schemas
  DPNSBuilders-->>DPNSSDK: Return entropy-derived documents
  DPNSSDK->>Platform: Register the documents
  Platform-->>DPNSSDK: Return registration result
Loading

Merge Risk: ⚪ Minimal · up to 796a8

The change centralizes DPNS and DashPay document construction while preserving the existing SDK document behavior. No actionable correctness, security, or availability risk remains identified for merge.

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 79.17% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 24 functions across 7 files. (1 skipped: … Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly summarizes the main change: extracting pure DPNS and DashPay document builders for sharing with embedders. It is concise and specific.
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.
Full details: Docstring Coverage

Explanation

Docstring coverage is 79.17% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 24 functions across 7 files. (1 skipped: 1 unsupported.)

  • Fix all pre-merge checks with AI
✨ Finishing Touches 💡 1
📝 Generate docstrings 💡
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/shared-dpns-dashpay-builders

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.

@PastaPastaPasta
PastaPastaPasta force-pushed the build/vendor-locked-single-source branch from 05927f9 to 1e8f252 Compare September 8, 2026 21:16
@PastaPastaPasta
PastaPastaPasta force-pushed the feat/shared-dpns-dashpay-builders branch from 0631c07 to 5279114 Compare September 8, 2026 21:16
Base automatically changed from build/vendor-locked-single-source to v4.2-dev September 8, 2026 21:57
@github-actions github-actions Bot added this to the v4.2.0 milestone Sep 8, 2026
@PastaPastaPasta
PastaPastaPasta force-pushed the feat/shared-dpns-dashpay-builders branch 2 times, most recently from 47b05e3 to cfb93ac Compare September 10, 2026 19:36
@PastaPastaPasta
PastaPastaPasta marked this pull request as ready for review September 10, 2026 19:37
@thepastaclaw

thepastaclaw commented Sep 10, 2026

Copy link
Copy Markdown
Collaborator

✅ Final review complete — no blockers (commit 796a86b) · triage: normal

@codecov

codecov Bot commented Sep 10, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 92.94872% with 22 lines in your changes missing coverage. Please review.
✅ Project coverage is 87.41%. Comparing base (63cf57f) to head (796a86b).
⚠️ Report is 1 commits behind head on v4.2-dev.

Files with missing lines Patch % Lines
packages/dash-platform-queries/src/dashpay.rs 93.45% 11 Missing ⚠️
...ckages/dash-platform-queries/src/dpns_usernames.rs 92.85% 10 Missing ⚠️
packages/dash-platform-queries/src/error.rs 75.00% 1 Missing ⚠️
Additional details and impacted files
@@             Coverage Diff              @@
##           v4.2-dev    #4632      +/-   ##
============================================
- Coverage     87.86%   87.41%   -0.45%     
============================================
  Files          2766     2797      +31     
  Lines        360981   366966    +5985     
============================================
+ Hits         317162   320801    +3639     
- Misses        43819    46165    +2346     
Components Coverage Δ
dpp 87.73% <ø> (-1.38%) ⬇️
drive 86.52% <ø> (-0.07%) ⬇️
drive-abci 89.86% <ø> (+0.03%) ⬆️
sdk ∅ <ø> (∅)
dapi-client ∅ <ø> (∅)
platform-version ∅ <ø> (∅)
platform-value 92.92% <ø> (ø)
platform-wallet ∅ <ø> (∅)
drive-proof-verifier 49.78% <ø> (ø)
🚀 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.

…ders

dash-platform-queries gains build_dpns_preorder_document /
build_dpns_domain_document / salted_domain_hash (dpns_usernames) and
build_contact_request_document (new dashpay module): the document-assembly
halves of dash-sdk's register_dpns_name and create_contact_request as pure
functions that take caller-supplied entropy, salt and ciphertexts and touch no
network or randomness. dash-sdk's networked flows now call them, so an embedder
that assembles its own transitions (the Dash Core platform GUI) and the SDK
share one implementation.

This is a move, not a rewrite: the documents these builders produce are byte
for byte what the inline SDK code produced. The builders lean on what the
codebase already has rather than re-deriving it: normalization is dpp's
consensus convert_to_homograph_safe_chars (the crate's ASCII-only copy, whose
non-ASCII behaviour differed from the data trigger's, is replaced by a
re-export; the two agree on every label the contract's ASCII-only pattern
admits); the preorder commitment uses dpp::util::hash::hash_double; property
names come from the dpns-contract / dashpay-contract constants; and the DashPay
byte-array bounds (96 / 48-80 / 38-102) are read from the contract schema
instead of being hard-coded to the same numbers.

Deliberately not changed here, to keep this reviewable as a pure extraction:

- No new label validation. An earlier draft rejected labels via
  is_valid_username before the preorder was paid for, but that helper is
  stricter than the DPNS contract (it also refuses consecutive hyphens, which
  the contract's pattern admits), so it would have refused names Platform
  accepts. Failing early on a bad label is worth doing on its own, against the
  contract's actual pattern; it is not this PR.
- No entropy/document-id consistency check in dpp. dash-sdk keeps its existing
  private ensure_entropy_matches_document_id. Hoisting that into
  DocumentCreateTransitionV0::from_document so every caller inherits it is a
  good change, but it adds an error path to a shared crate and is separable.

The autoAcceptProof bound is still checked in create_contact_request before the
recipient lookup: the shared builder re-checks it against the schema, but that
field is raw caller input and the lookup is a network round trip. The two
checks on the SDK's own encryption output are dropped as dead code — the
pre-existing COMPACT_XPUB_LEN guard forces the encrypted xpub to 96 bytes and
fit_account_label bounds the encrypted label to 48-80 — and the builder covers
both for embedders that do their own encryption.

API shape (unreleased v4.2-dev): ContactRequestResult now carries the assembled
document plus entropy instead of id/owner_id/properties; send_contact_request
no longer hand-rebuilds a DocumentV0.
@PastaPastaPasta
PastaPastaPasta force-pushed the feat/shared-dpns-dashpay-builders branch from cfb93ac to edb833b Compare September 10, 2026 20:18
The coverage phase of tests-rs-workspace.yml drives nextest from an explicit
package allowlist, and dash-platform-queries was never added to it when the
crate was split out of dash-sdk. The crate still reaches the report as a
dependency of dash-sdk, so llvm-cov instruments its lines — but its own test
binaries are never run, and every line it owns is recorded as a miss.

Two consequences: the crate's unit tests have not executed in CI since the
split, and any PR touching it is charged for uncovered lines that its tests
do in fact cover, which no amount of added testing can fix from the PR side.

Adding the package runs those tests and makes the reported coverage reflect
them. It only adds hits, since the lines were already in the denominator.

@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.

Final validation — Phase 1 + Phase 2

The extraction is faithful and preserves the existing SDK behavior, document IDs, entropy handling, normalization, and schema-derived byte bounds. The new public builders have a few quality issues: one test does not independently verify the trigger commitment, invalid caller data is mislabeled as SDK configuration, and one builder doc recommends a stricter pre-check than consensus requires.

🟡 2 suggestion(s) | 💬 3 nitpick(s)

Review provenance

Source: reviewer 1: glm-5.3-flash (agent: phase1-reviewer, role: general); reviewer 2: glm-5.3-flash (agent: phase1-reviewer, role: security-auditor); reviewer 3: gpt-6-astra (agent: phase2-reviewer, role: general); reviewer 4: gpt-6-astra (agent: phase2-reviewer, role: rust-quality); reviewer 5: gpt-6-astra (agent: phase2-reviewer, role: security-auditor); final verifier: gpt-6-astra (agent: astra-verifier, role: final-verifier)

  • Triage: normal by gpt-6-astra (effort low) — This is a substantial cross-package extraction affecting DPNS and DashPay document construction, encryption-related field handling, schema-derived bounds, and SDK flow integration, but it does not itself alter consensus rules, signing/key handling, funds movement, or other critical surfaces.
  • Phase 1 reviewers: glm-5.3-flash — general (completed, effort max); agent phase1-reviewer, glm-5.3-flash — security-auditor (completed, effort max); agent phase1-reviewer
  • Phase 1 model: glm-5.3-flash — zai quota: 5h 69% left, weekly 36% left; passed over gemini-3.8-flash-high (antigravity below 15% reserve: weekly 53% left, 5h 2% left)
  • Fresh verifier: gpt-6-astra — final-verifier; agent astra-verifier
  • Phase 2 reviewers: gpt-6-astra — general (completed, effort high); agent phase2-reviewer, gpt-6-astra — rust-quality (completed, effort high); agent phase2-reviewer, gpt-6-astra — security-auditor (completed, effort high); agent phase2-reviewer
🤖 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/dash-platform-queries/src/dashpay.rs`:
- [SUGGESTION] packages/dash-platform-queries/src/dashpay.rs:121-127: Caller-input size violations surface as Error::Config
  `check_byte_field` reports invalid encrypted fields through `Error::Config`, whose documented meaning is that the SDK is misconfigured. These values are caller input (`encryptedPublicKey`, `encryptedAccountLabel`, or `autoAcceptProof`), so embedders cannot distinguish malformed document data from an integration/configuration failure. Add a dedicated invalid-field error variant and map it to the corresponding non-configuration SDK error.
- [NITPICK] packages/dash-platform-queries/src/dashpay.rs:118-122: Byte-bounds validation silently becomes a no-op when schema bounds are absent
  `unwrap_or(0)` and `unwrap_or(u16::MAX)` make `check_byte_field` fail open if a property type reports no size bounds. The current DashPay contract makes that path unreachable, so this is not an active vulnerability, but the helper is intended to provide schema-driven validation for embedders. Return an error when either bound is unavailable so a future contract/type change cannot silently remove local validation.

In `packages/dash-platform-queries/src/dpns_usernames.rs`:
- [SUGGESTION] packages/dash-platform-queries/src/dpns_usernames.rs:140-143: Builder documentation recommends a pre-check that rejects valid contract labels
  The documentation points callers to `is_valid_username` as a client-side pre-check, but that helper rejects consecutive hyphens while the contract pattern `^[a-zA-Z0-9][a-zA-Z0-9-]{0,61}[a-zA-Z0-9]$` accepts them. An embedder following this documentation would reject names that consensus accepts. Document the actual contract pattern, or explicitly warn that `is_valid_username` is stricter than consensus until it is corrected separately.
- [NITPICK] packages/dash-platform-queries/src/dpns_usernames.rs:251-256: Commitment test compares the builder against its own helper
  The test claims to verify agreement with the DPNS trigger, but both the builder and assertion call `salted_domain_hash` with the same inputs. This verifies that the builder invokes the helper, not that the preimage framing matches the independently implemented trigger. Reconstruct the expected preimage from the domain document's stored salt and normalized label, or assert against a fixed independently computed digest so a coordinated helper/preimage regression cannot pass unnoticed.
- [NITPICK] packages/dash-platform-queries/src/dpns_usernames.rs:252-277: DPNS builder test does not pin all generated properties
  The DPNS test checks only the normalized label, label, preorder salt, and document IDs. It does not assert `parentDomainName`, `normalizedParentDomainName`, `records`, or `subdomainRules`, even though these fields are part of the shared builder output and are consumed by the DPNS trigger. Add assertions for every generated property to catch dropped entries or incorrect `Value` variants.
Out-of-scope follow-up suggestions (3)

These are valid observations, but they are outside this PR's scope and should be handled in separate issues or author/maintainer-requested PRs rather than blocking this review.

  • Align is_valid_username with the deployed DPNS contractis_valid_username rejects consecutive hyphens even though the DPNS contract pattern accepts them, and its documentation attributes that restriction to the pattern. This is pre-existing behavior outside the pure-builder extraction, but it can cause client-side false negatives on existing registration paths.
    • Follow-up: Track a separate change that aligns the helper and its documentation with the contract, then review all callers that use it as a hard gate.
  • Move entropy/document-ID consistency validation into DPP — The entropy/document-ID consistency check remains in the SDK-specific path, so embedders assembling transitions directly after using these builders do not receive the same early consistency check. Consensus still rejects mismatched transitions, making this a hardening and user-feedback improvement rather than a correctness vulnerability in this PR.
    • Follow-up: Implement the check in the shared document-create transition constructor in a separate change covering SDK, WASM, FFI, and embedder callers.
  • Generic document-construction helper parked in the DPNS module — NOT_ACTIONABLE: The helper is crate-private, has only two current consumers, and its placement introduces no correctness, API, or dependency problem. Moving it would be organizational cleanup outside the feature's stated goal.
    • Follow-up: Consider creating a separate issue or author/maintainer-requested PR for this.

Comment on lines +121 to +127
let min = property.property_type.min_size().unwrap_or(0) as usize;
let max = property.property_type.max_size().unwrap_or(u16::MAX) as usize;
if bytes.len() < min || bytes.len() > max {
return Err(Error::Config(format!(
"{field} must be {min}-{max} bytes, got {}",
bytes.len()
)));

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.

🟡 Suggestion: Caller-input size violations surface as Error::Config

check_byte_field reports invalid encrypted fields through Error::Config, whose documented meaning is that the SDK is misconfigured. These values are caller input (encryptedPublicKey, encryptedAccountLabel, or autoAcceptProof), so embedders cannot distinguish malformed document data from an integration/configuration failure. Add a dedicated invalid-field error variant and map it to the corresponding non-configuration SDK error.

source: glm-5.3-flash (phase1-reviewer: general, security-auditor)

Comment on lines +140 to +143
/// `normalizedLabel` is the consensus normalization of `label`. The label is
/// not validated here; the contract's label pattern is enforced by consensus
/// when the domain document is created (see [`is_valid_username`] for a
/// client-side pre-check).

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.

🟡 Suggestion: Builder documentation recommends a pre-check that rejects valid contract labels

The documentation points callers to is_valid_username as a client-side pre-check, but that helper rejects consecutive hyphens while the contract pattern ^[a-zA-Z0-9][a-zA-Z0-9-]{0,61}[a-zA-Z0-9]$ accepts them. An embedder following this documentation would reject names that consensus accepts. Document the actual contract pattern, or explicitly warn that is_valid_username is stricter than consensus until it is corrected separately.

Suggested change
/// `normalizedLabel` is the consensus normalization of `label`. The label is
/// not validated here; the contract's label pattern is enforced by consensus
/// when the domain document is created (see [`is_valid_username`] for a
/// client-side pre-check).
/// `normalizedLabel` is the consensus normalization of `label`. The label is
/// not validated here; the contract's label pattern
/// (`^[a-zA-Z0-9][a-zA-Z0-9-]{0,61}[a-zA-Z0-9]$`) is enforced by consensus
/// when the domain document is created.

source: glm-5.3-flash (phase1-reviewer: general, security-auditor)

Comment on lines +251 to +256
// The commitment in the preorder is the one the DPNS data trigger
// recomputes from the domain document's salt and normalized label.
assert_eq!(
preorder.get("saltedDomainHash"),
Some(&Value::Bytes32(salted_domain_hash("Alice", salt)))
);

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.

💬 Nitpick: Commitment test compares the builder against its own helper

The test claims to verify agreement with the DPNS trigger, but both the builder and assertion call salted_domain_hash with the same inputs. This verifies that the builder invokes the helper, not that the preimage framing matches the independently implemented trigger. Reconstruct the expected preimage from the domain document's stored salt and normalized label, or assert against a fixed independently computed digest so a coordinated helper/preimage regression cannot pass unnoticed.

source: glm-5.3-flash (phase1-reviewer: general, security-auditor)

Comment on lines +252 to +277
// recomputes from the domain document's salt and normalized label.
assert_eq!(
preorder.get("saltedDomainHash"),
Some(&Value::Bytes32(salted_domain_hash("Alice", salt)))
);
assert_eq!(
domain.get(domain::properties::NORMALIZED_LABEL),
Some(&Value::Text("a11ce".to_string()))
);
assert_eq!(
domain.get(domain::properties::LABEL),
Some(&Value::Text("Alice".to_string()))
);
assert_eq!(
domain.get(domain::properties::PREORDER_SALT),
Some(&Value::Bytes32(salt))
);
assert_eq!(
preorder.id(),
Document::generate_document_id_v0(
&contract.id(),
&owner,
PREORDER_DOCUMENT_TYPE,
&entropy
)
);

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.

💬 Nitpick: DPNS builder test does not pin all generated properties

The DPNS test checks only the normalized label, label, preorder salt, and document IDs. It does not assert parentDomainName, normalizedParentDomainName, records, or subdomainRules, even though these fields are part of the shared builder output and are consumed by the DPNS trigger. Add assertions for every generated property to catch dropped entries or incorrect Value variants.

source: glm-5.3-flash (phase1-reviewer: general, security-auditor)

Comment on lines +118 to +122
// Only `Array`/`VariableTypeArray` report no size, and dpp refuses those
// at contract creation ("only byte arrays are supported now"), so every
// property reachable here is sized and the defaults never apply.
let min = property.property_type.min_size().unwrap_or(0) as usize;
let max = property.property_type.max_size().unwrap_or(u16::MAX) as usize;

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.

💬 Nitpick: Byte-bounds validation silently becomes a no-op when schema bounds are absent

unwrap_or(0) and unwrap_or(u16::MAX) make check_byte_field fail open if a property type reports no size bounds. The current DashPay contract makes that path unreachable, so this is not an active vulnerability, but the helper is intended to provide schema-driven validation for embedders. Return an error when either bound is unavailable so a future contract/type change cannot silently remove local validation.

Suggested change
// Only `Array`/`VariableTypeArray` report no size, and dpp refuses those
// at contract creation ("only byte arrays are supported now"), so every
// property reachable here is sized and the defaults never apply.
let min = property.property_type.min_size().unwrap_or(0) as usize;
let max = property.property_type.max_size().unwrap_or(u16::MAX) as usize;
let (min, max) = match (
property.property_type.min_size(),
property.property_type.max_size(),
) {
(Some(min), Some(max)) => (min as usize, max as usize),
_ => {
return Err(Error::Config(format!(
"{field} declares no byte bounds to validate against"
)))
}
};

source: glm-5.3-flash (phase1-reviewer: general, security-auditor)

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.

2 participants