Skip to content

fix(confidential): reject set_spender with a past live_until_ledger - #955

Closed
ninglinLiu wants to merge 1 commit into
OpenZeppelin:mainfrom
ninglinLiu:fix/set-spender-past-live-until
Closed

ninglinLiu wants to merge 1 commit into
OpenZeppelin:mainfrom
ninglinLiu:fix/set-spender-past-live-until

Conversation

@ninglinLiu

@ninglinLiu ninglinLiu commented Oct 28, 2026 •

Copy link
Copy Markdown

Fixes #947

set_spender now reverts with a new ConfidentialTokenError::InvalidLiveUntilLedger = 3515 when live_until_ledger < e.ledger().sequence(). Before this change, the delegation was expired as soon as it was stored, but the owner still paid for the proof and the escrow, and the (owner, spender) pair stayed blocked until revoke_spender.

  • The check is the first thing in storage::set_spender, so it runs before the account and auditor lookups and before proof verification.
  • The bound is inclusive. It matches the liveness predicate (ledger.sequence() <= live_until_ledger) and the fungible set_allowance, so live_until_ledger == ledger.sequence() is still a valid single-ledger delegation.
  • storage::set_spender lists the new error in its # Errors docs.

Tests

  • set_spender_with_past_live_until_ledger_panics: at ledger 100, live_until_ledger = 99 panics with #3515.
  • set_spender_with_current_ledger_is_live: at ledger 100, live_until_ledger = 100 is stored and is_spender returns true.

cargo test -p stellar-confidential passes (117 tests). cargo clippy -p stellar-confidential --all-targets and cargo +nightly fmt --check report nothing for the changed files.

PR Checklist

  • Tests
  • Documentation

AI disclosure: I used an AI coding assistant to help write this change. I reviewed the diff and ran the tests above.

Summary by CodeRabbit

  • Bug Fixes
    • Delegations that have already expired are now rejected when created, with a clear error. Delegations expiring on the current ledger remain allowed.

set_spender stored a live_until_ledger that was already in the past. The
proof was verified, the allowance was escrowed out of the owner's spendable
balance, and the delegation was expired on arrival, so every
confidential_transfer_from reverted with DelegationExpired until the owner
revoked it.

Revert with the new ConfidentialTokenError::InvalidLiveUntilLedger (3515)
when live_until_ledger < ledger.sequence(), before the auditor lookup and
proof verification. The bound is inclusive like the liveness check and the
fungible set_allowance, so live_until_ledger == ledger.sequence() is still a
valid single-ledger delegation.

Fixes OpenZeppelin#947
@coderabbitai

coderabbitai Bot commented Oct 28, 2026 •

Copy link
Copy Markdown
Contributor

Review in Change Stack →

No actionable comments were generated in the recent review.

Recent review info
Run configuration
  • Source mode: Auto
  • Repository: Unavailable
  • Branch: Unavailable
  • File: No configuration file from this repository applied
  • Commit: Unavailable
  • Layers: Organization UI
  • Review profile: CHILL
  • Plan: Advanced
  • Run ID: 34327eb3-e46b-47db-b48c-db1db224504f
Commits

Reviewing files that changed from the base of the PR and between ddc327e and 904c939.

Files selected for processing: 3
  • packages/confidential/src/lib.rs
  • packages/confidential/src/storage.rs
  • packages/confidential/src/test.rs

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

Walkthrough

set_spender now rejects a delegation if its live_until_ledger is earlier than the current ledger sequence. It accepts an expiration equal to the current ledger. The change adds error code 3515 and tests for both boundaries.

Changes

Confidential delegation expiry

Layer / File(s) Summary
Expiry validation and boundary tests
packages/confidential/src/lib.rs, packages/confidential/src/storage.rs, packages/confidential/src/test.rs
ConfidentialTokenError adds InvalidLiveUntilLedger = 3515. set_spender rejects an expiration earlier than the current ledger before account retrieval and proof processing. Tests cover rejection of ledger 99 and acceptance of ledger 100 when the current ledger is 100.

Priority: Normal

Estimated code review effort: 2 (Simple) | ~10 minutes

Change: Bug fix · Severity of issue fixed: Medium

Suggested reviewers: brozorec

Merge Risk: Minimal · up to 904c9

The expiry validation and boundary behavior are consistent with the intended delegation contract, so the change is ready to merge.

Pre-merge checks: Passed 5
Check name Status Explanation
Title check Passed The title clearly and concisely describes the main change: rejecting set_spender when live_until_ledger is in the past.
Description check Passed The description includes the issue reference, change details, boundary behavior, tests, validation commands, documentation updates, and completed checklist items.
Linked Issues check Passed Issue #947 requires an early rejection when live_until_ledger < e.ledger().sequence(), error ConfidentialTokenError::InvalidLiveUntilLedger = 3515, and acceptance at equality. The supplied change …
Out of Scope Changes check Passed The reported changes are limited to the new error variant, the early set_spender liveness check and its documentation, and focused boundary tests. These changes directly implement issue #947. No unr…
Docstring Coverage Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff with cyclomatic complexity of at least 2. Documentat…
Full details: Linked Issues check

Explanation

Issue #947 requires an early rejection when live_until_ledger &lt; e.ledger().sequence(), error ConfidentialTokenError::InvalidLiveUntilLedger = 3515, and acceptance at equality. The supplied change summary reports this check as the first operation in storage::set_spender, before lookups and proof verification. It also reports the new error declaration, error documentation, and tests for ledger 99 versus current ledger 100. These changes satisfy the stated coding and test requirements.

Full details: Out of Scope Changes check

Explanation

The reported changes are limited to the new error variant, the early set_spender liveness check and its documentation, and focused boundary tests. These changes directly implement issue #947. No unrelated production behavior or unrelated tests are identified.

Full details: Docstring Coverage

Explanation

Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff with cyclomatic complexity of at least 2. Documentation-only edits and deletions within surviving functions are included; unchanged diff context and fully deleted functions are excluded. Analyzed 1 functions across 3 files.

Finishing Touches
🧪 Generate unit tests (beta)
  • Create a new PR
  • Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

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

A rabbit checks the ledger’s page,
No stale expiry can engage.
At today’s mark, the grant may stay,
One code names what’s turned away.
Two tests hop through the boundary.
The burrow keeps the rules in view.

Comment @coderabbitai help to get the list of available commands.

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.

🐞 [Bug]: Confidential token set_spender accepts an already-expired live_until_ledger

2 participants