Skip to content

Conversation

@shumkov
Copy link
Collaborator

@shumkov shumkov commented Nov 18, 2025

Issue being fixed or feature implemented

SDK fails without retries when it gets invalid proof from a malicious or outdated node

What was done?

  • Retry on proof error

Future improvement: validate response height and time (freshness) before we validate proofs (heavy check).

How Has This Been Tested?

With existing tests.

Breaking Changes

None

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

  • Bug Fixes

    • Improved error resilience by enabling automatic retry for proof validation failures.
  • Chores

    • Removed debug logging output.

@shumkov shumkov self-assigned this Nov 18, 2025
@coderabbitai
Copy link
Contributor

coderabbitai bot commented Nov 18, 2025

Walkthrough

Modified proof verification and error handling across the SDK. Removed debug logging output, expanded retry mechanisms to include proof-related errors, and added a performance optimization note for verification scheduling.

Changes

Cohort / File(s) Summary
Debug logging cleanup
packages/rs-drive-proof-verifier/src/proof/token_info.rs
Removed debug print statement that serialized and output grovedb_proof in hexadecimal format.
Error retry expansion
packages/rs-sdk/src/error.rs
Extended CanRetry implementation to treat Error::Proof(_) variants as retryable errors, alongside existing Error::StaleNode and Error::TimeoutReached cases.
Performance optimization note
packages/rs-sdk/src/sdk.rs
Added TODO comment in parse_proof_with_metadata_and_proof indicating that freshness verification should precede heavy proof validation.

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~8 minutes

  • Homogeneous, low-complexity changes across three files
  • One logic change (CanRetry expansion) is straightforward conditional addition
  • Mostly comment and debug statement removal with minimal risk of regression

Poem

🐰 A hop through the code, so tidy and neat,
Logs cleaned away, no debug print heat,
Errors now retry with confidence bright,
Fresh checks first—proof follows the light!
Simple, swift changes, the SDK takes flight! ✨

Pre-merge checks and finishing touches

❌ Failed checks (1 inconclusive)
Check name Status Explanation Resolution
Title check ❓ Inconclusive The title 'fix(sdk): fail on invalid proof' is vague and contradicts the PR's actual objective of implementing retries on proof errors. The title suggests the PR makes the SDK fail more, while the PR actually implements recovery/retry logic. Consider a more precise title like 'fix(sdk): retry on invalid proof from node' to clarify that the fix implements recovery through retries rather than causing failures.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch fix/failing-with-invalid-proof

Tip

📝 Customizable high-level summaries are now available in beta!

You can now customize how CodeRabbit generates the high-level summary in your pull requests — including its content, structure, tone, and formatting.

  • Provide your own instructions using the high_level_summary_instructions setting.
  • Format the summary however you like (bullet lists, tables, multi-section layouts, contributor stats, etc.).
  • Use high_level_summary_in_walkthrough to move the summary from the description to the walkthrough section.

Example instruction:

"Divide the high-level summary into five sections:

  1. 📝 Description — Summarize the main change in 50–60 words, explaining what was done.
  2. 📓 References — List relevant issues, discussions, documentation, or related PRs.
  3. 📦 Dependencies & Requirements — Mention any new/updated dependencies, environment variable changes, or configuration updates.
  4. 📊 Contributor Summary — Include a Markdown table showing contributions:
    | Contributor | Lines Added | Lines Removed | Files Changed |
  5. ✔️ Additional Notes — Add any extra reviewer context.
    Keep each section concise (under 200 words) and use bullet or numbered lists for clarity."

Note: This feature is currently in beta for Pro-tier users, and pricing will be announced later.


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 and usage tips.

@shumkov shumkov moved this to In review / testing in Platform team Nov 18, 2025
@shumkov shumkov added this to the v2.2.0 milestone Nov 18, 2025
@github-actions
Copy link

✅ gRPC Query Coverage Report

================================================================================
gRPC Query Coverage Report - NEW QUERIES ONLY
================================================================================

Total queries in proto: 47
Previously known queries: 47
New queries found: 0


================================================================================
Summary:
--------------------------------------------------------------------------------
No new queries found

Total known queries: 47
  - Implemented: 44
  - Not implemented: 2
  - Excluded: 1

Not implemented queries:
  - getConsensusParams
  - getTokenPreProgrammedDistributions

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 0

🧹 Nitpick comments (2)
packages/rs-sdk/src/error.rs (1)

186-191: Retrying on Error::Proof(_) aligns with PR goal; consider finer‑grained classification later

Extending can_retry to include Error::Proof(_) matches the intent to fail over when a node returns an invalid or stale proof and keeps behavior consistent with StaleNode/TimeoutReached.

One nuance: Error::Proof(_) also covers locally-caused verifier errors such as ContextProviderNotSet (see parse_proof_with_metadata_and_proof), where retrying won’t help and only adds extra calls. It’s not a blocker, but over time it may be worth distinguishing “node‑dependent” proof failures from “local/misconfiguration” ones and only treating the former as retryable. Based on learnings

packages/rs-sdk/src/sdk.rs (1)

337-339: TODO is accurate and well‑placed; consider tying it to a tracking issue

