Skip to content

fix(sdk): default to nonce 0 for first-time identity/contract interactions#3170

Merged
lklimek merged 10 commits into
v3.1-devfrom
fix/sdk-identity-contract-nonce-not-found
Mar 4, 2026
Merged

fix(sdk): default to nonce 0 for first-time identity/contract interactions#3170
lklimek merged 10 commits into
v3.1-devfrom
fix/sdk-identity-contract-nonce-not-found

Conversation

@lklimek

@lklimek lklimek commented Mar 3, 2026

Copy link
Copy Markdown
Contributor

Issue being fixed or feature implemented

When an identity interacts with a contract for the first time, Platform returns None for the nonce. The SDK previously surfaced this as Error::IdentityNonceNotFound, causing document_create (and other state transitions) to fail on first use.

Closes #3169

What was done?

  • NonceCache::get_identity_nonce / get_identity_contract_nonce (internal_cache/mod.rs): Changed None response from Platform to default to nonce 0 (via unwrap_or_else) instead of returning Error::IdentityNonceNotFound. This matches Drive's own unwrap_or_default() behavior. Downgraded log level from warn to debug.
  • Sdk::get_identity_nonce / get_identity_contract_nonce (sdk.rs): Replaced verbose doc comments with concise descriptions clarifying that a missing nonce is treated as 0 before applying the optional bump.
  • NonceCache::get_or_fetch_nonce (internal_cache/mod.rs): Simplified error doc — removed IdentityNonceNotFound reference since it's no longer returned.
  • Added 3 regression tests covering None → 0 defaulting:
    • first_identity_nonce_defaults_to_zero_then_bumps_to_onebump_first=true
    • first_identity_contract_nonce_defaults_to_zero_then_bumps_to_onebump_first=true
    • first_identity_nonce_defaults_to_zero_without_bumpbump_first=false, directly verifies the 0 default

How Has This Been Tested?

cargo test -p dash-sdk --lib -- first_identity_nonce first_identity_contract_nonce

3 new unit tests in packages/rs-sdk/src/internal_cache/mod.rs using mock SDK with expect_fetch returning None to simulate first-time interactions.

Breaking Changes

None. The Error::IdentityNonceNotFound variant still exists but is no longer returned by the nonce cache. Callers that previously caught this error will simply stop seeing it — no API signature changes.

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

🤖 Co-authored by Claudius the Magnificent AI Agent

Summary by CodeRabbit

  • Bug Fixes

    • First-time interactions now treat missing identity and contract nonces as zero, avoiding previous error paths and enabling bump-on-demand.
  • Tests

    • Added tests covering default-zero nonce behavior and bump/no-bump scenarios for first-time interactions.
  • Documentation

    • Updated API docs to simplify error wording and document the new defaulting behavior for nonce retrieval.

…tions

When Platform returns `None` for an identity or identity-contract nonce,
it means the identity has never interacted with that contract (or
Platform at all). Previously this was treated as an error
(`IdentityNonceNotFound`), which broke first-time document creation for
freshly registered identities.

Now the SDK defaults to nonce 0 in that case, allowing first document
creations to succeed without special-casing by callers. The worst case
for a stale DAPI node returning None incorrectly is a retriable nonce
mismatch, which is strictly better than an unconditional failure.

Closes #3169

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@coderabbitai

coderabbitai Bot commented Mar 3, 2026

Copy link
Copy Markdown
Contributor
📝 Walkthrough

Walkthrough

The internal cache and SDK docs were changed so platform-missing identity and identity-contract nonces default to 0 (first-time interaction) instead of erroring; debug logs were added and three tests cover bump/no-bump first-interaction scenarios. Public function signatures were unchanged.

Changes

Cohort / File(s) Summary
Nonce caching & tests
packages/rs-sdk/src/internal_cache/mod.rs
get_identity_nonce and get_identity_contract_nonce now treat a None response from Platform as nonce 0, add debug logging for the first-interaction path, adjust error semantics for get_or_fetch_nonce, and add three tests for first-time bump/no-bump behavior.
Public API docs
packages/rs-sdk/src/sdk.rs
Updated documentation comments for get_identity_nonce and get_identity_contract_nonce to remove references to IdentityNonceNotFound and state that missing nonces are treated as 0 prior to bump logic.

Sequence Diagram(s)

sequenceDiagram
  participant Client as Client
  participant SDK as SDK/InternalCache
  participant Platform as Platform

  Client->>SDK: request get_identity_nonce(identity_id, bump_first)
  SDK->>SDK: check internal cache (stale/missing?)
  alt cache has valid nonce
    SDK-->>Client: return cached nonce (maybe bumped)
  else cache missing/stale
    SDK->>Platform: fetch nonce for identity (may be None)
    alt Platform returns a nonce
      SDK-->>Client: return fetched nonce (maybe bumped)
    else Platform returns None
      SDK->>SDK: log debug "platform returned no nonce"
      SDK-->>Client: treat as 0 (and bump to 1 if bump_first)
    end
  end
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Poem

