Repository navigation
Move the whitelist from a single instance-storage Vec to per-address persistent entries #1421
Copy link
Copy link
Labels
Stellar WaveStellar Wave Program issueStellar Wave Program issueperformancePerformancePerformancepriority:mediumMedium implementation difficultyMedium implementation difficultysmart-contractSoroban smart-contract workSoroban smart-contract worksorobanSoroban SDK / StellarSoroban SDK / Stellar
Description
Summary
callora-whitelist keeps every whitelisted address in one Vec<Address> under StorageKey::WhitelistList in instance storage. Instance storage is loaded on every call to the contract, so each added address makes every call more expensive, including is_whitelisted, get_admin and the cooldown views. contains is O(n). Once the instance entry reaches the ledger-entry size limit, add_address can no longer write and the whitelist stops growing. This issue moves to O(1) per-address persistent membership, with paginated enumeration.
Background and evidence
contracts/whitelist/src/lib.rs:- The
StorageKey::WhitelistListvariant is at line 47. add_address(lines 253–290) doesinstance().get::<Vec<Address>>(&WhitelistList), thenlist.contains(&address), thenpush_back, theninstance().set.remove_address(line 299),clear_all(line 355),is_whitelisted(line 386) andget_whitelist(line 405) all load the whole vector.
- The
- A related event wart in the same file:
set_admin(around lines 160–173) publishes twoadmin_nominatedevents with different topic shapes, one legacy and one versioned.
Scope
In scope
- New storage. Add
StorageKey::Member(Address) -> boolin persistent storage, with TTL extended on write and onis_whitelisted, andStorageKey::MemberCount -> u32in instance storage. - Paginated enumeration. Add
get_whitelist_page(start, limit), backed by a paged index such asIndexPage(u32)(see the settlement index pattern), with aMAX_PAGEcap. Either keepget_whitelist()capped and marked deprecated, or remove it, and document the choice indocs/MIGRATION.md. clear_all. Make it bounded:clear_page(limit), or a generation counter that invalidates every member in O(1). Document the choice.- Migration. Add an admin-only
migrate_list(limit)that drains the legacyWhitelistListvector into persistent entries in chunks.is_whitelistedconsults both stores until the migration completes. - Events. Drop the duplicate legacy
admin_nominatedpublish.
Out of scope
- Cooldown semantics (
admin::guard). - The vault's internal allowlist.
Design constraints
- Bounded reads.
is_whitelistedcosts at most two storage reads. - Events:
address_addedandaddress_removedkeep their current topic shape. - Error precedence: auth, then cooldown, then membership.
- Budget: a before/after
cost_estimateforis_whitelistedwith 1, 100 and 1 000 members.
Test plan
| # | Scenario | Expected |
|---|---|---|
| 1 | Add then is_whitelisted |
true; count +1 |
| 2 | Add a duplicate | AddressAlreadyInWhitelist |
| 3 | Remove a non-member | AddressNotInWhitelist |
| 4 | 1 000 members, is_whitelisted CPU |
flat versus the 1-member case (±10%) |
| 5 | get_whitelist_page walk |
every member exactly once |
| 6 | Legacy Vec with 300 entries, migrate_list(100) ×3 |
all migrated; the legacy key removed |
| 7 | clear_all |
is_whitelisted false for all; bounded cost |
| 8 | set_admin |
exactly one admin_nominated event |
| 9 | Member TTL | extended on add and on read |
Regression proof. On main, row 4 shows linear CPU growth.
Acceptance criteria
- All test plan rows pass (
cargo test -p callora-whitelist). -
cargo fmt --all -- --checkandcargo test --workspacepass, and CI is green. - The budget table is in the PR, and
docs/MIGRATION.mdis updated. - Coverage of
lib.rsis at least 95%.
Getting started
git checkout -b perf/whitelist-persistent-membership
cargo test -p callora-whitelistPR requirements
- Link this issue (
Closes #<n>). - Example commit:
perf(whitelist): per-address persistent membership with paged index.
Priority rationale
Medium. Storage redesign with migration in one contract. It removes a hard scaling ceiling and per-call cost growth.
Reactions are currently unavailable
Metadata
Metadata
Assignees
Labels
Stellar WaveStellar Wave Program issueStellar Wave Program issueperformancePerformancePerformancepriority:mediumMedium implementation difficultyMedium implementation difficultysmart-contractSoroban smart-contract workSoroban smart-contract worksorobanSoroban SDK / StellarSoroban SDK / Stellar
Activity
YoungBoss04 commentedon Oct 18, 2026
@YoungBoss04 has applied to work on this issue as part of the Stellar Wave Program's 10th wave.
ℹ️ Repo Maintainers: To accept this application, review their application or assign @YoungBoss04 to this issue.
Tekh134 commentedon Oct 19, 2026
@Tekh134 has applied to work on this issue as part of the Stellar Wave Program's 10th wave.
ℹ️ Repo Maintainers: To accept this application, review their application or assign @Tekh134 to this issue.
drips-wave commentedon Oct 19, 2026
Congratulations, @Tekh134! 🎉 Your application was accepted by the repo's maintainers, and the issue is due on October 22, 2026.
🧑💻 @Tekh134: Please resolve the issue such that the repo's maintainers have enough time to review your contribution before the due date. You'll earn Points for completing the issue on-time, which will make you eligible for a share of the Stellar Wave Program's reward pool.
Warning
When opening a PR, please link it to this issue to ensure it gets tracked accurately. Points are awarded when this issue is marked as completed by the maintainer.
🤠 Repo maintainers: Please keep an eye on the contributor's progress and review their work before the due date. You can manage this issue, including adjusting its complexity and points, here.
🌊 Happy Wave 🌊
perf(whitelist): per-address persistent membership with paged index