[Audit L-02] Replace lower(column) referral predicates with lowercase-bound parameters #165
Labels
No labels
agent:fix_bugfix
agent:fix_conflicts
agent:fix_security
agent:gap_analysis
agent:implement
agent:open_issues
agents
agent:security_audit
agent:verify
area
timecurve
arena
arena-v2
audit
block:feature_review
block:security
bug
contracts
design
devops
docs
documentation
enhancement
epic
feature
frontend
indexer
low priority
megaeth
operations
ops
performance
QA
qa checklist
referrals
removal
security
smart-contracts
tech-debt
testing
time-arena
timecurve
visual-qa
No milestone
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set
Reference
code/yieldomega#165
Loading…
Reference in a new issue
No description provided.
Delete branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Source
internal full-stack audit
audits/audit_full_1777967937.md(2026-05-05), finding L-02 (Low). Cross-checked at HEAD390f9be.Problem
several referral aggregate queries in
indexer/src/api.rsuseWHERE lower(column) = lower($n)against columns whose stored values are already lowercase (everyaddr_hex(...)insert path usesformat!("{:#x}", a), which emits lowercase) and whose bind params are also lowercased (addr.to_lowercase(),bind_addr_lower).both
lower()calls are redundant CPU, and wrapping the column inlower()defeats the btree indexidx_timecurve_referral_applied_referrer(migrations/20260325000000_referral_and_prize_payouts.up.sql:36-37). cost is negligible at current data volume but scales linearly with referral throughput.Affected lines (HEAD
390f9be)indexer/src/api.rs:422—lower(kk.buyer) = lower(b.buyer)JOIN predicate inside/v1/timecurve/buysLATERAL (both sides come fromaddr_hex, both already lowercase)indexer/src/api.rs:1814—WHERE lower(referrer) = lower($3)in/v1/referrals/applied(param lowercased at line 1807)indexer/src/api.rs:1971—WHERE lower(referrer) = lower($1)in/v1/referrals/wallet-charm-summaryreferrer_charm_wad subqueryindexer/src/api.rs:1974—WHERE lower(buyer) = lower($1)in same handler, referee_charm_wad subqueryindexer/src/api.rs:1977—WHERE lower(referrer) = lower($1)in same handler, referred_buy_count subqueryindexer/src/api.rs:1980—WHERE lower(buyer) = lower($1)in same handler, referee_buy_count subquery (param lowercased at line 1965)Recommended fix
lower(...)wrappers on the 6 lines above; rely on the existing lowercase invariant on both sidesidx_timecurve_referral_applied(buyer)to mirror the existing referrer index —lines 36-37of the up migration only cover referrer; thereferee_*subqueries scan without an indexAcceptance criteria
lower(column) = lower($n)patterns remain inindexer/src/api.rsagainst tables whose addresses are stored lowercase/v1/referrals/wallet-charm-summaryfor a non-existent referrer hits theidx_timecurve_referral_applied_referrerbtree (or seq scan only when the table is small enough that the planner prefers it)idx_timecurve_referral_applied_buyer(optional but matches the symmetry of the queries)cargo test+cargo clippy --all-targets -- -D warningscleanReferences
audits/audit_full_1777967937.mdL-02bind_addr_lower(...)+WHERE column = $n), see lines 758, 828, 894, 1085-1108INV-INDEXER-157cross-link: this fix touches the same handler family but does not change the public-facing 500 redaction surfacementioned in merge request !375
mentioned in commit
a4ce4a7d55Implemented GitLab #165 on
main(merge18a771b).Summary
lower(column) = lower($n)predicates with barecolumn = $ninindexer/src/api.rsfor/v1/referrals/applied,/v1/referrals/wallet-charm-summary, and the/v1/timecurve/buysKumbaya LATERAL join — addresses are already stored lowercase viaaddr_hex, and bind params are lowercased.20260506210000_idx_timecurve_referral_applied_buyer(idx_timecurve_referral_applied_buyerbtree onbuyer) with paired.down.sqlforsqlx migrate revert.Docs / cross-links
INV-INDEXER-165indocs/testing/invariants-and-business-logic.md(§ referrals #94 + anchor#indexer-referral-applied-address-predicates-gitlab-165).docs/indexer/design.md,docs/product/referrals.md,skills/README.md,.cursor/skills/yieldomega-guardrails/SKILL.md.Verification checklist
sqlx migrate run(or deploy) soidx_timecurve_referral_applied_buyerexists.EXPLAIN (ANALYZE, BUFFERS)onGET /v1/referrals/wallet-charm-summary?wallet=0x…with a wallet that has no rows: expect Index Scan onidx_timecurve_referral_applied_referrer/_buyerwhen the planner chooses index vs seq scan on small tables.GET /v1/referrals/applied?referrer=0x…,GET /v1/timecurve/buyswith Kumbaya rows — results unchanged vs prior behavior.cargo test+cargo clippy --all-targets -- -D warningsinindexer/(clean at merge).Issue left open for operator QA.
/cc @brouie — please verify when convenient.
@PlasticDigits — verification PASS on
a4ce4a7(merged MR !375).migration
20260506210000_idx_timecurve_referral_applied_buyerran cleanly on the active stack —_sqlx_migrationsshowssuccess: t. live\\d idx_timecurve_referral_appliedconfirms both_referrerand_buyerbtree indexes present.EXPLAIN (ANALYZE, BUFFERS) on the changed predicate paths against the active DB — planner picks the dedicated btrees for both columns:
no seq scan on either path. the new buyer btree is the planner-preferred path for the referee subqueries.
GET /v1/referrals/applied?limit=3&offset=0— 3 items, paginates cleanGET /v1/referrals/applied?referrer=0x87bdce...8b5e&limit=3— 3 items, all rows correctly scoped to the queried referrerGET /v1/referrals/wallet-charm-summary?wallet=0x87bdce...8b5e—referrer_charm_wadandreferred_buy_countpopulated correctly (this wallet is a referrer with 26822 referrals);referee_*zero (wallet is not a referee — correct)GET /v1/timecurve/buys?limit=3— 3 items, total 26822,next_offset: 3(Kumbaya rows ingest path unchanged)source side —
grep "lower(" indexer/src/api.rsreturns only thebind_addr_lowerhelper at:1102and its 6 caller sites (:770, 840, 906, 1122, 1183, 1283). all thelower(column) = lower($n)patterns from the original 6 cited lines are gone.cargo clippy --all-targets -- -D warnings && cargo testclean at HEAD284c15c— 42/42 PASS.ready for close on your side.
mentioned in commit
842f423fbb