🐰 I found a gap where nonces sleep, so small and low,
I nudged a zero gently — "Off we go!"
A tiny hop, a bump, a signed-off cheer,
New journeys start from nothing here. 🥕✨

🚥 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 'fix(sdk): default to nonce 0 for first-time identity/contract interactions' clearly and concisely summarizes the main change: defaulting missing nonces to 0 for first-time interactions.
Linked Issues check ✅ Passed The PR fully addresses issue #3169 by implementing the suggested fix: treating missing nonces (None from Platform) as 0 instead of returning IdentityNonceNotFound, enabling first-time contract interactions.
Out of Scope Changes check ✅ Passed All changes are directly in scope: nonce cache logic handles first-time interactions, documentation updates reflect this behavior, and tests validate the fix without introducing unrelated modifications.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
  • 📝 Generate docstrings (stacked PR)
  • 📝 Generate docstrings (commit on current branch)
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch fix/sdk-identity-contract-nonce-not-found

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.

@github-actions github-actions Bot added this to the v3.1.0 milestone Mar 3, 2026
@github-actions

github-actions Bot commented Mar 3, 2026

Copy link
Copy Markdown
Contributor

✅ gRPC Query Coverage Report

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

Total queries in proto: 53
Previously known queries: 47
New queries found: 6

================================================================================

New Query Implementation Status:
--------------------------------------------------------------------------------
✓ getAddressInfo                                /home/runner/work/platform/platform/packages/rs-sdk/src/platform/query.rs
✓ getAddressesBranchState                       /home/runner/work/platform/platform/packages/rs-sdk/src/platform/address_sync/mod.rs
✓ getAddressesInfos                             /home/runner/work/platform/platform/packages/rs-sdk/src/platform/fetch_many.rs
✓ getAddressesTrunkState                        /home/runner/work/platform/platform/packages/rs-sdk/src/platform/query.rs
✓ getRecentAddressBalanceChanges                /home/runner/work/platform/platform/packages/rs-sdk/src/platform/query.rs
✓ getRecentCompactedAddressBalanceChanges       /home/runner/work/platform/platform/packages/rs-sdk/src/platform/query.rs

================================================================================
Summary:
--------------------------------------------------------------------------------
New queries implemented: 6 (100.0%)
New queries missing: 0 (0.0%)

Total known queries: 53
  - Implemented: 50
  - Not implemented: 2
  - Excluded: 1

Not implemented queries:
  - getConsensusParams
  - getTokenPreProgrammedDistributions

lklimek and others added 5 commits March 3, 2026 16:28
GroveDB absence proofs cannot distinguish "identity missing",
"contract missing", and "no nonce yet" — all three produce None.
Drive's own query handler already applies unwrap_or_default() for
the non-proven path, so defaulting to 0 in the SDK is consistent.

Document this limitation inline so future readers understand the
trade-off.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…istence

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@lklimek lklimek marked this pull request as ready for review March 3, 2026 16:47
@lklimek lklimek requested a review from Copilot March 3, 2026 16:47

Copilot AI 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.

Pull request overview

This PR updates nonce handling so first-time identity and identity–contract interactions default to nonce 0 when Platform returns None, avoiding IdentityNonceNotFound and unblocking initial document submissions.

Changes:

  • Default None nonces from Platform to 0 (with debug logging) for identity and identity–contract nonce fetches
  • Update SDK doc comments to describe the new default-to-zero behavior
  • Add tests covering None → 0 → bump to 1 for both nonce paths

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 9 comments.

File Description
packages/rs-sdk/src/sdk.rs Updates public method doc comments to reflect default-to-zero nonce behavior.
packages/rs-sdk/src/internal_cache/mod.rs Implements None → 0 fallback in cache fetch closures and adds regression tests for first interactions.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread packages/rs-sdk/src/sdk.rs Outdated
Comment thread packages/rs-sdk/src/sdk.rs Outdated
Comment thread packages/rs-sdk/src/internal_cache/mod.rs Outdated
Comment thread packages/rs-sdk/src/internal_cache/mod.rs Outdated
Comment thread packages/rs-sdk/src/internal_cache/mod.rs Outdated
Comment thread packages/rs-sdk/src/internal_cache/mod.rs
Comment thread packages/rs-sdk/src/internal_cache/mod.rs
Comment thread packages/rs-sdk/src/internal_cache/mod.rs Outdated
Comment thread packages/rs-sdk/src/internal_cache/mod.rs
lklimek and others added 2 commits March 3, 2026 21:31
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
QuantumExplorer
QuantumExplorer previously approved these changes Mar 4, 2026
…lse test

- Align get_identity_contract_nonce doc with get_identity_nonce pattern
- Rename tests to *_then_bumps_to_one to match actual assertions
- Add first_identity_nonce_defaults_to_zero_without_bump test

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@lklimek lklimek changed the title fix(sdk): default to nonce 0 for first-time identity-contract interactions fix(sdk): default to nonce 0 for first-time identity/contract interactions Mar 4, 2026
@github-actions

github-actions Bot commented Mar 4, 2026