The new TODO correctly describes that metadata freshness checks are cheap compared to proof validation and should ideally run first. No functional change is introduced here; the comment just clarifies intended future work. If there’s a ticket tracking this optimization, referencing it in the TODO would make it easier to follow up. Based on learnings

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 5d02300 and 48edd4a.

📒 Files selected for processing (3)
  • packages/rs-drive-proof-verifier/src/proof/token_info.rs (0 hunks)
  • packages/rs-sdk/src/error.rs (1 hunks)
  • packages/rs-sdk/src/sdk.rs (1 hunks)
💤 Files with no reviewable changes (1)
  • packages/rs-drive-proof-verifier/src/proof/token_info.rs
🧰 Additional context used
📓 Path-based instructions (1)
**/*.rs

📄 CodeRabbit inference engine (CLAUDE.md)

**/*.rs: Format Rust code with cargo fmt
Run Clippy linter for Rust code

Files:

  • packages/rs-sdk/src/sdk.rs
  • packages/rs-sdk/src/error.rs
🧠 Learnings (11)
📓 Common learnings
Learnt from: QuantumExplorer
Repo: dashpay/platform PR: 2332
File: packages/rs-drive-proof-verifier/src/error.rs:21-23
Timestamp: 2024-11-15T14:39:23.704Z
Learning: In the `packages/rs-drive-proof-verifier`, avoid suggesting size limits for `proof_bytes` in errors unless there's a specific need, as it may not be necessary.
📚 Learning: 2024-10-18T15:43:32.447Z
Learnt from: lklimek
Repo: dashpay/platform PR: 2254
File: packages/rs-sdk/src/sdk.rs:654-658
Timestamp: 2024-10-18T15:43:32.447Z
Learning: In `packages/rs-sdk/src/sdk.rs`, within the `verify_metadata_height` function, when using `compare_exchange` on `last_seen_height`, it's acceptable to use `Ordering::Relaxed` for the failure ordering, as any inconsistency would only trigger an extra loop iteration without affecting correctness.

Applied to files:

  • packages/rs-sdk/src/sdk.rs
📚 Learning: 2024-10-29T14:40:54.727Z
Learnt from: lklimek
Repo: dashpay/platform PR: 2277
File: packages/rs-sdk/src/core/transaction.rs:0-0
Timestamp: 2024-10-29T14:40:54.727Z
Learning: In `packages/rs-sdk/src/platform/document_query.rs` and `packages/rs-sdk/src/core/transaction.rs`, certain places don't implement `IntoInner`, so direct error mappings cannot be simplified using `IntoInner`. A TODO comment has been added to address this in a future PR.

Applied to files:

  • packages/rs-sdk/src/sdk.rs
  • packages/rs-sdk/src/error.rs
📚 Learning: 2024-10-30T11:04:33.634Z
Learnt from: lklimek
Repo: dashpay/platform PR: 2277
File: packages/rs-sdk/src/platform/fetch_unproved.rs:0-0
Timestamp: 2024-10-30T11:04:33.634Z
Learning: In `packages/rs-sdk/src/platform/fetch_unproved.rs`, the `execute()` method consumes the request object, so cloning the request is necessary before passing it to `execute()` and `maybe_from_unproved_with_metadata`.

Applied to files:

  • packages/rs-sdk/src/sdk.rs
📚 Learning: 2025-08-05T13:55:39.147Z
Learnt from: thephez
Repo: dashpay/platform PR: 2718
File: packages/wasm-sdk/index.html:0-0
Timestamp: 2025-08-05T13:55:39.147Z
Learning: The get_identity_keys_with_proof_info function in the Rust WASM bindings does not support the "search" key request type and lacks the searchPurposeMap parameter. When proof mode is enabled with keyRequestType === 'search', the implementation falls back to the non-proof version (get_identity_keys) to maintain functionality.

Applied to files:

  • packages/rs-sdk/src/sdk.rs
📚 Learning: 2024-11-15T14:39:23.704Z
Learnt from: QuantumExplorer
Repo: dashpay/platform PR: 2332
File: packages/rs-drive-proof-verifier/src/error.rs:21-23
Timestamp: 2024-11-15T14:39:23.704Z
Learning: In the `packages/rs-drive-proof-verifier`, avoid suggesting size limits for `proof_bytes` in errors unless there's a specific need, as it may not be necessary.

Applied to files:

  • packages/rs-sdk/src/sdk.rs
📚 Learning: 2025-05-28T16:22:26.334Z
Learnt from: QuantumExplorer
Repo: dashpay/platform PR: 2644
File: packages/rs-drive/src/cache/system_contracts.rs:18-19
Timestamp: 2025-05-28T16:22:26.334Z
Learning: In packages/rs-drive/src/cache/system_contracts.rs, the `active_since_protocol_version` field in `ActiveSystemDataContract` struct is intentionally added for future use, not current implementation. QuantumExplorer confirmed this is "meant for later" when questioned about the `#[allow(unused)]` attribute.

Applied to files:

  • packages/rs-sdk/src/sdk.rs
