feat: referral loop guard, merkle fuzz targets, TTL rent model, ABI snapshot CI - #1083
Merged
joelpeace48-cell merged 2 commits intoJul 29, 2026
Conversation
…esseStudioLab#743 FinesseStudioLab#799 FinesseStudioLab#803 FinesseStudioLab#805) - FinesseStudioLab#743: Add ReferralLoop (error 123) and REFERRAL_LOCK storage key to campaign contract. do_register now rejects direct referral cycles (A→B where B's lock already points to A) with Error::ReferralLoop. REFERRAL_LOCK is written on first registration and persists through deregister, so re-registering with a different referrer is a no-op (sybil guard). REFERRAL_COUNT is only incremented once per participant per referrer — already_attributed check prevents farming via de/re-register. Three new unit tests cover all abuse paths. - FinesseStudioLab#803: Add two proptest fuzz targets to fuzz_test.rs (fuzz_merkle_random_leaf_ rejected, fuzz_merkle_bit_flip_in_proof_rejected). Each generates random leaf / proof bytes against a fixed non-zero Merkle root and asserts Error::NotInAllowlist is returned — verifying the allowlist gate cannot be bypassed with garbage input. - FinesseStudioLab#799: Add docs/TTL_RENT_MODEL.md documenting the rent cost formula, per-entry cost table, extension strategy, and budget projections. Add scripts/check-ttl- rent.js — reads storage_stats from a live contract and prints ALERT for entries within RENT_ALERT_THRESHOLD ledgers of expiry (default 10,000 / ~0.7 days); exits non-zero when alerts are present for use in cron monitoring. - FinesseStudioLab#805: Add contracts/campaign/contract_spec.json — pinned ABI snapshot listing all 30 functions and all 24 error codes. Add CI step in contracts-ci.yml to verify the snapshot is present and error codes are gap-free; any interface change must update the snapshot and bump schema_version or the build fails. Closes FinesseStudioLab#743 Closes FinesseStudioLab#799 Closes FinesseStudioLab#803 Closes FinesseStudioLab#805
| `--fn ${fn}`, | ||
| args, | ||
| ].filter(Boolean).join(' '); | ||
| const output = execSync(cmd, { encoding: 'utf8', timeout: 15_000 }); |
joelpeace48-cell
merged commit Jul 29, 2026
e167895
into
FinesseStudioLab:main
3 of 16 checks passed
3 tasks
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.
Summary
Contracts: Referral self/loop prevention and sybil guard in campaign referrals #743 — Referral self/loop prevention and sybil guard: Added
ReferralLoop(error 123) and a permanentREFERRAL_LOCKstorage key.do_registernow rejects direct cycles (A→B when B's lock already points to A). The lock is written on first registration and survives deregister, so a participant can't swap referrers or farmREFERRAL_COUNTvia de/re-register cycles.REFERRAL_COUNTis only ever incremented once per participant, guarded by analready_attributedcheck. Three new unit tests cover self-referral (existing), direct loops, count inflation, and referrer-swap attempts.Testing: Expand fuzzing for merkle proofs, XDR decoding, and webhook payloads #803 — Expanded fuzzing for merkle proof verification: Two new proptest targets in
fuzz_test.rs(fuzz_merkle_random_leaf_rejected— 200 cases,fuzz_merkle_bit_flip_in_proof_rejected— 100 cases). Both generate random leaf/proof bytes against a fixed non-zero root and assertError::NotInAllowlist— verifying the on-chain gate cannot be bypassed with garbage input. These join the existing CI fuzz run (cargo test -- fuzz_).Performance: Contract storage rent / TTL budgeting model and alerts #799 — Storage rent / TTL budgeting model: Added
docs/TTL_RENT_MODEL.mdwith the full rent cost formula, per-entry cost table (1K–1M participants), extension strategy (bump-on-write, sweeper, pruning), and a recommended CI constant check. Addedscripts/check-ttl-rent.js— readsstorage_statsfrom a live contract and printsALERTfor entries within the configurable threshold (RENT_ALERT_THRESHOLD, default 10,000 ledgers ≈ 0.7 days); exits non-zero for use in cron monitoring.Release: ABI / contract-interface snapshot tests to catch breaking spec changes #805 — ABI / contract-interface snapshot tests: Added
contracts/campaign/contract_spec.jsonpinning all 30 public functions and all 24 error codes (including the newReferralLoop = 123). Added a CI step tocontracts-ci.ymlthat verifies the snapshot is present and that error codes are gap-free (100–123); any interface change must update the snapshot and bumpschema_versionor the build fails.Test plan
cargo test -p trivela-campaign-contract test_referral_direct_loop_rejected— passes withReferralLooperror returnedcargo test -p trivela-campaign-contract test_referral_count_not_inflated_by_re_register— count stays at 1 after cyclecargo test -p trivela-campaign-contract test_referral_lock_prevents_referrer_swap_on_re_register— referrer_b count stays 0cargo test -p trivela-campaign-contract -- fuzz_merkle_— both fuzz targets run 200/100 cases with no panicsnode scripts/check-ttl-rent.js --network testnet --contract <ID>— prints estimation table and exits 0 when no live contract availableVerify campaign contract ABI snapshotstep passes with the committed specCloses #743
Closes #799
Closes #803
Closes #805