Skip to content

fix(sdk): fail closed without proof verification#4168

Merged
QuantumExplorer merged 6 commits into
v4.1-devfrom
codex/fix-js-sdk-unproved-responses
Jul 23, 2026
Merged

fix(sdk): fail closed without proof verification#4168
QuantumExplorer merged 6 commits into
v4.1-devfrom
codex/fix-js-sdk-unproved-responses

Conversation

@QuantumExplorer

@QuantumExplorer QuantumExplorer commented Jul 20, 2026

Copy link
Copy Markdown
Member

Summary

  • require an authenticated proof-verifier boundary before accepting successful Platform transition results
  • request and verify proved data-contract history by default
  • retain endpoint-trusted history only through the explicitly named historyUnproved method
  • retry and reject malformed proved responses at the DAPI-client boundary

Covers security review findings DS-CAND-025 and DS-CAND-354.

Compatibility

This intentionally changes the legacy JS SDK default from endpoint trust to fail-closed. Callers must configure platformProofVerifier for transition broadcasts and verified history. Existing non-security-sensitive history integrations can migrate explicitly to contracts.historyUnproved.

Validation

  • yarn workspace dash build:ts
  • yarn workspace dash test:unit (66 passing)
  • yarn workspace @dashevo/dapi-client test:unit (317 passing)
  • both package linters (no errors)

Summary by CodeRabbit

  • New Features

    • Added authenticated Platform proof verification for state transitions and data contract history.
    • Contract history now offers verified and explicitly unverified retrieval options.
    • Added configuration support for Platform proof verifiers and exposed related SDK errors.
  • Bug Fixes

    • Added validation to reject responses missing required proof or metadata when verification is requested.
    • Improved handling of invalid or incomplete verified history responses.
  • Tests

    • Expanded coverage for proof validation, verifier requirements, and verified versus unverified history retrieval.

@coderabbitai

coderabbitai Bot commented Jul 20, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Warning

Review limit reached

@QuantumExplorer, you've reached your PR review limit, so we couldn't start this review.

Next review available in: 33 minutes

Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available.
You're only billed for reviews past your plan's rate limits ($0.25/file).

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 305cdff1-0fb0-4457-8dcc-1712f0bda387

📥 Commits

Reviewing files that changed from the base of the PR and between 7eab96b and abe328e.

📒 Files selected for processing (4)
  • packages/bench-suite/lib/client/createPlatformProofVerifier.js
  • packages/js-dash-sdk/src/SDK/Client/Platform/broadcastStateTransition.spec.ts
  • packages/js-dash-sdk/src/SDK/Client/Platform/methods/contracts/history.spec.ts
  • packages/platform-test-suite/lib/test/createPlatformProofVerifier.js
📝 Walkthrough

Walkthrough

The PR adds authenticated Platform proof verification to the JavaScript SDK, validates proved DAPI responses, adds an unproved contract-history path, integrates EvoSDK-backed verifiers into test clients, and regenerates Yarn Plug’n’Play dependency mappings.

Changes

Platform proof verification