📚 Learning: 2024-10-04T14:16:05.798Z
Learnt from: lklimek
Repo: dashpay/platform PR: 2207
File: packages/rs-drive-proof-verifier/src/proof.rs:1646-1664
Timestamp: 2024-10-04T14:16:05.798Z
Learning: In the implementation of `FromProof<platform::GetContestedResourceIdentityVotesRequest>` in `packages/rs-drive-proof-verifier/src/proof.rs`, when matching `maybe_votes`, using `.expect()` on `v.into_iter().next()` is acceptable because the prior match arm `Some(v) if v.is_empty()` ensures that the map is not empty, preventing a panic.

Applied to files:

  • packages/rs-sdk/src/sdk.rs
  • packages/rs-sdk/src/error.rs
📚 Learning: 2024-10-18T13:52:36.233Z
Learnt from: lklimek
Repo: dashpay/platform PR: 2254
File: packages/rs-dapi-client/src/dapi_client.rs:267-267
Timestamp: 2024-10-18T13:52:36.233Z
Learning: In `packages/rs-dapi-client/src/dapi_client.rs`, when handling retries, if `can_retry()` returns `None`, we should treat it as non-retryable and not retry. This is the preferred approach for safety, and future implementations of `CanRetry` may return `None` less frequently.

Applied to files:

  • packages/rs-sdk/src/error.rs
📚 Learning: 2024-10-22T10:53:12.111Z
Learnt from: shumkov
Repo: dashpay/platform PR: 2259
File: packages/rs-dapi-client/src/dapi_client.rs:137-139
Timestamp: 2024-10-22T10:53:12.111Z
Learning: In `packages/rs-dapi-client/src/dapi_client.rs`, when passing data into asynchronous code, ensure that data structures are `Send + Sync`. Using `Arc<AtomicUsize>` is necessary for the retry counter.

Applied to files:

  • packages/rs-sdk/src/error.rs
📚 Learning: 2024-10-25T10:35:05.071Z
Learnt from: shumkov
Repo: dashpay/platform PR: 2266
File: packages/rs-sdk/src/sync.rs:171-221
Timestamp: 2024-10-25T10:35:05.071Z
Learning: In `packages/rs-sdk/src/sync.rs`, within the `retry` function, the closure passed to `.when()` in `backon::Retryable::retry` is not executed concurrently, so using a mutable variable like `retries` inside it does not lead to data races.

Applied to files:

  • packages/rs-sdk/src/error.rs
🧬 Code graph analysis (2)
packages/rs-sdk/src/sdk.rs (1)
packages/wasm-sdk/src/queries/mod.rs (1)
  • metadata (237-239)
packages/rs-sdk/src/error.rs (1)
packages/js-dapi-client/lib/methods/platform/waitForStateTransitionResult/WaitForStateTransitionResultResponse.js (1)
  • Proof (3-3)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (11)
  • GitHub Check: Rust packages (dash-sdk) / Check each feature
  • GitHub Check: Rust packages (dash-sdk) / Linting
  • GitHub Check: Rust packages (dash-sdk) / Formatting
  • GitHub Check: Build Docker images (Dashmate helper, dashmate-helper, dashmate-helper) / Build Dashmate helper image
  • GitHub Check: Build Docker images (RS-DAPI, rs-dapi, rs-dapi) / Build RS-DAPI image
  • GitHub Check: Build Docker images (Drive, drive, drive-abci) / Build Drive image
  • GitHub Check: Build JS packages / Build JS
  • GitHub Check: Rust crates security audit
  • GitHub Check: Build rs-sdk-ffi for iOS targets (aarch64-apple-ios-sim)
  • GitHub Check: Build rs-sdk-ffi for iOS targets (aarch64-apple-ios)
  • GitHub Check: Swift SDK and Example build (warnings as errors)

@github-actions
Copy link

✅ DashSDKFFI.xcframework built for this PR.

SwiftPM (host the zip at a stable URL, then use):

.binaryTarget(
  name: "DashSDKFFI",
  url: "https://your.cdn.example/DashSDKFFI.xcframework.zip",
  checksum: "3817f56406cc5d57b3164248d5cb9c44aaf0bdb09af75532deb1539909bacdc6"
)

Xcode manual integration:

  • Download 'DashSDKFFI.xcframework' artifact from the run link above.
  • Drag it into your app target (Frameworks, Libraries & Embedded Content) and set Embed & Sign.
  • If using the Swift wrapper package, point its binaryTarget to the xcframework location or add the package and place the xcframework at the expected path.

@QuantumExplorer
Copy link
Member

I don't think this is the correct approach, we should only retry when we get a proof error and the proof has metadata showing the block is old.

@shumkov
Copy link
Collaborator Author

shumkov commented Nov 19, 2025

@QuantumExplorer malicious node can send you wrong formed proof but valid metadata.

@shumkov shumkov merged commit bb640d0 into v2.2-dev Nov 21, 2025
42 of 43 checks passed
@github-project-automation github-project-automation bot moved this from In review / testing to Done in Platform team Nov 21, 2025
@shumkov shumkov deleted the fix/failing-with-invalid-proof branch November 21, 2025 11:11
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Status: Done

Development

Successfully merging this pull request may close these issues.

3 participants