Skip to content

docs: add DIP-33 (DashPay payment addresses and payment notifications) - #188

Open
QuantumExplorer wants to merge 3 commits into
masterfrom
dip-payment-addresses-and-notifications
Open

docs: add DIP-33 (DashPay payment addresses and payment notifications)#188
QuantumExplorer wants to merge 3 commits into
masterfrom
dip-payment-addresses-and-notifications

Conversation

@QuantumExplorer

@QuantumExplorer QuantumExplorer commented Aug 3, 2026

Copy link
Copy Markdown
Member

Summary

Adds DIP 0033: DashPay Payment Addresses and Payment Notifications — two-tier payment addressing for DashPay across the Core chain and Platform payment address (DIP-17/DIP-18) rails.

Public tier — optional static address fields on the DashPay profile document:

  • corePaymentAddress (Base58Check string, position 5)
  • platformPaymentAddress (21-byte DIP-18 storage form, position 6)

Payments to these are publicly linkable to the profile by design (the "tips jar"). Extended public keys are deliberately rejected: a world-readable derivation source is exactly as linkable as one static address while adding gap-limit and index-coordination costs.

Notified tier — private payments without a prior contact relationship:

  • Two new identity key purposes, PAYMENT_SCAN (7) and PAYMENT_SPEND (8): non-signing, no contract bounds, at most one active each. Registry placement puts key changes behind master-key authorization and makes any identity payable without a DashPay profile.
  • DIP-9 feature 33' derivation: m/9'/coin'/33'/account'/key_class'/index' (scan / spend / notification out-key classes).
  • Stealth one-time address derivation shared by both rails, rail-domain-separated in the tweak hash; P2PKH-only.
  • Encrypted payment notification documents in an append-only private store, discovered by trial decryption: fixed 586-byte entries, five rail codes, inner recoverable signature for sender attribution, an anonymous-sender variant, and an OVK-style out-ciphertext so the payer can also recover payments from seed.
  • A normative ordering rule: for one-time address payments the notification must reach Platform finality before settlement broadcast — stealth outputs are unrecoverable from the recipient's seed alone, so the store is the recovery record.

Includes rationale for rejecting BIP-47-style notification transactions and BIP-352-style scanning (input public keys are absent from compact block filters, and the sought output script is uncomputable without per-transaction ECDH — so scanning degenerates to downloading every transaction forever), self-verified secp256k1 test vectors, and reservations for a shielded-pool tier and a future tweak-index scanning mode.

Also in this PR: README table row, a few dictionary words, and a fix for the one pre-existing markdownlint error on the README ([here] link text) so CI passes on changed files.

Note: DIP number 32 is intentionally skipped to avoid confusion with BIP-32, since both concern key derivation.

🤖 Generated with Claude Code

Summary by CodeRabbit

  • New Features

    • Added DIP-0033 documentation covering DashPay payment addresses and private payment notifications.
    • Documented payment detection and spending keys, address derivation, encrypted notifications, scanning, restoration, and privacy requirements.
    • Registered the proposal in the DIP-9 feature index and added it to the reference summary.
  • Documentation

    • Updated the BIP repository reference link.
    • Expanded the spell-check dictionary with new terminology.

Two-tier payment addressing for DashPay across the Core chain and
Platform payment address (DIP-17/18) rails:

* Public tier: optional corePaymentAddress / platformPaymentAddress
  fields on the profile document (static, deliberately linkable).
* Notified tier: PAYMENT_SCAN / PAYMENT_SPEND identity key purposes,
  DIP-9 feature 33' derivation, stealth one-time address derivation
  shared by both rails, and encrypted payment notification documents
  with sender recovery and an anonymous-sender variant.

Reserves the shielded-pool tier and a silent-payments-style tweak
index as future extensions. Includes derivation test vectors.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@coderabbitai

coderabbitai Bot commented Aug 3, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

DIP-0033 documents DashPay payment addresses and encrypted payment notifications across Core and Platform. It defines key derivation, notification formats, settlement ordering, client recovery, test vectors, and DIP-9 registration. Repository references and spell-check entries are also updated.

Changes

DashPay payment notifications

