Skip to content

In create_order.rs, compute the recovery ID before returning the StateWrapper#1412

Open
weijiekoh wants to merge 1 commit intomainfrom
wj/fix-cancelling-ring1-orders
Open

In create_order.rs, compute the recovery ID before returning the StateWrapper#1412
weijiekoh wants to merge 1 commit intomainfrom
wj/fix-cancelling-ring1-orders

Conversation

@weijiekoh
Copy link
Copy Markdown
Contributor

This fixes ring1 order cancellation. The problem is that in crates/darkpool-types/src/state_wrapper.rs:191, we have:

// Compute the last recovery 
let index = self.recovery_stream.index - 1;

This underflows when self.recovery_stream.index is 0, which is the case when crates/workers/task-driver/src/tasks/create_order.rs:379 creates a new state wrapper with default values.

…efore returning state_intent to avoid subtraction underflow if the nullifier is incorrectly believed to be 0
@weijiekoh weijiekoh requested review from akirillo and sehyunc March 13, 2026 02:25
@weijiekoh weijiekoh marked this pull request as ready for review March 13, 2026 02:25
@claude
Copy link
Copy Markdown

claude bot commented Mar 13, 2026

Claude encountered an error —— View job


I'll analyze this and get back to you.

@claude
Copy link
Copy Markdown

claude bot commented Mar 13, 2026

Claude encountered an error —— View job


I'll analyze this and get back to you.

Copy link
Copy Markdown
Contributor

@akirillo akirillo left a comment

Choose a reason for hiding this comment

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

Summary

  • Overall: This PR calls compute_recovery_id() on the DarkpoolStateIntent at order creation time in create_private_intent_state_wrapper, advancing the stored recovery stream index from 0 to 1. The intent is to fix a u64 subtraction underflow in compute_nullifier() (state_wrapper.rs:191) when called on a Ring 1 intent before its first fill. However, this fix introduces a recovery stream double-advancement: settlement also calls advance_recovery_stream() (ring1.rs:345), so after the first fill the stored index would be 2 instead of the expected 1.
  • Concerns: The double-advancement shifts the recovery ID sequence — the first recovery ID emitted onchain would be H(seed || 1) instead of H(seed || 0), breaking the sequential derivation that state recovery relies on. The root cause is that compute_nullifier() unconditionally does index - 1, which underflows when no recovery ID has been emitted yet (index == 0). The fix should target that assumption rather than mutating the stored intent's stream state at creation time.
  • Architecture: The API server's compute_recovery_id() call in helpers.rs:148-153 is on a throwaway intent used only for computing the signed commitment — it does not affect the stored state and should not be mirrored on the stored intent.

Rating: needs changes

Areas for Manual Review

  • Identify which code path calls compute_nullifier() on a Ring 1 intent before first fill — this is the actual trigger for the underflow and determines which alternative fix is appropriate.

Ok(DarkpoolStateIntent::new(self.intent.clone(), share_stream.seed, recovery_stream.seed))
let mut state_intent =
DarkpoolStateIntent::new(self.intent.clone(), share_stream.seed, recovery_stream.seed);
state_intent.compute_recovery_id();
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.

This advances the stored intent's recovery stream index from 0 to 1 at creation time. Since ring1.rs:345 also calls advance_recovery_stream() at first-fill settlement, the index ends up at 2 after the first fill instead of the expected 1. This also shifts the recovery ID values: the first-fill validity proof (intent_only.rs:85-86) clones the stored intent and calls compute_recovery_id() on the clone, so with stored index=1 the first onchain recovery ID would be H(seed || 1) instead of H(seed || 0).

The underlying bug is in state_wrapper.rs:191:

let index = self.recovery_stream.index - 1; // u64 underflow when index == 0

compute_nullifier() assumes at least one recovery ID has been computed. For a Ring 1 intent before first fill, that assumption doesn't hold — there's no onchain state and no meaningful nullifier.

Suggested alternatives:

  1. Guard compute_nullifier() — return a sentinel or error when index == 0, since a pre-first-fill Ring 1 intent has no onchain nullifier.
  2. Fix the caller — whatever code path computes the nullifier of a pre-first-fill Ring 1 intent should check has_been_filled (or equivalent) first and skip the nullifier computation.

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