fix(contracts): keep enrollment_count and get_total_held in sync with state transitions - #233
Open
nanoulloa wants to merge 1 commit into
Open
Conversation
… state transitions Closes Stellar-Deejah#10 - lineproof-escrow: extract add_held/subtract_held helpers and decrement the per-queue running total in release(), refund(), and expire(). Previously the total only ever grew, so get_total_held() permanently overstated the held balance after any transition. subtract_held saturates and clamps at zero. - lineproof-enrollment: extract increment_count/decrement_count helpers (saturating) and use them in enroll(), cancel(), and promote_from_waitlist(). Promotion now only increments when the promoted identity has no live record, fixing a double-count (waitlisted identities already hold an active record). Also removed a duplicated extend_ttl call in enroll(). - Repair both test suites, which did not compile on main (nonexistent Address::new, direct trait calls without contract context, Val equality), migrating lineproof-escrow tests to the generated-client pattern. - Add accounting tests covering every acceptance criterion of Stellar-Deejah#10, including underflow/negative-clamp tests that simulate drifted storage via as_contract. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
|
@nanoulloa is attempting to deploy a commit to the Deejah Team on Vercel. A member of the Team first needs to authorize it. |
Collaborator
|
Welldone, please resolve conflicts |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Closes #10
What this fixes
Issue #10 flags three accounting bugs. Since the issue was filed,
maingained inline increment/decrement blocks for the enrollment counter, so this PR completes the enrollment side (helpers, a remaining double-count bug, saturation) and fixes the still-live escrow bug:1.
lineproof-escrow:get_total_held()never decreased (live bug)deposit()accumulated intoescrow_total, butrelease(),refund(), andexpire()never subtracted, so the reported held balance only ever grew. Extracted two private helpers and wired them into every transition:add_held(env, queue_id, amount)— saturating add, used bydeposit().subtract_held(env, queue_id, amount)— saturating sub clamped at zero, used byrelease(),refund(), andexpire().2.
lineproof-enrollment: counter helpers + a promotion double-countincrement_count/decrement_count(both saturating; decrement clamps at 0 instead of the previousif count > 0guard) and replaced the three duplicated inline blocks inenroll(),cancel(), andpromote_from_waitlist().promote_from_waitlist()unconditionally incremented the counter, but a waitlisted identity by construction already holds a live enrollment record (the waitlist only accepts duplicate enrollers), so promotion overwrites the record yet inflated the count. Promotion now increments only when the identity has no live record — the counter is exactly "number of live enrollment records per queue".extend_ttlcall inenroll().3. Both test suites did not compile on
maincargo test -p lineproof-escrowand-p lineproof-enrollmentfailed with 16 and 9 compile errors respectively (calls to a nonexistentAddress::new, direct trait calls outside a contract context,Valcompared with==, movedEnv). The escrow suite was migrated to the generated-client pattern (env.register+EscrowImplClient+mock_all_auths), preserving every existing test's intent; the enrollment suite had its leftover direct-call fragments removed.Acceptance criteria → tests
enrollment_countcorrect after multipleenroll()test_enrollment_count_increments_per_enrollcancel()test_enrollment_count_decrements_on_canceltest_enrollment_count_never_underflows(forces drifted storage viaenv.as_contract, then cancels)get_total_held= 0 after all deposits released/refunded/expiredtest_total_held_zero_after_all_transitionstest_total_held_decreases_on_release(2 deposits, 1 release)test_total_held_never_goes_negative(drifted storage, release clamps at 0)test_total_held_decreases_on_refund/_on_expire,test_total_held_is_isolated_per_queuetest_enrollment_count_ignores_waitlist_and_survives_promotion,test_enrollment_count_unchanged_by_override_expiredunwrap()without fallback on first-access keysunwrap_or(0)inside the helpersTest output
Full per-test output
Contributor note
Was the missing counter intentional? Almost certainly not. The contract shipped
count_key, a publicenrollment_count()getter, and (in escrow) an event trail designed for auditors — all the read-side machinery exists, only the write side was missing. That pattern (getter + storage key with no writer) reads as scaffold code where the wiring was deferred and forgotten, not a design decision. The same asymmetry appearing independently in both crates (accumulate-without-reverse in escrow, read-without-write in enrollment) supports "incomplete scaffold" over intent.In-storage counter vs. deriving from the index at query time:
(queue_id, identity), so counting would require maintaining aVec<Address>per queue — which reintroduces the same write-symmetry burden on a more expensive structure, plus unbounded read cost and instruction-budget risk on large queues. For escrow, summing all records is worse still (i128 amounts across unbounded records).🤖 Generated with Claude Code