Layer / File(s) Summary
Payment profile and key contracts
dip-0033.md
Defines payment profile fields, identity key purposes, HD derivation paths, stealth addresses, and Core and Platform rail rules.
Notification document and cryptography
dip-0033.md
Defines the paymentNotification format, encryption, commitments, sender recovery, and attribution modes.
Payment execution and client recovery
dip-0033.md
Documents notification-before-settlement ordering, client scanning, restoration, security properties, pseudocode, and test vectors.
DIP registration and repository references
README.md, dip-0033.md, project-words.txt
Adds DIP-0033 to the proposal index, registers DIP-9 feature index 33', updates the BIP link, adds spell-check terms, and adds the 2026 copyright notice.

Estimated code review effort: 3 (Moderate) | ~25 minutes

Sequence Diagram(s)

sequenceDiagram
  participant Sender
  participant Platform
  participant Recipient
  participant Core
  Sender->>Platform: Publish encrypted paymentNotification
  Platform-->>Recipient: Expose finalized notification
  Recipient->>Recipient: Decrypt and verify notification
  Sender->>Core: Broadcast settlement
  Core-->>Recipient: Provide settlement data
  Recipient->>Recipient: Verify one-time destination
Loading
🚥 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 clearly and concisely describes the primary change: adding DIP-33 for DashPay payment addresses and payment notifications.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
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.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch dip-payment-addresses-and-notifications

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.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 8

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@dip-0033.md`:
- Around line 194-201: Bind notifications to the exact settled amount and
transition: require the Core output amount to equal the signed amount, and add a
Platform transition locator plus proof binding the credited amount rather than
relying on aggregate balance/nonce state. Update notification deduplication and
restore logic to use this locator, including the corresponding flows near the
Platform verification and restore sections.
- Around line 127-139: Update the DIP-11 identity public-key schema and related
validation to accept purpose values 7 and 8 for PAYMENT_SCAN and PAYMENT_SPEND,
while preserving their required non-signing, no-contract-bounds, and
single-active-key constraints. Activate the updated schema and DIP-0033 under
the same protocol-version rule so validators accept these purposes only when
that version is enabled.
- Around line 288-297: Revise the Anonymous variant and Funding modes text in
dip-0033.md to distinguish inner-payload anonymity from envelope-level creator
attribution: identity-funded notifications must still be rendered as revealing
their document creator, while pool-funded anonymous notifications can omit payer
identity entirely. Update the client-rendering, abstract, and privacy statements
accordingly, and remove or qualify the claim that attribution always comes from
the inner signature.
- Around line 183-186: Update the scalar derivation and serialization rules
around t_n, esk, r, and ovk to make them normative: interpret each hash digest
as a big-endian integer, encode k_out as a fixed 32-byte scalar before
concatenation, and require regenerating eskSeed whenever esk == 0 or r == 0.
Apply the same clarification to the corresponding derivations in the referenced
sections so all implementations produce identical values.
- Around line 362-375: Update pay_notified to reject rail values other than 0
and 2 before deriving the stealth destination, so public profile rails 1 and 3
cannot enter this flow. Preserve the existing stealth derivation for supported
rails and require a separate public-payment path for public rails.
- Around line 139-162: The periodic notification scan must retain disabled
historical PAYMENT_SCAN keys and rescan each one once, not only the currently
active registered scan key. Update the periodic scan and key-rotation handling
to include disabled scan-key versions alongside the active key, while preserving
the existing restore behavior that replays the notification store from position
zero for every derived key.
- Around line 249-256: Update the signature verification requirements around
ECDSA_sign_recoverable so recipients resolve senderKeyIndex against the sender’s
authentication-capable key state at notification publication or finality, retain
proof of that historical key, and reject or anonymize notifications lacking
valid historical verification. Extend the signed preimage to include
senderIdentityId and senderKeyIndex alongside the existing fields, and require
both values during verification.
- Around line 207-211: Extend the paymentNotification document-type section to
define bounded read and admission semantics: specify how recipients are
partitioned or how private range reads are scoped so clients do not scan all
recipients, and define quotas or rate limits for immutable entries to bound
light-client trial-decryption work and append-only storage growth.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: 945aef72-cdea-4a28-b096-92c1c2ffed18

📥 Commits

Reviewing files that changed from the base of the PR and between a4d46dd and d3ed319.

📒 Files selected for processing (3)
  • README.md
  • dip-0033.md
  • project-words.txt

Comment thread dip-0033.md
Comment thread dip-0033.md
Comment thread dip-0033.md Outdated
Comment thread dip-0033.md
Comment thread dip-0033.md
Comment thread dip-0033.md Outdated
Comment thread dip-0033.md Outdated
Comment thread dip-0033.md
…g, rotation scanning

* Define int_be / ser_scalar and zero-scalar regeneration normatively
* Bind sig preimage to senderIdentityId + senderKeyIndex; verify against
  key state at notification creation (disabled-after counts as valid)
* Settled value is authoritative; signed amount must match for verified
  attribution; rail 2 gains an optional stHash settlement locator
* Anonymous variant must be pool-funded; clarify inner vs envelope
  attribution
* Scan-key rotation: retain disabled keys, grace-period dual scanning
* Document store admission economics and deliberate non-partitioning
* Guard pay_notified pseudo-code to notified rails only
* Couple the DIP-11 purpose registry update to the same activation rule

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@QuantumExplorer

Copy link
Copy Markdown
Member Author

Addressed the review in 0fdee78:

  • Scalar encodingint_be/ser_scalar defined normatively; zero-scalar seeds are regenerated; pseudo-code updated to match.
  • Signature binding — preimage now includes senderIdentityId and senderKeyIndex; verification resolves the key at notification creation time (a key disabled after creation still verifies — identity keys are disabled, never removed, so current state suffices).
  • Amount/transition binding — the settled value (Core output value / proven address balance) is authoritative for crediting; the signed amount must equal it for verified attributed display; rail 2 gains an optional stHash settlement locator (a one-time platform address receives exactly one payment by construction, which is why the derived address remains the primary locator).
  • Inner vs envelope attribution — the anonymous variant is now MUST-pool-funded, and the funding-modes text distinguishes verified attribution (inner sig only) from envelope metadata.
  • Scan-key rotation — new client rule: retain disabled detection keys, dual-scan with the previous scan key for a grace period, include all historical keys on restore.
  • pay_notified guard — restricted to rails 0/2.
  • DIP-11 registry — coupled textually in Backwards Compatibility (registry + validators extend with purposes 7/8 under the same protocol-version rule). Deliberately not editing dip-0011/identityPublicKey.json here: that schema is already stale against the implemented protocol (purposes stop at 3 and describe 3 as encryption+decryption, while the implementation defines 3–6 as TRANSFER/SYSTEM/VOTING/OWNER) — refreshing it is separate DIP-11 maintenance.
  • Bounded reads/admission — documented as designed: admission is fee-priced (no free write path), and the store is deliberately not recipient-partitioned because recipient-scoped reads would leak recipient activity to serving nodes; scan cost scales with global throughput, mitigated by fixed-size entries, chunked proofs, and scan-key delegation. Platform-side quotas are out of scope for this DIP.

The character-class pattern could not prove the checksum or network byte,
so clients had to fully validate the address regardless. Enforcing it on
chain only bought a partial check while making every optional-field
document generator produce schema-invalid profiles.

Consensus now constrains the length only; full validation is explicitly a
client responsibility.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 3

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@dip-0033.md`:
- Line 118: Update the Markdown wording in the corePaymentAddress description to
use the hyphenated “on-chain” form instead of “on chain,” without changing the
surrounding validation requirements.
- Line 261: Update the notification verification requirements around
senderIdentityId, senderKeyIndex, and sig so any payload with claimed sender
fields whose signature fails verification is discarded, rather than downgraded
to anonymous. Preserve the anonymous variant only when all three sender fields
are zero, and retain the existing authentication-capability and key-validity
checks for attributed notifications.
- Around line 247-248: Update the rail-2 deduplication rule to treat an all-zero
stHash as absent rather than as a matching identifier. Deduplicate
locator-bearing notifications using the derived address and outputCounter, and
use non-zero stHash only as an additional identifier; preserve separate entries
for unrelated or multiple outputs that lack a usable locator.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: 8b990fb3-c6d5-440b-ae3a-d3112cae4cda