Layer / File(s) Summary
Verifier contracts and client configuration
packages/js-dash-sdk/src/SDK/Client/*, packages/js-dash-sdk/src/errors/*, packages/js-dash-sdk/src/SDK/SDK.ts
Adds IPlatformProofVerifier, client configuration/access, a verified history entry type, and an exported verifier-unavailable error.
Proved DAPI response validation
packages/js-dapi-client/lib/methods/platform/*, packages/js-dapi-client/test/unit/methods/platform/*, packages/js-dash-sdk/src/SDK/Client/Platform/Fetcher/Fetcher.ts
Validates proof and metadata for proved responses and forwards the proof-mode option through history fetching.
Verified Platform operations
packages/js-dash-sdk/src/SDK/Client/Platform/broadcastStateTransition.*, packages/js-dash-sdk/src/SDK/Client/Platform/methods/contracts/history.*, packages/js-dash-sdk/src/test/mocks/*
Verifies broadcast results and authenticated contract history, while exposing historyUnproved and updating related tests and mocks.
EvoSDK verifier and test-client wiring
packages/bench-suite/lib/client/*, packages/platform-test-suite/lib/test/*, packages/bench-suite/package.json, packages/platform-test-suite/package.json
Adds lazy EvoSDK-backed verifier factories and configures benchmark and platform test clients to use them.

Generated dependency resolution

Layer / File(s) Summary
Yarn PnP dependency mappings
.pnp.cjs
Updates workspace descriptors and regenerated virtual package mappings for the new contract, DAPI client, EvoSDK, and test tooling resolutions.

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

Sequence Diagram(s)

sequenceDiagram
  participant Client
  participant Platform
  participant DAPI
  participant ProofVerifier
  Client->>Platform: submit Platform operation
  Platform->>DAPI: request proved response
  DAPI-->>Platform: response with proof and metadata
  Platform->>ProofVerifier: verify response
  ProofVerifier-->>Platform: verification result
  Platform-->>Client: return verified operation result
Loading

Possibly related PRs

Suggested reviewers: shumkov

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 76.92% which is insufficient. The required threshold is 80.00%. 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 captures the main change: the SDK now fails closed when proof verification is not configured.
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 codex/fix-js-sdk-unproved-responses

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 added this to the v4.1.0 milestone Jul 20, 2026
@QuantumExplorer
QuantumExplorer marked this pull request as ready for review July 21, 2026 02:04
@QuantumExplorer
QuantumExplorer requested a review from shumkov as a code owner July 21, 2026 02:04
@thepastaclaw

thepastaclaw commented Jul 21, 2026

Copy link
Copy Markdown
Collaborator

🕓 Ready for review — 20 ahead in queue (commit 7eab96b)
Queue position: 21/24 · 2 reviews active
ETA: start ~07:55 UTC · complete ~08:16 UTC (median 21m across 30 recent reviews; 2 slots)
Queued 8h 40m ago · Last checked: 2026-07-23 04:20 UTC

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

Preliminary review — Codex only

The PR introduces proof verification for data contract history, but the verified path still accepts an endpoint-originated gRPC NOT_FOUND before any proof is verified. Because Platform returns an authenticated absence proof for prove=true requests, this exception creates an in-scope fail-closed bypass and requires changes.

Source: reviewers gpt-5.6-sol (general, security-auditor); verifier gpt-5.6-sol; Sonnet deferred by the blocker gate.

Validated blockers were found in the Codex precheck. Sonnet is deferred until a fresh Codex revalidation clears the blocker gate.

Review provenance

  • Codex reviewers: gpt-5.6-sol — general (failed), gpt-5.6-sol — security-auditor (failed), gpt-5.6-sol — general (completed), gpt-5.6-sol — security-auditor (completed), gpt-5.6-sol — general (failed), gpt-5.6-sol — security-auditor (failed), gpt-5.6-sol — general (failed), gpt-5.6-sol — security-auditor (failed)
  • Verifier: gpt-5.6-sol — verifier
  • Sonnet: not run (deferred by blocker gate)

🔴 1 blocking

🤖 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/js-dash-sdk/src/SDK/Client/Platform/methods/contracts/history.ts`:
- [BLOCKING] packages/js-dash-sdk/src/SDK/Client/Platform/methods/contracts/history.ts:39-50: Endpoint-forged NOT_FOUND bypasses history proof verification
  The verified history path translates gRPC NOT_FOUND into `null` before invoking `verifyDataContractHistory`, so the selected DAPI endpoint can conceal an existing contract or its history with an unauthenticated negative response. This contradicts the PR's fail-closed boundary. The Platform handler returns a GroveDB proof whenever `prove` is true, including for nonexistent contract history, and emits NOT_FOUND only for unproved queries. The verified method must therefore propagate endpoint errors and accept absence only through the authenticated proof-verifier result; `historyUnproved` can retain the endpoint-trusted NOT_FOUND behavior.

Comment thread packages/js-dash-sdk/src/SDK/Client/Platform/methods/contracts/history.ts Outdated
@QuantumExplorer QuantumExplorer changed the title fix(js-sdk): fail closed without proof verification fix(sdk): fail closed without proof verification Jul 21, 2026
QuantumExplorer and others added 4 commits July 21, 2026 13:08
Mirrors the waitForStateTransitionResult guard: a proved response
without metadata cannot be verified, so reject it at the client
boundary too instead of deferring to the SDK layer.

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

js-dash-sdk now fails closed without an IPlatformProofVerifier, but no
implementation existed and no consumer wired one, so every proved
broadcast and contract-history call would throw
PlatformProofVerificationUnavailableError.

Add createPlatformProofVerifier to the platform test suite and the bench
suite: it is backed by @dashevo/evo-sdk (the Rust/WASM SDK), which
authenticates results end to end — GroveDB proof verification plus the
Tenderdash quorum signature over the root hash, with quorum keys served
by the dashmate quorum-list API (enabled automatically on the local_seed
node). Verification re-queries Platform through the WASM SDK's proved
paths rather than re-checking the transport bytes: the returned data is
quorum-authenticated, so the unverified DAPI response is never the
source of truth. State transition results are confirmed by waiting on
the proved endpoint for the same transition; contract history entries
are returned from the verified query and become the data the caller
consumes.

The verifier maps the suite's existing NETWORK / DAPI_SEED /
DAPI_ADDRESSES configuration to EvoSDK options (regtest -> local) and is
wired into createClientWithoutWallet, createClientWithFundedWallet and
createFaucetClient, plus the bench-suite client factory which shares the
same env conventions.

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

Copy link
Copy Markdown
Member Author

Addressed the merge blocker in 7eab96b (plus a clean v4.1-dev merge).

The PR made js-dash-sdk fail closed without an IPlatformProofVerifier, but no implementation existed anywhere and no consumer wired one — every proved broadcast and contract-history call in the platform test suite (and the bench suite) would throw PlatformProofVerificationUnavailableError. That never surfaced in CI because the Test Suite / E2E jobs only run on version changes, schedules, or manual dispatch.

This adds a real verifier and wires it in:

  • createPlatformProofVerifier (platform-test-suite and bench-suite) is backed by @dashevo/evo-sdk, i.e. the Rust/WASM SDK, which authenticates results end to end: GroveDB proof verification plus the Tenderdash quorum signature over the root hash. Quorum keys come from the dashmate quorum-list API, which dashmate setup local enables automatically on the local_seed node, so the trusted context works out of the box in the e2e environment.
  • Verification re-queries Platform through the WASM SDK's proved paths rather than re-checking the exact transport bytes: broadcast results are confirmed by waiting on the proved endpoint for the same state transition (throws unless it executed), and contract history entries are returned from the verified query — the caller consumes the quorum-authenticated data, never the unverified DAPI response.
  • Wired into createClientWithoutWallet, createClientWithFundedWallet, createFaucetClient, and the bench-suite client factory; all map the existing NETWORK / DAPI_SEED / DAPI_ADDRESSES env configuration to EvoSDK options.

Validation: built wasm-sdk + evo-sdk and ran a Node smoke test covering the full path short of a live network — wasm init under Node, every API the verifier touches (StateTransition.fromBytes, contracts.getHistory, stateTransitions.waitForResponse, DataContract.toBytes), clean rejection of malformed transition bytes, and trusted-context connect failing exactly at the quorum-prefetch network boundary. Lint clean (remaining warnings are the pre-existing unbuilt-workspace resolution ones). Since the e2e suite is dispatch-gated, a workflow_dispatch run of the tests workflow on this branch is the way to exercise it end to end before merge.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 4

🧹 Nitpick comments (1)
packages/js-dash-sdk/src/SDK/Client/Platform/broadcastStateTransition.spec.ts (1)

4-56: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Add a happy-path test for verified broadcast.

Only the two failure branches (missing verifier, missing proof/metadata) are covered. Consider adding a test where the verifier resolves and verifyStateTransitionResult is actually invoked and the result returned, to lock in the primary success flow of this security-sensitive path.

🤖 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
`@packages/js-dash-sdk/src/SDK/Client/Platform/broadcastStateTransition.spec.ts`
around lines 4 - 56, Add a success-path test alongside the existing cases for
broadcastStateTransition, configuring a proof verifier whose
verifyStateTransitionResult resolves and DAPI stubs that return valid proof
metadata. Assert the verifier is invoked with the broadcast result and that
broadcastStateTransition returns the verified result, while preserving the
existing failure tests.
🤖 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 `@packages/bench-suite/lib/client/createPlatformProofVerifier.js`:
- Around line 16-39: Update verifyStateTransitionResult and
verifyDataContractHistory to retain each input’s network and protocolVersion,
and validate the requested network against the EvoSDK network used by getEvoSdk.
Ensure getEvoSdk does not reuse a client for a different network; throw on any
mismatch before verification rather than silently authenticating against the
environment-selected network.

In
`@packages/js-dash-sdk/src/SDK/Client/Platform/broadcastStateTransition.spec.ts`:
- Line 5: Rename the affected test titles in the broadcast state transition spec
so both begin with “should …”, including the tests currently titled “fails
before broadcasting…” and “rejects a success response…”. Preserve the existing
test behavior and make the titles remain descriptive.

In
`@packages/js-dash-sdk/src/SDK/Client/Platform/methods/contracts/history.spec.ts`:
- Around line 106-115: Rename the test case around the history.call invocation
to describe that it fetches from DAPI again rather than reading from local
cache. Keep the existing assertions and test behavior unchanged, including
askedFromDapi equaling 2 and requestedProofModes.

In `@packages/platform-test-suite/lib/test/createPlatformProofVerifier.js`:
- Around line 16-39: Update the platform proof verifier flow around
resolveNetwork and its IPlatformProofVerifier implementation to preserve and
forward each call’s network and protocolVersion options instead of relying only
on process.env.NETWORK. Ensure EvoSDK connections are memoized per resolved
network/devnet identity, not globally from the first call, and apply the same
correction to the corresponding bench-suite implementation.

---

Nitpick comments:
In
`@packages/js-dash-sdk/src/SDK/Client/Platform/broadcastStateTransition.spec.ts`:
- Around line 4-56: Add a success-path test alongside the existing cases for
broadcastStateTransition, configuring a proof verifier whose
verifyStateTransitionResult resolves and DAPI stubs that return valid proof
metadata. Assert the verifier is invoked with the broadcast result and that
broadcastStateTransition returns the verified result, while preserving the
existing failure tests.
🪄 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

Run ID: 46333e8f-d413-4cad-9ffa-67782dc23f8d

📥 Commits

Reviewing files that changed from the base of the PR and between 16a63c8 and 7eab96b.

⛔ Files ignored due to path filters (1)
  • yarn.lock is excluded by !**/yarn.lock, !**/*.lock
📒 Files selected for processing (25)
  • .pnp.cjs
  • packages/bench-suite/lib/client/createClientWithFundedWallet.js
  • packages/bench-suite/lib/client/createPlatformProofVerifier.js
  • packages/bench-suite/package.json
  • packages/js-dapi-client/lib/methods/platform/getDataContractHistory/getDataContractHistoryFactory.js
  • packages/js-dapi-client/lib/methods/platform/waitForStateTransitionResult/waitForStateTransitionResultFactory.js
  • packages/js-dapi-client/test/unit/methods/platform/getDataContractHistory/getDataContractHistoryFactory.spec.js
  • packages/js-dapi-client/test/unit/methods/platform/waitForStateTransitionResult/waitForStateTransitionResultFactory.spec.js
  • packages/js-dash-sdk/src/SDK/Client/Client.spec.ts
  • packages/js-dash-sdk/src/SDK/Client/Client.ts
  • packages/js-dash-sdk/src/SDK/Client/Platform/Fetcher/Fetcher.ts
  • packages/js-dash-sdk/src/SDK/Client/Platform/IPlatformProofVerifier.ts
  • packages/js-dash-sdk/src/SDK/Client/Platform/Platform.ts
  • packages/js-dash-sdk/src/SDK/Client/Platform/broadcastStateTransition.spec.ts
  • packages/js-dash-sdk/src/SDK/Client/Platform/broadcastStateTransition.ts
  • packages/js-dash-sdk/src/SDK/Client/Platform/methods/contracts/history.spec.ts
  • packages/js-dash-sdk/src/SDK/Client/Platform/methods/contracts/history.ts
  • packages/js-dash-sdk/src/SDK/SDK.ts
  • packages/js-dash-sdk/src/errors/PlatformProofVerificationUnavailableError.ts
  • packages/js-dash-sdk/src/test/mocks/createDapiClientMock.ts
  • packages/platform-test-suite/lib/test/createClientWithFundedWallet.js
  • packages/platform-test-suite/lib/test/createClientWithoutWallet.js
  • packages/platform-test-suite/lib/test/createFaucetClient.js
  • packages/platform-test-suite/lib/test/createPlatformProofVerifier.js
  • packages/platform-test-suite/package.json

Comment thread packages/bench-suite/lib/client/createPlatformProofVerifier.js Outdated
Comment thread packages/js-dash-sdk/src/SDK/Client/Platform/broadcastStateTransition.spec.ts Outdated
Comment thread packages/js-dash-sdk/src/SDK/Client/Platform/methods/contracts/history.spec.ts Outdated
Comment thread packages/platform-test-suite/lib/test/createPlatformProofVerifier.js Outdated
@codecov

codecov Bot commented Jul 22, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 87.51%. Comparing base (16a63c8) to head (7eab96b).
⚠️ Report is 21 commits behind head on v4.1-dev.

Additional details and impacted files
@@             Coverage Diff              @@
##           v4.1-dev    #4168      +/-   ##
============================================
- Coverage     87.51%   87.51%   -0.01%     
============================================
  Files          2667     2667              
  Lines        336972   336972              
============================================
- Hits         294903   294902       -1     
- Misses        42069    42070       +1     
Components Coverage Δ
dpp 88.47% <ø> (ø)
drive 86.24% <ø> (ø)
drive-abci 89.58% <ø> (-0.01%) ⬇️
sdk ∅ <ø> (∅)
dapi-client ∅ <ø> (∅)
platform-version ∅ <ø> (∅)
platform-value 92.90% <ø> (ø)
platform-wallet ∅ <ø> (∅)
drive-proof-verifier 49.79% <ø> (ø)
🚀 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.

@QuantumExplorer

Copy link
Copy Markdown
Member Author

Status on the dispatched full-pipeline validation run (29951833896): it failed, but every failure traces to v4.1-dev breakage that full runs expose, not to this PR — Docker image builds are broken on base (the new document-history-contract crate is missing from the Dockerfile, which also takes down Test Suite / Dashmate E2E), the Swift warnings-as-errors build is broken by the #4198 deprecation, and the keyword-search/document-history contract unit tests were latently broken (first CI exposure since the package-filter fix in #4203). The latest two v4.1-dev branch runs fail on the same Docker builds.

All of that is fixed in #4205. Once it merges I'll re-dispatch the full pipeline on this branch to get the real e2e validation of the proof verifier wiring.

Address review feedback on the proof verifier wiring:

- The verifier ignored the per-call network from IPlatformProofVerifier
  and memoized its EvoSDK connection from process.env.NETWORK, so a
  client configured for a different network would silently verify
  against the wrong quorum set. Every verification now asserts the
  caller's network (normalized the same way) matches the connected one
  and throws on mismatch, in both the platform-test-suite and
  bench-suite copies.
- Add a happy-path broadcastStateTransition test locking in that the
  verifier is invoked with the full binding input and the proved result
  is returned.
- Rename test titles to the "should ..." convention and retitle the
  history re-fetch test that claimed caching behavior history() does
  not have.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@QuantumExplorer
QuantumExplorer merged commit 2842092 into v4.1-dev Jul 23, 2026
28 of 35 checks passed
@QuantumExplorer
QuantumExplorer deleted the codex/fix-js-sdk-unproved-responses branch July 23, 2026 01:07
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