Copy link
Copy Markdown
Contributor

✅ 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: "58f595a9367ff31f0614f01dabf997c8fd186fe4a0634cd05dfed532568aaeb6"
)

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.

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

🧹 Nitpick comments (2)
packages/rs-sdk/src/internal_cache/mod.rs (2)

184-201: Minor log message inconsistency with get_identity_nonce.

The identity nonce handler (line 153) logs "unknown identity, node missing data, or first interaction" while this identity-contract handler only mentions "first interaction". For consistency and completeness, consider aligning the messages since both cases can have the same root causes.

🔧 Suggested alignment
                     tracing::debug!(
                         identity_id = %identity_id,
                         contract_id = %contract_id,
                         "Platform returned no nonce for identity-contract pair; \
-                         defaulting to 0 (first interaction)"
+                         defaulting to 0 (unknown identity, node missing data, or first interaction)"
                     );
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/rs-sdk/src/internal_cache/mod.rs` around lines 184 - 201, Update the
debug message in the identity-contract nonce fallback (the unwrap_or_else in the
IdentityContractNonceFetcher::fetch_with_settings call used to compute `nonce`)
to match the wording used by `get_identity_nonce` — include "unknown identity,
node missing data, or first interaction" instead of only "first interaction" so
both handlers log the same possible root causes; locate the unwrap_or_else
closure where `tracing::debug!` is called for `identity_id` and `contract_id`
and replace the message text accordingly.

684-753: Good test coverage for the first-time interaction fix.

The three tests comprehensively cover the main scenarios for issue #3169. For completeness, consider adding a test for first_identity_contract_nonce_defaults_to_zero_without_bump to match the identity nonce coverage pattern, though this is optional since the underlying get_or_fetch_nonce logic is already tested.

🧪 Optional: Add test for identity-contract without bump
#[tokio::test]
async fn first_identity_contract_nonce_defaults_to_zero_without_bump() {
    use drive_proof_verifier::types::IdentityContractNonceFetcher;

    let mut sdk = crate::Sdk::new_mock();
    let identity_id = Identifier::default();
    let contract_id = Identifier::from([1u8; 32]);
    let settings = PutSettings::default();

    // Platform returns None (identity has never interacted with contract).
    sdk.mock()
        .expect_fetch::<IdentityContractNonceFetcher, _>((identity_id, contract_id), None)
        .await
        .expect("set mock expectation");

    // bump_first=false: should return the raw default of 0.
    let nonce = sdk
        .get_identity_contract_nonce(identity_id, contract_id, false, Some(settings))
        .await
        .unwrap();
    assert_eq!(nonce, 0, "first identity-contract nonce without bump should be 0");
}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/rs-sdk/src/internal_cache/mod.rs` around lines 684 - 753, Add a
complementary test for the identity-contract case that mirrors the existing
first_identity_nonce_defaults_to_zero_without_bump test: create a new
#[tokio::test] named
first_identity_contract_nonce_defaults_to_zero_without_bump, set up a mock Sdk,
an identity_id and contract_id, expect_fetch::<IdentityContractNonceFetcher,
_>((identity_id, contract_id), None), call
get_identity_contract_nonce(identity_id, contract_id, false,
Some(settings)).await.unwrap(), and assert_eq!(nonce, 0) to verify
bump_first=false returns 0.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@packages/rs-sdk/src/internal_cache/mod.rs`:
- Around line 184-201: Update the debug message in the identity-contract nonce
fallback (the unwrap_or_else in the
IdentityContractNonceFetcher::fetch_with_settings call used to compute `nonce`)
to match the wording used by `get_identity_nonce` — include "unknown identity,
node missing data, or first interaction" instead of only "first interaction" so
both handlers log the same possible root causes; locate the unwrap_or_else
closure where `tracing::debug!` is called for `identity_id` and `contract_id`
and replace the message text accordingly.
- Around line 684-753: Add a complementary test for the identity-contract case
that mirrors the existing first_identity_nonce_defaults_to_zero_without_bump
test: create a new #[tokio::test] named
first_identity_contract_nonce_defaults_to_zero_without_bump, set up a mock Sdk,
an identity_id and contract_id, expect_fetch::<IdentityContractNonceFetcher,
_>((identity_id, contract_id), None), call
get_identity_contract_nonce(identity_id, contract_id, false,
Some(settings)).await.unwrap(), and assert_eq!(nonce, 0) to verify
bump_first=false returns 0.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 99dbbcb2-143d-4300-a19c-5f24d33e36ed

📥 Commits

Reviewing files that changed from the base of the PR and between b87648e and 7cdedf7.

📒 Files selected for processing (1)
  • packages/rs-sdk/src/internal_cache/mod.rs

@lklimek lklimek merged commit 89c75de into v3.1-dev Mar 4, 2026
45 checks passed
@lklimek lklimek deleted the fix/sdk-identity-contract-nonce-not-found branch March 4, 2026 10:51
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.

SDK document_create fails for first-time contract interaction: identity contract nonce not found

3 participants