📥 Commits

Reviewing files that changed from the base of the PR and between d3ed319 and 1b04d3b.

📒 Files selected for processing (1)
  • dip-0033.md

Comment thread dip-0033.md

Normative requirements:

* `corePaymentAddress` MUST be a valid Base58Check Core chain address for the network the contract is deployed on. Consensus constrains only the string length: a Base58 character-class pattern would still not prove the checksum or network byte, so validation of the address proper is a client responsibility and is not duplicated on chain. Clients MUST validate the checksum and network byte before paying, and MUST treat an undecodable value as no address rather than as an error in the profile.

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Use on-chain in the Markdown text.

Replace on chain with on-chain to resolve the reported documentation lint warning.

🧰 Tools
🪛 LanguageTool

[grammar] ~118-~118: Use a hyphen to join words.
Context: ... responsibility and is not duplicated on chain. Clients MUST validate the checksu...

(QB_NEW_EN_HYPHEN)

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@dip-0033.md` at line 118, Update the Markdown wording in the
corePaymentAddress description to use the hyphenated “on-chain” form instead of
“on chain,” without changing the surrounding validation requirements.

Source: Linters/SAST tools

Comment thread dip-0033.md
Comment on lines +247 to +248
| `2` | Platform, one-time address | `R[33] \|\| outputCounter[4] \|\| amount[8] \|\| fundingKind[1] \|\| stHash[32]` — amount in credits; `fundingKind`: `0` = address funds transfer, `1` = unshielding. The derived address is the primary locator; `stHash` (all-zeros when absent) SHOULD name the crediting state transition to bind the exact settlement |
| `3` | Platform, public profile address | `platformAddress[21] \|\| amount[8] \|\| stHash[32]` — amount in credits; `stHash` identifies the crediting state transition |

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win

Exclude an absent stHash from deduplication.

Rail 2 uses an all-zero stHash when no locator exists. The deduplication rule at Line 318 treats equal stHash values as duplicates. That can merge unrelated rail-2 notifications without locators. It can also merge multiple outputs from one transition. Treat zero as absent and deduplicate by derived address plus outputCounter; use stHash only when non-zero.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@dip-0033.md` around lines 247 - 248, Update the rail-2 deduplication rule to
treat an all-zero stHash as absent rather than as a matching identifier.
Deduplicate locator-bearing notifications using the derived address and
outputCounter, and use non-zero stHash only as an additional identifier;
preserve separate entries for unrelated or multiple outputs that lack a usable
locator.

