Skip to content

feat(platform)!: dashpay contact requests declare their checks (PV14) - #4933

Draft
QuantumExplorer wants to merge 2 commits into
v4.2-devfrom
claude/dpns-dashpay-4-2-upgrade-f971de
Draft

QuantumExplorer wants to merge 2 commits into
v4.2-devfrom
claude/dpns-dashpay-4-2-upgrade-f971de

Conversation

@QuantumExplorer

@QuantumExplorer QuantumExplorer commented Sep 23, 2026 •

Copy link
Copy Markdown
Member

Issue being fixed or feature implemented

DashPay v2 (protocol version 14) is the first DashPay contract that can use the schema keywords landed for PV14 (refersTo with identityPublicKey, distinctFrom, encryptedFor). The contact request still relied on a hand-written data trigger that checked two things: the recipient is not the sender, and the recipient identity exists. It never checked that the recipient holds the key the request names, and nothing in the contract told a wallet how encryptedPublicKey and encryptedAccountLabel are encrypted.

Separately, the three profile address fields added in 4.2 carried long description strings that are stored and billed with the contract on chain.

What was done?

contactRequest declares its checks (DashPay schema v2, edited in place: it is only loaded from protocol version 14)

"toUserId": {
  "type": "array", "byteArray": true, "minItems": 32, "maxItems": 32,
  "contentMediaType": "application/x.dash.dpp.identifier",
  "refersTo": { "type": "identityPublicKey", "keyIdProperty": "recipientKeyIndex" },
  "distinctFrom": "$ownerId"
},
"encryptedPublicKey": {
  "type": "array", "byteArray": true, "minItems": 96, "maxItems": 96,
  "encryptedFor": {
    "recipient": "toUserId", "recipientKey": "recipientKeyIndex",
    "senderKey": "senderKeyIndex", "scheme": "ecdh-secp256k1-aes256-cbc"
  }
},
"senderKeyIndex":    { "type": "integer", "minimum": 0, "maximum": 4294967295 },
"recipientKeyIndex": { "type": "integer", "minimum": 0, "maximum": 4294967295 }

encryptedAccountLabel carries the same encryptedFor. The maximum bounds are what encryptedFor requires of key id properties. DashPay keeps sizedIntegerTypes off, so both key indexes are still stored as i64, exactly as v1 stored them.

Data trigger bindings 2 (only selected from protocol version 14) drop the contact request create trigger, since the declarations make both of its checks. create_contact_request_data_trigger stays at 1 in the v10 table, now unbound, with a comment.

Behaviour at protocol version 14, before and after this PR:

contact request to oneself
  before: DataTriggerConditionError (40500) "Identity <id> must not be equal to owner id"
  after:  DocumentPropertyNotDistinctError (10419) contactRequest.toUserId distinct from $ownerId

contact request to an identity that does not exist
  before: DataTriggerConditionError (40500) "Identity <id> doesn't exist"
  after:  ReferencedIdentityKeyNotFoundError (40123) identity <id>, key <recipientKeyIndex>, path toUserId

recipientKeyIndex = 7, recipient has keys 0 and 1
  before: accepted
  after:  ReferencedIdentityKeyNotFoundError (40123)

recipientKeyIndex names a disabled key
  before: accepted
  after:  ReferencedIdentityKeyDisabledError (40124)

