Skip zero-reserve path candidates in DB-hybrid route solve (#369) #878

Merged
PlasticDigits merged 2 commits from fix/369-zero-reserve-route-solve-skip into main 2026-06-12 07:57:59 +00:00
PlasticDigits commented 2026-06-12 06:35:34 +00:00 (Migrated from gitlab.com)

Summary

Fixes GitLab #369: under ROUTE_SOLVER_DB_HYBRID=1, GET /api/v1/route/solve and /best no longer return 502 "Route mirror simulation failed" when a candidate path touches an unfunded pair (pair_reserves 0/0) but a funded direct route exists.

  • Mirror load: zero-reserve rows get MirrorFreshness::EmptyPool (same LCD-fallback bucket as missing/stale mirror).
  • Per-hop sim: InsufficientLiquidity on a fresh mirror triggers LCD fallback instead of hard-failing the grid point.
  • Global solver: path candidates where no hop can be simulated return PathUnusable and are skipped; the best remaining path wins, or 404 when every enumerated path is unusable (no whole-request 502 for this case).

Docs/skills updated: docs/indexer-invariants.md, skills/AGENTS_INDEXER_HYBRID_BEST_EXECUTION.md.

Acceptance checklist

Criterion Verification Result
EMBER↔CORAL (or equivalent) returns 200 on funded direct path when a longer path hits zero-reserve pair cd indexer && cargo test --test api_route_solve_db_hybrid route_solve_db_hybrid_skips_zero_reserve_path_candidate -- --test-threads=1 PASS
/route/solve/best same behavior same test (both endpoints) PASS
No regression on existing DB-hybrid route solve tests cd indexer && cargo test --test api_route_solve_db_hybrid -- --test-threads=1 PASS
No regression on LCD route solve tests cd indexer && cargo test --test api_route_solve -- --test-threads=1 PASS
Zero-reserve mirror unit behavior cd indexer && cargo test --lib db_orderbook_sim::tests::zero_reserve_mirror_rejects_pool_leg PASS
Invariants documented docs/indexer-invariants.md, skills/AGENTS_INDEXER_HYBRID_BEST_EXECUTION.md PASS
Live QA EMBER↔CORAL on main 8408689 Not re-run in agent VM (no QA indexer deploy) SKIP

Third-party verification

  1. make setup-indexer-postgres
  2. cd indexer && cargo test --test api_route_solve_db_hybrid route_solve_db_hybrid_skips_zero_reserve_path_candidate -- --test-threads=1
  3. On QA with ROUTE_SOLVER_DB_HYBRID=1 and zero-reserve pairs in pair_reserves, confirm GET /api/v1/route/solve?token_in=<A>&token_out=<B>&amount_in=1000000 returns 200 with solver_version=global_v4 (not 502) when a funded direct pool exists.

Note

Medium Risk
Changes core global best-execution and hybrid simulation error handling for DB-hybrid mode; behavior shifts from whole-request 502 to path skipping and 404, which affects retail quoting when unfunded pairs appear on longer routes.

Overview
Under ROUTE_SOLVER_DB_HYBRID=1, global route solve no longer fails the whole request with 502 when one enumerated path hits an unfunded pair (pair_reserves 0/0) but another funded path (e.g. a direct pool) still works.

Mirror / per-hop sim: zero-reserve mirrors get MirrorFreshness::EmptyPool; fresh mirrors that reject the pool leg with InsufficientLiquidity fall back to LCD instead of aborting the grid. Global solver: candidates that cannot simulate any hop surface PathUnusable, are skipped during concurrent evaluation, and the best remaining path wins; if every path is unusable the API returns 404 (not 502). Infra LCD failures still map to 502.

Docs/skills and a regression test (route_solve_db_hybrid_skips_zero_reserve_path_candidate) cover the new behavior.

Reviewed by Cursor Bugbot for commit 24690ae7a2. Bugbot is set up for automated code reviews on this repo. Configure here.

## Summary Fixes GitLab #369: under `ROUTE_SOLVER_DB_HYBRID=1`, `GET /api/v1/route/solve` and `/best` no longer return **502 "Route mirror simulation failed"** when a candidate path touches an unfunded pair (`pair_reserves` 0/0) but a funded direct route exists. - **Mirror load:** zero-reserve rows get `MirrorFreshness::EmptyPool` (same LCD-fallback bucket as missing/stale mirror). - **Per-hop sim:** `InsufficientLiquidity` on a fresh mirror triggers LCD fallback instead of hard-failing the grid point. - **Global solver:** path candidates where no hop can be simulated return `PathUnusable` and are **skipped**; the best remaining path wins, or **404** when every enumerated path is unusable (no whole-request 502 for this case). Docs/skills updated: `docs/indexer-invariants.md`, `skills/AGENTS_INDEXER_HYBRID_BEST_EXECUTION.md`. ## Acceptance checklist | Criterion | Verification | Result | |-----------|--------------|--------| | EMBER↔CORAL (or equivalent) returns 200 on funded direct path when a longer path hits zero-reserve pair | `cd indexer && cargo test --test api_route_solve_db_hybrid route_solve_db_hybrid_skips_zero_reserve_path_candidate -- --test-threads=1` | PASS | | `/route/solve/best` same behavior | same test (both endpoints) | PASS | | No regression on existing DB-hybrid route solve tests | `cd indexer && cargo test --test api_route_solve_db_hybrid -- --test-threads=1` | PASS | | No regression on LCD route solve tests | `cd indexer && cargo test --test api_route_solve -- --test-threads=1` | PASS | | Zero-reserve mirror unit behavior | `cd indexer && cargo test --lib db_orderbook_sim::tests::zero_reserve_mirror_rejects_pool_leg` | PASS | | Invariants documented | `docs/indexer-invariants.md`, `skills/AGENTS_INDEXER_HYBRID_BEST_EXECUTION.md` | PASS | | Live QA EMBER↔CORAL on main 8408689 | Not re-run in agent VM (no QA indexer deploy) | SKIP | ## Third-party verification 1. `make setup-indexer-postgres` 2. `cd indexer && cargo test --test api_route_solve_db_hybrid route_solve_db_hybrid_skips_zero_reserve_path_candidate -- --test-threads=1` 3. On QA with `ROUTE_SOLVER_DB_HYBRID=1` and zero-reserve pairs in `pair_reserves`, confirm `GET /api/v1/route/solve?token_in=<A>&token_out=<B>&amount_in=1000000` returns **200** with `solver_version=global_v4` (not 502) when a funded direct pool exists. <!-- CURSOR_SUMMARY --> --- > [!NOTE] > **Medium Risk** > Changes core global best-execution and hybrid simulation error handling for DB-hybrid mode; behavior shifts from whole-request 502 to path skipping and 404, which affects retail quoting when unfunded pairs appear on longer routes. > > **Overview** > Under **`ROUTE_SOLVER_DB_HYBRID=1`**, global route solve no longer fails the whole request with **502** when one enumerated path hits an unfunded pair (`pair_reserves` **0/0**) but another funded path (e.g. a direct pool) still works. > > **Mirror / per-hop sim:** zero-reserve mirrors get **`MirrorFreshness::EmptyPool`**; fresh mirrors that reject the pool leg with **`InsufficientLiquidity`** fall back to LCD instead of aborting the grid. **Global solver:** candidates that cannot simulate any hop surface **`PathUnusable`**, are **skipped** during concurrent evaluation, and the best remaining path wins; if every path is unusable the API returns **404** (not 502). Infra LCD failures still map to **502**. > > Docs/skills and a regression test (`route_solve_db_hybrid_skips_zero_reserve_path_candidate`) cover the new behavior. > > <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit 24690ae7a2fda565443b2caca9dcd182051bf3a6. Bugbot is set up for automated code reviews on this repo. Configure [here](https://www.cursor.com/dashboard/bugbot).</sup> <!-- /CURSOR_SUMMARY -->
ghost1 commented 2026-06-12 06:36:45 +00:00 (Migrated from gitlab.com)

changed the description

changed the description
ghost1 commented 2026-06-12 06:36:52 +00:00 (Migrated from gitlab.com)

changed the description

changed the description
ghost1 commented 2026-06-12 06:38:23 +00:00 (Migrated from gitlab.com)

Cursor Bugbot has reviewed your changes using default effort and found 1 potential issue.

Fix All in Cursor

Bugbot Autofix is ON. A cloud agent has been kicked off to fix the reported issue.

Reviewed by Cursor Bugbot for commit d847275c89. Configure here.

<!-- BUGBOT_REVIEW --> Cursor Bugbot has reviewed your changes using default effort and found 1 potential issue. <!-- BUGBOT_FIX_ALL --> <a href="https://cursor.com/open?link=eyJ2ZXJzaW9uIjoxLCJ0eXBlIjoiQlVHQk9UX0ZJWF9BTExfSU5fQ1VSU09SIiwiZGF0YSI6eyJyZWRpc0tleSI6ImJ1Z2JvdC1tdWx0aTpjZDNkNWU5Yi0yMzdiLTQwYzgtYWY2ZC04ZThlMGE5MDVkMGQiLCJlbmNyeXB0aW9uS2V5IjoiX3hTMU1Lc0NJVjZrZmVVYndWWTBjWEtScjNNR21EQUh4bTNobGREQUJiNCIsImJyYW5jaCI6ImZpeC8zNjktemVyby1yZXNlcnZlLXJvdXRlLXNvbHZlLXNraXAiLCJyZXBvT3duZXIiOiJQbGFzdGljRGlnaXRzIiwicmVwb05hbWUiOiJjbDh5LWRleC10ZXJyYWNsYXNzaWMifX0" target="_blank" rel="noopener noreferrer"><picture><source media="(prefers-color-scheme: dark)" srcset="https://cursor.com/assets/images/fix-in-cursor-dark.png"><source media="(prefers-color-scheme: light)" srcset="https://cursor.com/assets/images/fix-in-cursor-light.png"><img alt="Fix All in Cursor" width="115" height="28" src="https://cursor.com/assets/images/fix-in-cursor-dark.png"></picture></a> <!-- /BUGBOT_FIX_ALL --> <!-- BUGBOT_AUTOFIX_REVIEW_FOOTNOTE_BEGIN --> <sup>Bugbot Autofix is [ON](https://www.cursor.com/dashboard/bugbot). A cloud agent has been kicked off to fix the reported issue. <!-- BUGBOT_AUTOFIX_AGENT_LINK --></sup> <!-- BUGBOT_AUTOFIX_REVIEW_FOOTNOTE_END --> <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit d847275c8997203b6a2c7447558101ebddc652e2. Configure [here](https://www.cursor.com/dashboard/bugbot).</sup>
ghost1 commented 2026-06-12 06:38:24 +00:00 (Migrated from gitlab.com)

Db mirror errors become path skip

Medium Severity

When hop grid search exhausts without a successful candidate, the final query_pool_only_unified fallback maps every non-infra error—including HybridSimError::Db from mirror simulation—to PathUnusable. That skips the path or yields 404 instead of the documented 502 “Route mirror simulation failed” for indexer mirror/data failures.

Additional Locations (1)
Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit d847275c89. Configure here.

### Db mirror errors become path skip **Medium Severity** <!-- DESCRIPTION START --> When hop grid search exhausts without a successful candidate, the final `query_pool_only_unified` fallback maps every non-infra error—including `HybridSimError::Db` from mirror simulation—to `PathUnusable`. That skips the path or yields **404** instead of the documented **502** “Route mirror simulation failed” for indexer mirror/data failures. <!-- DESCRIPTION END --> <!-- BUGBOT_BUG_ID: 1a9a02ec-7e5c-48e0-8481-7f83c8b8e198 --> <!-- LOCATIONS START indexer/src/api/hybrid_route_opt.rs#L392-L412 indexer/src/api/hybrid_route_opt.rs#L392-L393 LOCATIONS END --> <details> <summary>Additional Locations (1)</summary> - [`indexer/src/api/hybrid_route_opt.rs#L392-L393`](https://gitlab.com/PlasticDigits/cl8y-dex-terraclassic/-//blob/d847275c8997203b6a2c7447558101ebddc652e2/indexer/src/api/hybrid_route_opt.rs#L392-L393) </details> <div><a href="https://cursor.com/open?link=eyJ2ZXJzaW9uIjoxLCJ0eXBlIjoiQlVHQk9UX0ZJWF9JTl9DVVJTT1IiLCJkYXRhIjp7InJlZGlzS2V5IjoiYnVnYm90OjdjOTc4NTJlLTg2YjktNDk4MC04YTNmLWMzZTY2N2E1ODA3ZCIsImVuY3J5cHRpb25LZXkiOiIxVFFwSnJoUjNSOERyMmxHbXdCQ1BNUjdqV2VZalZEMVJKZE5PaUpCcVBNIiwiYnJhbmNoIjoiZml4LzM2OS16ZXJvLXJlc2VydmUtcm91dGUtc29sdmUtc2tpcCIsInJlcG9Pd25lciI6IlBsYXN0aWNEaWdpdHMiLCJyZXBvTmFtZSI6ImNsOHktZGV4LXRlcnJhY2xhc3NpYyJ9fQ" target="_blank" rel="noopener noreferrer"><picture><source media="(prefers-color-scheme: dark)" srcset="https://cursor.com/assets/images/fix-in-cursor-dark.png"><source media="(prefers-color-scheme: light)" srcset="https://cursor.com/assets/images/fix-in-cursor-light.png"><img alt="Fix in Cursor" width="115" height="28" src="https://cursor.com/assets/images/fix-in-cursor-dark.png"></picture></a>&nbsp;<a href="https://cursor.com/agents?link=eyJ2ZXJzaW9uIjoxLCJ0eXBlIjoiQlVHQk9UX0ZJWF9JTl9XRUIiLCJkYXRhIjp7InJlZGlzS2V5IjoiYnVnYm90OjdjOTc4NTJlLTg2YjktNDk4MC04YTNmLWMzZTY2N2E1ODA3ZCIsImVuY3J5cHRpb25LZXkiOiIxVFFwSnJoUjNSOERyMmxHbXdCQ1BNUjdqV2VZalZEMVJKZE5PaUpCcVBNIiwiYnJhbmNoIjoiZml4LzM2OS16ZXJvLXJlc2VydmUtcm91dGUtc29sdmUtc2tpcCIsInJlcG9Pd25lciI6IlBsYXN0aWNEaWdpdHMiLCJyZXBvTmFtZSI6ImNsOHktZGV4LXRlcnJhY2xhc3NpYyIsInByTnVtYmVyIjoxNjAsImNvbW1pdFNoYSI6ImQ4NDcyNzVjODk5NzIwM2I2YTJjNzQ0NzU1ODEwMWViZGRjNjUyZTIiLCJwcm92aWRlciI6ImdpdGxhYiIsImhvc3RuYW1lIjoiZ2l0bGFiLmNvbSJ9fQ" target="_blank" rel="noopener noreferrer"><picture><source media="(prefers-color-scheme: dark)" srcset="https://cursor.com/assets/images/fix-in-web-dark.png"><source media="(prefers-color-scheme: light)" srcset="https://cursor.com/assets/images/fix-in-web-light.png"><img alt="Fix in Web" width="99" height="28" src="https://cursor.com/assets/images/fix-in-web-dark.png"></picture></a></div> <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit d847275c8997203b6a2c7447558101ebddc652e2. Configure [here](https://www.cursor.com/dashboard/bugbot).</sup>
PlasticDigits commented 2026-06-12 06:39:33 +00:00 (Migrated from gitlab.com)

Security review — MR !878

Commit reviewed: d847275c8997203b6a2c7447558101ebddc652e2
Scope: DB-hybrid route solver changes for GitLab #369 — EmptyPool mirror classification, per-hop LCD fallback on zero reserves, PathUnusable path-candidate skipping, 502→404 when all candidates unusable. Files: best_execution.rs, hybrid_route_opt.rs, db_orderbook_sim.rs, tests/docs.

Method: Traced attacker-controlled inputs (token_in, token_out, amount_in, optional trader/sender) through mirror load → hybrid grid → candidate merge → router maybe_simulate / fidelity guard. Compared infra vs contract-level LCD error handling and concurrent candidate evaluation behavior.

Outcome: FINDINGS: 0 medium+

No inline threads (no medium+ findings).

Notes (informational, not findings)

  • Path skipping is bounded to enumerated candidates (MAX_PATH_CANDIDATES / SOLVE_CONCURRENCY); the winning path still goes through router simulation and, in DB-hybrid mode, apply_fidelity_guard drift clamping.
  • Infra LCD failures (AllEndpointsFailed, Request) still surface as 502 via is_infra_lcd_error + hybrid_sim_gateway_err; only hop-level “unusable” liquidity (zero pool + failed pool-only fallback) is skipped.
  • pair_reserves are indexer-sourced, not client-writable; zero-reserve topology reflects on-chain pair state, not request injection.
  • Prior security-review threads on this MR: none.

Security review: no medium+ findings on this diff.

## Security review — MR !878 **Commit reviewed:** `d847275c8997203b6a2c7447558101ebddc652e2` **Scope:** DB-hybrid route solver changes for GitLab #369 — `EmptyPool` mirror classification, per-hop LCD fallback on zero reserves, `PathUnusable` path-candidate skipping, 502→404 when all candidates unusable. Files: `best_execution.rs`, `hybrid_route_opt.rs`, `db_orderbook_sim.rs`, tests/docs. **Method:** Traced attacker-controlled inputs (`token_in`, `token_out`, `amount_in`, optional `trader`/`sender`) through mirror load → hybrid grid → candidate merge → router `maybe_simulate` / fidelity guard. Compared infra vs contract-level LCD error handling and concurrent candidate evaluation behavior. **Outcome:** `FINDINGS: 0` medium+ No inline threads (no medium+ findings). ### Notes (informational, not findings) - **Path skipping** is bounded to enumerated candidates (`MAX_PATH_CANDIDATES` / `SOLVE_CONCURRENCY`); the winning path still goes through router simulation and, in DB-hybrid mode, `apply_fidelity_guard` drift clamping. - **Infra LCD failures** (`AllEndpointsFailed`, `Request`) still surface as **502** via `is_infra_lcd_error` + `hybrid_sim_gateway_err`; only hop-level “unusable” liquidity (zero pool + failed pool-only fallback) is skipped. - **`pair_reserves` are indexer-sourced**, not client-writable; zero-reserve topology reflects on-chain pair state, not request injection. - Prior security-review threads on this MR: none. Security review: no medium+ findings on this diff.
ghost1 commented 2026-06-12 06:41:00 +00:00 (Migrated from gitlab.com)

added 1 commit

  • 24690ae7 - fix(indexer): propagate Db mirror errors from pool-only fallback

Compare with previous version

added 1 commit <ul><li>24690ae7 - fix(indexer): propagate Db mirror errors from pool-only fallback</li></ul> [Compare with previous version](/PlasticDigits/cl8y-dex-terraclassic/-/merge_requests/160/diffs?diff_id=1848335576&start_sha=d847275c8997203b6a2c7447558101ebddc652e2)
ghost1 commented 2026-06-12 06:41:13 +00:00 (Migrated from gitlab.com)

changed the description

changed the description
ghost1 commented 2026-06-12 06:41:25 +00:00 (Migrated from gitlab.com)

resolved all threads

resolved all threads
PlasticDigits commented 2026-06-12 07:57:59 +00:00 (Migrated from gitlab.com)

mentioned in commit d64052c857

mentioned in commit d64052c857e9339004836f0daa8e03ffe199a146
PlasticDigits (Migrated from gitlab.com) merged commit d64052c857 into main 2026-06-12 07:57:59 +00:00
Sign in to join this conversation.
No reviewers
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
code/cl8y-dex-terraclassic!878
No description provided.