Comment thread dip-0033.md

Including `senderIdentityId` and `senderKeyIndex` in the preimage binds the signature to the claimed identity: a signature cannot be re-attributed to another identity that happens to have registered the same public key.

Recipients MUST verify `sig` against the key at `senderKeyIndex` of `senderIdentityId` (fetched with proof). The key MUST be authentication-capable and MUST have been valid when the notification was created: either currently active, or disabled with a disabling time later than the entry's creation time (identity keys are disabled, never removed, so current identity state suffices for this check — sender key rotation does not retroactively invalidate attribution). Notifications whose signature does not verify MUST be treated as anonymous at best and SHOULD be discarded.

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🔒 Security & Privacy | 🟠 Major | ⚡ Quick win

Reject malformed attributed notifications instead of downgrading them to anonymous.

The anonymous variant requires all-zero senderIdentityId, senderKeyIndex, and sig. If a payload has claimed sender fields but its signature fails, clients MUST discard it. Otherwise, an attacker can attach an arbitrary memo to a real settlement and have clients display it as an anonymous payment.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@dip-0033.md` at line 261, Update the notification verification requirements
around senderIdentityId, senderKeyIndex, and sig so any payload with claimed
sender fields whose signature fails verification is discarded, rather than
downgraded to anonymous. Preserve the anonymous variant only when all three
sender fields are zero, and retain the existing authentication-capability and
key-validity checks for attributed notifications.

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.

1 participant