encryptedAccountLabel of 50 bytes (within the schema's 48..80, not an IV plus whole AES blocks)
  before: accepted
  after:  InvalidEncryptedPropertyShapeError (10420)

All of these are paid consensus errors. Wallets and SDKs can now read the encryption recipe from the contract (documentTypeEncryptedProperties("contactRequest") in JS, encrypted_properties() in Rust).

Profile address descriptions removed

The description strings on corePaymentAddress, platformPaymentAddress and shieldedAddress are gone from the schema; the formats they described now live in a comment in packages/dashpay-contract/src/v2/mod.rs. Net effect with the declarations above, creating the DashPay contract at protocol version 14 (check_tx test pin):

before: processing fee 24_003_037_140
after:  processing fee 24_002_860_070

Docs: v14 changelog item 33, the SYSTEM_DATA_CONTRACT_VERSIONS_V3 comment, and a "What consensus checks" table in book/src/evo-sdk/dashpay-contact-requests.md.

What was deliberately left out

  • keyRequirements on the recipient key. A census of all 368 testnet contact requests (docs/dashpay/SPEC.md, G15) found the mobile wallets (223 of them) use an unbound ENCRYPTION key, id 2, for both indexes, and mobile identities carry no DECRYPTION key. Requiring purpose: "decryption" or boundTo: "contactRequest" would refuse Dash Wallet's requests, and keyRequirements takes one purpose, not "encryption or decryption".
  • A check on the sender's key. It would need identityProperty: "$ownerId" on senderKeyIndex, and a key id property with that reference is always stored as a u32. DashPay stores it as an i64, so every stored contact request would be misread after the upgrade (see below). Possible follow-up: store KeyIdWithReference at the width the bare integer would get.
  • contactInfo. encToUserId is AES-256-ECB (two raw blocks, no IV) and privateData is AES-256-CBC under a BIP32-derived key, both self-encrypted; neither is the ECDH scheme encryptedFor knows. Declaring ecdh-secp256k1-aes256-cbc on encToUserId would even pass the shape check (32 bytes) while giving wallets the wrong recipe.

How Has This Been Tested?

DashPay v2 replaces v1 in place at the upgrade (apply_contract in transition_to_version_14) without the contract update checks, and system contracts are never run through contract create validation. So the new tests cover what those checks would have:

  • rs-dpp should_store_every_dashpay_v1_property_as_v1_did: every property v1 declares has the same stored encoding in v2. Mutation-checked: adding identityProperty to senderKeyIndex fails it (i64 vs u32).
  • rs-dpp should_declare_the_contact_request_checks_and_encryption: the parsed contract carries the reference, distinctFrom and both encryptedFor declarations.
  • drive-abci should_accept_the_references_the_dashpay_system_contract_declares: the registration reference checks accept DashPay's declarations. Mutation-checked: a keyIdProperty naming a missing property fails it.
  • drive-abci batch/tests/document/dashpay_contact_request.rs: a valid request, and the refusals above for oneself, a missing identity, a missing key and a 50-byte label, end to end through process_raw_state_transitions on the system contract. The disabled key case is covered by the generic identityPublicKey reference tests, not a DashPay-specific one.
  • drive-abci test_protocol_change_v13_to_v14_upgrades_dashpay_and_keeps_v1_documents_readable (renamed from ..._profiles_readable): also stores a v1 contactRequest (with an accountReference above u32) and a v1 contactInfo at protocol version 13 and asserts v2 reads both back unchanged. Mutation-checked: with the u32 re-encoding above, v2 silently reads senderKeyIndex 1 as 0 and recipientKeyIndex 0 as 4294967296.
  • Pins updated for the smaller/changed contract: the protocol version 14 root hash in deterministic_root_hash.rs and the DashPay-contract fees in check_tx. Two deletion/replacement tests that build a random system contact request now set a valid 48-byte encryptedAccountLabel.

Local runs on the rebased branch:

  • drive-abci --lib: 3385 passed; the only 2 failures were the deletion/replacement tests whose random encryptedAccountLabel length now fails encryptedFor. With a fixed 48-byte label both pass, and the not-mutable replace fee pins moved by 200 (second commit).
  • rs-dpp --lib: 4648 passed. dashpay-contract and platform-version tests pass.
  • Re-run on the final head: the new contact request tests, the registration test, the upgrade test, check_tx, the deletion/replacement tests and drive deterministic_root_hash.
  • Not run locally: clippy.

Breaking Changes

Consensus change at protocol version 14 (unreleased): contact requests that were accepted before (unknown or disabled recipient key, account label or public key that is not an IV plus whole blocks) are refused, and the errors for a request to oneself or to a missing identity change code, as listed above. Clients that match on the old DataTriggerConditionError messages must match the new errors.

In-place changes to shipped generations

None. The DashPay v2 schema and data trigger bindings 2 are only selected by protocol version 14, which has not shipped. The v14 changelog entry is item 33, after #4930 took 32.

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
  • If I added or changed GroveDB structure, I described it in the area's structure.rs, regenerated grovedb-structure.json, and checked the structure viewer link posted on this pull request

For repository code-owners and collaborators only

  • I have assigned this pull request to a milestone

🤖 Generated with Claude Code

Summary by CodeRabbit

  • New Features
    • DashPay contact requests now receive additional validation starting with protocol version 14: you can’t send a request to yourself, and the recipient must have a valid, non-disabled identity key.
    • Encrypted contact-request fields are checked to ensure they have a valid AES-CBC format.
  • Bug Fixes
    • Contact requests with missing recipient keys or incorrectly formatted encrypted fields are now rejected with specific validation errors.

DashPay v2's contactRequest now states in its schema what consensus checks,
using the PV14 keywords, instead of a hand-written data trigger:

- toUserId: distinctFrom "$ownerId" (no request to oneself) and an
  identityPublicKey refersTo naming recipientKeyIndex (the recipient
  identity exists, holds that key, and the key is not disabled; the
  trigger only checked the identity).
- encryptedPublicKey / encryptedAccountLabel: encryptedFor with the
  ecdh-secp256k1-aes256-cbc scheme, so wallets read the recipe from the
  contract and consensus refuses bytes that are not an IV plus whole
  AES blocks. senderKeyIndex / recipientKeyIndex gain maximum u32::MAX,
  which encryptedFor requires; DashPay keeps sizedIntegerTypes off, so
  both are still stored as i64.
- Data trigger bindings 2 (PV14 only) drop the contact request trigger.

The three new profile address fields lose their schema descriptions to
keep the stored contract small; their formats move to a code comment.

DashPay v2 replaces v1 in place at the PV14 upgrade without the contract
update checks, so new tests pin that every v1 property keeps its stored
encoding, run the registration checks system contracts skip, and read v1
contact documents back unchanged across the upgrade.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@github-actions github-actions Bot added the waiting-bots Waiting for the review bots to report on this head label Sep 23, 2026
@coderabbitai

coderabbitai Bot commented Sep 23, 2026 •

Copy link
Copy Markdown
Contributor

Review in Change Stack →

Navigate logical layers of code changes, visualize relationships, and explore their blast radius.

Important

Draft PR not reviewed

Draft PRs are not automatically reviewed by default.

  • Trigger a manual review

To automatically review draft PRs, update your CodeRabbit configuration:

reviews:
  auto_review:
    drafts: true
✨ Finishing Touches
📝 Generate docstrings
  • Commit to this branch
  • Create a new PR
🧪 Generate unit tests (beta)
  • Commit to this branch
  • Create a new PR

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.

@github-actions

github-actions Bot commented Sep 23, 2026 •

Copy link
Copy Markdown
Contributor

📖 Book Preview built successfully.

Download the preview from the workflow artifacts.
To view locally: download the artifact, unzip, and open index.html.

Updated at 2026-09-23T03:19:23.557Z

@github-actions github-actions Bot added this to the v4.2.0 milestone Sep 23, 2026
@thepastaclaw

thepastaclaw commented Sep 23, 2026 •

Copy link
Copy Markdown
Collaborator

🕓 Review not started yet because this PR is a draft.

  • Request normal review — click when the PR is ready for review.
  • Request priority review — click to move this review to the front of the queue.

Commit 64fb59c. Normal review starts when eligible; priority review starts as soon as a slot is available.

…ize account label

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@QuantumExplorer
QuantumExplorer marked this pull request as draft September 23, 2026 03:29
@github-actions github-actions Bot removed the waiting-bots Waiting for the review bots to report on this head label Sep 23, 2026
@QuantumExplorer QuantumExplorer changed the title feat(platform)!: DashPay contact requests declare their checks (PV14) feat(platform)!: dashpay contact requests declare their checks (PV14) Sep 23, 2026

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

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants