Hybrid solver cache key omits sender while on-chain discount depends on it #283
Labels
No labels
agent:fix_bugfix
agent:fix_conflicts
agent:fix_security
agent:gap_analysis
agent:implement
agent:implement
agent:implement
agent:open_issues
agent:ready
agent:research
agent:security_audit
agent:verify
architecture
backend
blocker:hybrid
blocker:launch
blocker:limit-orders
blocker:v2
block:log_only
block:security
bug
ci
contracts
correctness
deploy
dev
devops
docs
documentation
duplicate
e2e
enhancement
epic
feature
frontend
functional-completion
gas
good first issue
governance
help wanted
high-risk
hooks
hybrid
indexer
infra
infrastructure
integrators
invalid
launch-blocker
limit-orders
localnet
localterra
low priority
missing-implementation
needs-design
ops
performance
priority
high
priority
medium
product
qa
QA
question
ready
ready
research
scripts
security
security-hardening
smartcontracts
tech-debt
testing
ux
UX
v2
verification
wontfix
No milestone
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set.
Reference
code/cl8y-dex-terraclassic#283
Loading…
Add table
Add a link
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?
Severity: Low
Reachability: Two senders sharing the same
tradervalue hitting the solver.Affected:
hybrid_cache_key(indexer/src/api/route_solver.rs).Root cause: the solver's quote cache key omits
sender, but the on-chain fee discount can depend onsender, so cached quotes can carry the wrong fee for a different sender.Summary
hybrid_cache_keyis keyed onsolver_version | token_in | token_out | amount_bucket | max_maker_fills | trader— nosender. Butmaybe_simulateforwards bothtraderandsenderto the chain sim, and the on-chain discount lookup is keyed(trader, sender)(the pair's discount cache and the registry'sGetDiscountboth takesender). So two different senders that pass the sametraderget each other's cached quote, which may embed a different effective fee than they'd actually get.It's a quote-accuracy bug, not a fund issue — the chain enforces the real fee at execution. Low.
Current codebase
route_solver.rshybrid_cache_key: builds the key withoutsender;maybe_simulatepassessenderthrough to simulation.Recommended direction
senderin the cache key when the simulation depends on it (or normalize so the key reflects everything the quote depends on).Acceptance criteria
traderbut different discount eligibility get distinct, correct cached quotes.Cache key should be for the sender's discount tier, so senders with same discount tier can reuse same cache
mentioned in issue #279
Implementation plan (your "key on the resolved discount tier" direction). Small — and it folds into #279.
route_solver.rs:hybrid_cache_keyis built fromsolver_version|token_in|token_out|amount_bucket|max_maker_fills|trader_keyand never readssender, yetmaybe_simulateforwardssenderand the on-chain discount branches on it. Resolve the effective tier in the caller (it has&state.pool) fromtraders.tier_id(already synced bytrader_tracker, no extra LCD), append a single tier segment to the key; keephybrid_cache_keypure/sync. Extend the existinghybrid_cache_key_tests(#245 precedent): same tier → equal key, different tier → distinct.Caveats (Low, accepted-class):
tier_idsyncs every ~600s, so it can disagree with live discount for ~10min (needs_deregister drift) — same quote-accuracy staleness #283 already accepts (chain enforces the real fee). And the indexer doesn't trackis_trusted_router, so on the router path (sender≠trader) key on BOTH tiers to avoid a wrong-fee cache collision.Recommendation: land as a narrow interim with the tier resolved through ONE helper + a single appended key segment, so #279's solver rework can swap the source (traders.tier_id → full discount-state table) without touching the key format — OR just do it inside #279's Phase 3. Your call on standalone-now vs fold-into-#279. @PlasticDigits
mentioned in merge request !751
Shipped your "key on the discount tier" direction — MR !751.
The gap:
hybrid_cache_keykeyed ontraderonly, butmaybe_simulateforwards bothtraderandsenderto the router and the on-chain discount is the subject's tier. So two callers withtraderunset and different senders on different tiers collided on the key and could be served each other's quote.Fix: resolve the discount subject (
traderif set, elsesender), look up its tier from the syncedtraders.tier_id(no extra LCD), and fold the tier into the cache key. Same-tier callers still share the cache (what you asked for); different tiers can't collide. Kepthybrid_cache_keypure/sync — the tier lookup sits in the caller. Addedhybrid_cache_key_distinguishes_discount_tier.Scoped to the cache-key correctness; the deeper sender / is_trusted_router routing tracking I'm keeping in #279's cache phase as flagged. @PlasticDigits
mentioned in commit
013b51a77bmentioned in merge request !761
mentioned in commit
faccef2512mentioned in merge request !764
mentioned in issue #306
mentioned in commit
6420a7747dmentioned in issue #319
marked as related to #319
This one's covered on main — fix MR !751 (
013b51a) and the docs cross-link MR !764 (faccef2) both landed.Checked the acceptance criterion ("two senders with the same trader but different discount eligibility get distinct, correct cached quotes") against the code + test:
The deeper sender / is_trusted_router routing-state tracking I flagged stays out of scope here — that's tracked under #279's cache phase, and the HTTP-level cache-isolation integration test is #306. The cache-key correctness this issue is about is done.
Good to close from my side. @PlasticDigits
mentioned in issue #322
mentioned in issue #323
mentioned in issue #324
Verification complete — issue #283
Issue: Hybrid solver cache key omits sender while on-chain discount depends on it
Verified on branch
mainat531d00e(fix landed in MR !751a6ac683, docs cross-link MR !764faccef2).Acceptance criteria
traderbut different discount eligibility get distinct, correct cached quotesresolve_discount_tierresolves the discount subject (traderif set, elsesender) from syncedtraders.tier_idand folds it intohybrid_cache_keyas…|trader|none|t{tier}. Different tiers → distinct keys; same tier → shared cache (per @PlasticDigits direction on the issue). Unit testhybrid_cache_key_distinguishes_discount_tierasserts tier 0 ≠ 5 ≠ 9 and same-tier/different-sender key equality.Automated checks
Code / docs alignment
indexer/src/api/route_solver.rs:resolve_discount_tier+ tier segment inhybrid_cache_key; caller resolves tier before cache lookup inexecute_hybrid_route_solve.docs/indexer-invariants.md,docs/integrators.md,skills/AGENTS_HYBRID_QUOTING.md,skills/AGENTS_INDEXER_HYBRID_BEST_EXECUTION.md— all document thediscount_tiercache-key segment (#283).Follow-up (out of scope for this issue, already tracked)
Trusted-router path where
sender ≠ traderand on-chainGetDiscountbranches onis_trusted_routerremains in #279 cache phase; HTTP-level cache-isolation integration test tracked in #306. Accepted per implementer notes on this issue.Closing — cache-key correctness for tier-based isolation is implemented, tested, and documented. No repo changes from this verification pass.
mentioned in merge request !796
mentioned in commit
662cab6523mentioned in merge request !797
mentioned in merge request !798
mentioned in merge request !809
mentioned in issue #335
mentioned in commit
ebb64ecb25mentioned in merge request !818
mentioned in commit
8c8b629ee6mentioned in commit
8bc8886287mentioned in issue #364
mentioned in merge request !876
mentioned in issue #376
mentioned in issue #379
mentioned in issue #477
mentioned in issue #485
mentioned in issue #615