Skip to content

feat(platform)!: a document batch proof carries the owner's credit balance - #4887

Open
QuantumExplorer wants to merge 5 commits into
v4.2-devfrom
claude/state-transition-user-balance-047c58
Open

QuantumExplorer wants to merge 5 commits into
v4.2-devfrom
claude/state-transition-user-balance-047c58

Conversation

@QuantumExplorer

@QuantumExplorer QuantumExplorer commented Sep 21, 2026

Copy link
Copy Markdown
Member

Issue being fixed or feature implemented

A state transition's waitForStateTransitionResult answer gave the new credit balance back only for identity-money transitions (top-up, withdrawal, credit transfer, fee claim). A document batch proved only the document, so a wallet had to query the balance again after every write: the post-DPNS balance refresh that #4799 adds exists for that reason. This makes the batch proof, and the unproved wait, report the owner's balance.

What was done?

  • Proof. From protocol version 14, prove_state_transition (new version 1, selected by PV14's drive tables) merges the owner's balance query into the document path query, for stored and indexOnly document types; version 0 keeps the document-only proof. The verifier (new version 1) rebuilds the merged query and verifies it strictly once; version 0 verifies the document alone. StateTransitionProofResult::VerifiedDocuments becomes VerifiedDocuments(documents, Option<Credits>): Some from PV14, None for a proof made at an earlier version. The ExecutionProved/AffectedState tag is unchanged: the document binds the execution, the balance is a snapshot at the proof's block and may already include later transitions of the same identity.
  • Wire format. WaitForStateTransitionResultRequestV0 gains bool request_user_balance = 3; the response's result oneof gains UnprovedResultWithOwnerBalance unproved_with_owner_balance = 4 (owner_balance as uint64, JS_STRING), so a response is exactly one of an error, a proof, or the unproved balance. rs-dapi answers with it only when the flag is set and no proof was requested, for any transition with an owner, from an unproved getIdentityBalance read of Drive whose state is at or past the block that executed the transition, and sets the response metadata from that read; a failed or stale read is logged and skipped like a failed proof fetch. A proved wait carries the balance inside the proof. Generated clients regenerated. The legacy JS DAPI in packages/dapi is untouched.
  • Clients. rs-sdk: wait_for_document_and_owner_balance and the provided PutDocument::put_to_platform_and_wait_for_response_with_owner_balance are additive and return (Document, Option<Credits>); the typed Document*Result enums keep their shape; the SDK keeps proving and never sets the request flag. wasm-dpp2 VerifiedDocuments.ownerBalance (BigInt or undefined; a number in JSON while exact, a decimal string past MAX_SAFE_INTEGER, null when absent), wasm-drive-verify ownerBalance, wasm-sdk StateTransitionResult.ownerBalance (its raw wait sets the flag whenever it does not prove, and reports an unproved success as SUCCESS), js-dapi-client getOwnerBalance() with a requestUserBalance option.
  • The SDK book's put-operations chapter notes the balance.

How Has This Been Tested?

  • rs-drive: verify_batch_document_create_happy_path and verify_batch_document_delete_happy_path now prove through prove_state_transition and assert the balance; new verify_batch_document_proof_without_owner_balance_is_rejected pins that a document-only proof verifies the document alone but not the batch at version 1.
  • rs-drive-abci: new document::owner_balance_proof tests execute a dashpay profile create, replace and delete, a keeps-history create and replace, and a contested (prefunded) DPNS create, and check each proof's balance against the stored balance; a proof made for another owner's batch is rejected while its document part verifies on its own. test_executed_index_only_create_and_delete_proofs asserts the balance for indexOnly entries. The strategy-test harness verifies the balance from prover version 1 on.
  • rs-dapi: transition_owner_id unit tests (document batch, credit transfer, undecodable bytes). rs-dpp: JSON shape test for the two-field variant with a balance past MAX_SAFE_INTEGER and for the None case. js-dapi-client: wait-factory spec covers requestUserBalance and getOwnerBalance(). wasm-dpp2 ProofResult.spec.ts updated (not run locally: needs a wasm build).
  • cargo clippy --tests -D warnings over dpp, drive, drive-proof-verifier, dash-sdk, rs-dapi and drive-abci; cargo fmt --check; wasm-dpp2, wasm-drive-verify and wasm-sdk compile on wasm32-unknown-unknown. Not run: platform-test-suite e2e, Swift and Kotlin builds.

Breaking Changes

  • StateTransitionProofResult::VerifiedDocuments has a second field (Option<Credits>); every consumer matching it changes. JSON shape becomes {"VerifiedDocuments": [documents, balance-or-null]}.
  • The merged proof is selected by the protocol version's tables: a 4.2 node keeps answering with the document-only proof until PV14 activates, and from then on a client verifying at PV13 rejects the merged proof while a client at PV14 requires it. Proved document writes need client and node on the same protocol version. Unproved waits and every other transition type are unaffected.
  • WaitForStateTransitionResultResponseV0.result gains a third member; a client that treats "no error, no proof" as success keeps working.
  • No protocol (consensus) change; the proof is a query response.

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

PR Hygiene · 3a7b703

  • Bots — coderabbitai ✓ · thepastaclaw not yet — /skip-bots proceeds without the ones not yet reported
  • Self-review — post /self-reviewed once the bots are done
  • Within your 5 open PRs
  • Build running
  • Approvals
    • files with no dedicated owner — you own it
    • rust-dapi (packages/rs-dapi/src/services/platform_service/wait_for_state_transition_result.rs) — lklimek or shumkov
    • dpp — you own it
    • rs-drive-abci — you own it
    • rs-drive — you own it
    • rust-sdk (packages/rs-sdk/src/platform/documents/transitions/create.rs, packages/rs-sdk/src/platform/documents/transitions/delete.rs, packages/rs-sdk/src/platform/documents/transitions/purchase.rs and 7 more) — lklimek or shumkov
    • js-wasm-sdk (packages/wasm-sdk/src/queries/system.rs) — shumkov

When every box is checked the PR Hygiene check passes and this can merge.

Summary by CodeRabbit

  • New Features

    • Document proofs can now include the document owner’s post-execution credit balance.
    • Added an option to request an owner balance when waiting without requesting a proof.
    • Added SDK and WebAssembly access to owner balances, including safe handling of large values.
    • Added support for returning a document together with its owner balance after submission.
  • Compatibility

    • Proofs created before protocol version 14 report no owner balance.
    • Unproved responses now expose balances through a dedicated result type.

…lance

A document batch's waitForStateTransitionResult proof proved only the
document, so a wallet had to query the balance again after every write
(the post-DPNS refresh in #4799). The prover now merges the owner's
balance into the document proof, for stored and indexOnly types, and the
verifier reads the document and the balance as subsets of that proof from
one root hash. `StateTransitionProofResult::VerifiedDocuments` carries the
balance next to the documents; the execution-proved tag is unchanged, the
balance is a snapshot at the proof's block.

A wait that asks for no proof reports the same balance unverified in the
new `owner_balance` field of the response: rs-dapi reads it from Drive
for a document batch, with the metadata of that read.

rs-sdk exposes it through `wait_for_document_and_owner_balance` and
`put_to_platform_and_wait_for_response_with_owner_balance`; wasm-dpp2,
wasm-drive-verify, wasm-sdk and js-dapi-client hand it out as well.

Old clients verify the document strictly and reject the merged proof, so
proved document writes against a 4.2 node need 4.2 clients.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
@github-actions github-actions Bot added this to the v4.2.0 milestone Sep 21, 2026
@coderabbitai

coderabbitai Bot commented Sep 21, 2026

Copy link
Copy Markdown
Contributor

Review Change StackReview Change Stack

Understand this PR’s impact

Explore downstream dependencies and potential security impact with Blast Radius.

View blast radius →

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Repository: dashpay/platform/.coderabbit.yaml

Review profile: CHILL

Plan: Advanced

Run ID: 13dfc1ea-b598-409f-991d-2bbb240a463f

📥 Commits

Reviewing files that changed from the base of the PR and between f42d2b7 and 3a7b703.

📒 Files selected for processing (17)
  • book/src/sdk/put-operations.md
  • packages/dapi-grpc/clients/drive/v0/nodejs/drive_pbjs.js
  • packages/dapi-grpc/clients/platform/v0/nodejs/platform_pbjs.js
  • packages/dapi-grpc/clients/platform/v0/nodejs/platform_protoc.js
  • packages/dapi-grpc/clients/platform/v0/objective-c/Platform.pbobjc.h
  • packages/dapi-grpc/clients/platform/v0/objective-c/Platform.pbobjc.m
  • packages/dapi-grpc/clients/platform/v0/python/platform_pb2.py
  • packages/dapi-grpc/clients/platform/v0/web/platform_pb.d.ts
  • packages/dapi-grpc/clients/platform/v0/web/platform_pb.js
  • packages/dapi-grpc/protos/platform/v0/platform.proto
  • packages/js-dapi-client/lib/methods/platform/waitForStateTransitionResult/WaitForStateTransitionResultResponse.js
  • packages/js-dapi-client/lib/methods/platform/waitForStateTransitionResult/waitForStateTransitionResultFactory.js
  • packages/js-dapi-client/test/unit/methods/platform/waitForStateTransitionResult/waitForStateTransitionResultFactory.spec.js
  • packages/rs-dapi/src/services/platform_service/wait_for_state_transition_result.rs
  • packages/rs-sdk/src/platform/transition/broadcast_request.rs
  • packages/rs-sdk/tests/fetch/broadcast.rs
  • packages/wasm-sdk/src/queries/system.rs

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


📝 Walkthrough

Walkthrough

The change adds owner balances to document-batch proofs from protocol version 14 and adds optional unproved balance responses for wait requests. It updates Drive proof processing, DAPI and generated protobuf bindings, SDK and WASM APIs, tests, and documentation.

Changes

Owner balance propagation

Layer / File(s) Summary
Protocol and generated client contracts
packages/dapi-grpc/protos/..., packages/dapi-grpc/clients/...
The wait request gains requestUserBalance. The response uses unprovedWithOwnerBalance as a result oneof message. Generated bindings support serialization, deserialization, validation, and accessors.
Proof generation and verification
packages/rs-drive/..., packages/rs-dpp/..., packages/rs-platform-version/...
Versioned Drive methods merge and verify document and owner-balance queries. VerifiedDocuments carries an optional balance. Platform version wiring selects the new prover and verifier versions.
Wait responses and SDK access
packages/rs-dapi/..., packages/rs-sdk/..., packages/js-dapi-client/..., packages/wasm-sdk/...
Unproved waits can return an owner balance. SDK helpers return balances with documents, while existing document methods discard the additional value.
WASM conversion and validation
packages/wasm-dpp2/..., packages/wasm-drive-verify/...
WASM proof results expose ownerBalance, accept optional non-negative credit values, and serialize values above JavaScript’s safe-integer limit as strings.
Tests and documentation
packages/rs-drive-abci/..., packages/js-dapi-client/test/..., packages/wasm-dpp2/tests/..., book/...
Tests cover document operations, malformed or incomplete proofs, response options, serialization, and protocol-version behavior. Documentation describes proved and unproved balance results.

Priority: ➖ Normal

Estimated code review effort: 5 (Critical) | ~90 minutes

Change: Feature

Sequence Diagram(s)

sequenceDiagram
  participant SDK
  participant DAPI
  participant Drive
  participant ProofResult
  SDK->>DAPI: wait with prove or requestUserBalance
  DAPI->>Drive: fetch proof or unproved owner balance
  Drive-->>DAPI: return proof or balance
  DAPI-->>SDK: return transition result
  SDK-->>ProofResult: expose optional owner balance
Loading

Suggested reviewers: lklimek, shumkov

Merge Risk: ⚪ Minimal · up to 3a7b7

No actionable compatibility defect remains; the change is mergeable after normal checks.

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 78.38% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 74 functions across 37 files. (9 skipped:… Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
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.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and concisely describes the primary change: document batch proofs now carry the owner's credit balance.
Full details: Docstring Coverage

Explanation

Docstring coverage is 78.38% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 74 functions across 37 files. (9 skipped: 3 unsupported, 6 too large.)

  • Fix all pre-merge checks with AI
✨ Finishing Touches 💡 1
📝 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 21, 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-21T19:01:51.773Z

@github-actions

github-actions Bot commented Sep 21, 2026

Copy link
Copy Markdown
Contributor

PR Hygiene: the checklist is in the description.

@github-actions github-actions Bot added the waiting-bots Waiting for the review bots to report on this head label Sep 21, 2026
@thepastaclaw

thepastaclaw commented Sep 21, 2026

Copy link
Copy Markdown
Collaborator

⚠️ DEGRADED — Queued for automated review — 11th in line, estimated start in ~5 h (commit 3a7b703)
Estimated review time once started: ~0.9 h (two-phase automated review; median of recent runs).
The primary review models are currently out of quota; this review will run on stand-in models and be marked as degraded.

  • Request priority review — click to move this review to the front of the queue.

QuantumExplorer and others added 3 commits September 21, 2026 21:51
Regenerated the dapi-grpc clients from the merged proto: the python
client conflicted only in its serialized descriptor.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…tegy harness

The harness verified a batch's document strictly, which rejects the
merged proof that now also carries the owner's balance. Verify the
document as a subset and check the owner's balance from the same proof
against the stored one.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Review fix-ups for the document batch owner-balance proof:

- The merged document+balance proof is prove_state_transition v1 and
  verify_state_transition_was_executed_with_proof v1, selected only by
  PV14's drive tables; v0 keeps the document-only proof, so the client
  break aligns with the activation rather than the binary upgrade.
  `VerifiedDocuments` carries `Option<Credits>`: `None` for a proof made
  at an earlier version.
- The verifier verifies the prover's merged query strictly once instead
  of parsing the proof twice as subsets; a proof without the balance is
  rejected by that one verification, and the negative tests pin the
  error kind while showing the document part verifies on its own.
- rs-dapi reports the unproved balance only from a Drive state at or
  past the block that executed the transition.
- wasm-sdk reports an unproved success as SUCCESS whether or not a
  balance came back.
- The new PutDocument method is provided, not required.
- Keeps-history and contested creates are proven and verified in tests.
- wasm-dpp2 imports the JSON normaliser at the top of the file.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
The unproved balance is no longer a loose field on the wait response:
`WaitForStateTransitionResultRequestV0.request_user_balance` asks for it,
and the answer is the `unproved_with_owner_balance` member of the result
oneof (an `UnprovedResultWithOwnerBalance` with the balance), so a
response is exactly one of an error, a proof, or the unproved balance.
It works for any transition with an owner. rs-dapi reads the balance
from Drive only when asked and only from a state at or past the block
that executed the transition.

The Rust SDK keeps proving, so it never sets the flag (a document
batch's proof carries the balance from protocol version 14); the wasm
SDK's raw wait sets it whenever it does not prove; js-dapi-client takes
a `requestUserBalance` option. Generated clients regenerated.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

waiting-bots Waiting for the review bots to report on this head

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants