Phase 1b: book_snapshot loop — mirror reserves + resting book (#322) #793

Merged
PlasticDigits merged 3 commits from cursor/gitlab-issue-workflow-dfd6 into main 2026-06-05 10:39:54 +00:00
PlasticDigits commented 2026-06-05 10:14:34 +00:00 (Migrated from gitlab.com)

Summary

Implements GitLab #322 (Phase 1b): a background book_snapshot loop that periodically mirrors on-chain pool reserves, fee config, and resting limit books into the Phase 1a Postgres tables (pair_reserves, resting_limit_orders).

Why: Phase 1c (#319) needs a populated mirror and a documented freshness contract before the 0-LCD solver can read from Postgres instead of per-request LCD.

Changes

  • New module indexer/src/indexer/book_snapshot.rs with run_book_snapshot_loop, snapshot_all_pairs, freshness constants (BOOK_SNAPSHOT_MAX_STALENESS_MS, etc.), and documented LCD budget (book_snapshot_lcd_budget).
  • Config: book_snapshot_interval_ms / BOOK_SNAPSHOT_INTERVAL_MS (default 10s).
  • Wired via tokio::spawn in poller.rs::run_indexer.
  • Docs: docs/runbooks/book-snapshot-mirror.md, row in docs/indexer-invariants.md.
  • Tests: indexer/tests/book_snapshot_loop.rs, atomicity test in db_orderbook_mirror.rs, wiremock helper start_book_snapshot_mock.

Acceptance checklist

Criterion Verification Result
book_snapshot loop exists and is spawned in poller.rs Code review book_snapshot.rs, poller.rs PASS
Each cycle populates both Phase 1a tables for all pairs cargo test --test book_snapshot_loop snapshot_populates_reserves_and_resting_book -j 1 -- --test-threads=1 PASS
Reserves map to reserve_0/reserve_1; fee_bps from get_fee_config as i16 Same integration test asserts reserves + fee PASS
Resting orders stored with correct fields; walk order via get_pair_resting_book Same test asserts bid/ask order + fields PASS
Bounded LCD per cycle (documented formula) cargo test --lib book_snapshot::tests + lcd_budget_constant_matches_formula PASS
Snapshots stamp snapshot_at + block_height when available Integration tests (height present / absent) PASS
Freshness contract documented; TTL constant exported Module header + docs/runbooks/book-snapshot-mirror.md PASS
Per-pair failure logged/skipped; prior snapshot retained snapshot_skips_failed_pair_and_keeps_prior_snapshot PASS
Config field book_snapshot_interval_ms wired config.rs, .env.example PASS

Verification for third parties

# Unit tests (no Postgres)
cd indexer && cargo test --lib book_snapshot

# Integration tests (Postgres + wiremock)
docker compose up -d postgres
./scripts/setup-postgres-dev-databases.sh
cd indexer && cargo test --test book_snapshot_loop --test db_orderbook_mirror -j 1 -- --test-threads=1

Unblocks Phase 1c (#319).

Issue: https://gitlab.com/PlasticDigits/cl8y-dex-terraclassic/-/work_items/322


Note

Medium Risk
Adds continuous background LCD load (scales with pairs and resting orders) and new mirror state that Phase 1c will trust; failures are isolated per pair but stale mirrors could affect quotes until TTL fallback.

Overview
Adds a background book snapshot mirror (GitLab #322) that periodically copies on-chain pool reserves, fee config, and full resting limit books into Postgres (pair_reserves, resting_limit_orders) for the upcoming Phase 1c 0-LCD solver.

Runtime: New book_snapshot module runs run_book_snapshot_loop on a configurable cadence (BOOK_SNAPSHOT_INTERVAL_MS, default 10s), spawned from poller.rs alongside oracle/tier-sync. Each cycle best-effort reads chain head, then for every indexed pair LCD-queries pool + fee + bid/ask book walks and atomically upserts reserves and replaces resting orders in one transaction. Per-pair LCD errors are logged and skipped so other pairs update and prior rows stay until a successful write.

Config & contract: book_snapshot_interval_ms and staleness helpers (BOOK_SNAPSHOT_MAX_STALENESS_MS = 2× cadence) document a degrade-not-error freshness contract for Phase 1c. LCD budget per cycle is exported via book_snapshot_lcd_budget().

DB layer: upsert_pair_reserves accepts any Postgres executor; replace_pair_resting_orders_in_tx lets the snapshot commit reserves + book together. Public replace_pair_resting_orders still wraps its own transaction.

Docs/tests: Runbook docs/runbooks/book-snapshot-mirror.md, indexer-invariants row, .env.example; integration tests (book_snapshot_loop, wiremock start_book_snapshot_mock), rollback test on invalid resting side.

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

## Summary Implements GitLab [#322](https://gitlab.com/PlasticDigits/cl8y-dex-terraclassic/-/work_items/322) (Phase 1b): a background `book_snapshot` loop that periodically mirrors on-chain pool reserves, fee config, and resting limit books into the Phase 1a Postgres tables (`pair_reserves`, `resting_limit_orders`). **Why:** Phase 1c ([#319](https://gitlab.com/PlasticDigits/cl8y-dex-terraclassic/-/issues/319)) needs a populated mirror and a documented freshness contract before the 0-LCD solver can read from Postgres instead of per-request LCD. ### Changes - New module `indexer/src/indexer/book_snapshot.rs` with `run_book_snapshot_loop`, `snapshot_all_pairs`, freshness constants (`BOOK_SNAPSHOT_MAX_STALENESS_MS`, etc.), and documented LCD budget (`book_snapshot_lcd_budget`). - Config: `book_snapshot_interval_ms` / `BOOK_SNAPSHOT_INTERVAL_MS` (default 10s). - Wired via `tokio::spawn` in `poller.rs::run_indexer`. - Docs: `docs/runbooks/book-snapshot-mirror.md`, row in `docs/indexer-invariants.md`. - Tests: `indexer/tests/book_snapshot_loop.rs`, atomicity test in `db_orderbook_mirror.rs`, wiremock helper `start_book_snapshot_mock`. ## Acceptance checklist | Criterion | Verification | Result | |-----------|--------------|--------| | `book_snapshot` loop exists and is spawned in `poller.rs` | Code review `book_snapshot.rs`, `poller.rs` | PASS | | Each cycle populates both Phase 1a tables for all pairs | `cargo test --test book_snapshot_loop snapshot_populates_reserves_and_resting_book -j 1 -- --test-threads=1` | PASS | | Reserves map to `reserve_0`/`reserve_1`; `fee_bps` from `get_fee_config` as `i16` | Same integration test asserts reserves + fee | PASS | | Resting orders stored with correct fields; walk order via `get_pair_resting_book` | Same test asserts bid/ask order + fields | PASS | | Bounded LCD per cycle (documented formula) | `cargo test --lib book_snapshot::tests` + `lcd_budget_constant_matches_formula` | PASS | | Snapshots stamp `snapshot_at` + `block_height` when available | Integration tests (height present / absent) | PASS | | Freshness contract documented; TTL constant exported | Module header + `docs/runbooks/book-snapshot-mirror.md` | PASS | | Per-pair failure logged/skipped; prior snapshot retained | `snapshot_skips_failed_pair_and_keeps_prior_snapshot` | PASS | | Config field `book_snapshot_interval_ms` wired | `config.rs`, `.env.example` | PASS | ## Verification for third parties ```bash # Unit tests (no Postgres) cd indexer && cargo test --lib book_snapshot # Integration tests (Postgres + wiremock) docker compose up -d postgres ./scripts/setup-postgres-dev-databases.sh cd indexer && cargo test --test book_snapshot_loop --test db_orderbook_mirror -j 1 -- --test-threads=1 ``` Unblocks Phase 1c ([#319](https://gitlab.com/PlasticDigits/cl8y-dex-terraclassic/-/issues/319)). Issue: https://gitlab.com/PlasticDigits/cl8y-dex-terraclassic/-/work_items/322 <!-- CURSOR_SUMMARY --> --- > [!NOTE] > **Medium Risk** > Adds continuous background LCD load (scales with pairs and resting orders) and new mirror state that Phase 1c will trust; failures are isolated per pair but stale mirrors could affect quotes until TTL fallback. > > **Overview** > Adds a **background book snapshot mirror** (GitLab #322) that periodically copies on-chain pool reserves, fee config, and full resting limit books into Postgres (`pair_reserves`, `resting_limit_orders`) for the upcoming Phase 1c 0-LCD solver. > > **Runtime:** New `book_snapshot` module runs `run_book_snapshot_loop` on a configurable cadence (`BOOK_SNAPSHOT_INTERVAL_MS`, default 10s), spawned from `poller.rs` alongside oracle/tier-sync. Each cycle best-effort reads chain head, then for every indexed pair LCD-queries pool + fee + bid/ask book walks and **atomically** upserts reserves and replaces resting orders in one transaction. Per-pair LCD errors are logged and skipped so other pairs update and prior rows stay until a successful write. > > **Config & contract:** `book_snapshot_interval_ms` and staleness helpers (`BOOK_SNAPSHOT_MAX_STALENESS_MS` = 2× cadence) document a **degrade-not-error** freshness contract for Phase 1c. LCD budget per cycle is exported via `book_snapshot_lcd_budget()`. > > **DB layer:** `upsert_pair_reserves` accepts any Postgres executor; `replace_pair_resting_orders_in_tx` lets the snapshot commit reserves + book together. Public `replace_pair_resting_orders` still wraps its own transaction. > > **Docs/tests:** Runbook `docs/runbooks/book-snapshot-mirror.md`, indexer-invariants row, `.env.example`; integration tests (`book_snapshot_loop`, wiremock `start_book_snapshot_mock`), rollback test on invalid resting `side`. > > <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit af03fbda73270bb119bf9cd0b09a75e9871e3c00. 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-05 10:14:42 +00:00 (Migrated from gitlab.com)

changed the description

changed the description
PlasticDigits commented 2026-06-05 10:14:47 +00:00 (Migrated from gitlab.com)

changed the description

changed the description
PlasticDigits commented 2026-06-05 10:14:48 +00:00 (Migrated from gitlab.com)

mentioned in issue #322

mentioned in issue #322
ghost1 commented 2026-06-05 10:14:51 +00:00 (Migrated from gitlab.com)

changed the description

changed the description
ghost1 commented 2026-06-05 10:15:49 +00:00 (Migrated from gitlab.com)
Stale Security Review comment

Cursor Bugbot has reviewed your changes and found 2 potential issues.
Bugbot Autofix is ON. A cloud agent has been kicked off to fix the reported issues.

Reviewed by Cursor Bugbot for commit 6c5a09acee. Configure here.

<details> <summary>Stale Security Review comment</summary> <!-- BUGBOT_REVIEW --> Cursor Bugbot has reviewed your changes and found 2 potential issues.<!-- 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 issues. <!-- BUGBOT_AUTOFIX_AGENT_LINK --></sup> <!-- BUGBOT_AUTOFIX_REVIEW_FOOTNOTE_END --> <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit 6c5a09acee8cc131c6d1025bb49c66449bfa3a59. Configure [here](https://www.cursor.com/dashboard/bugbot).</sup> </details>
ghost1 commented 2026-06-05 10:15:50 +00:00 (Migrated from gitlab.com)

Staleness ignores configured cadence

Medium Severity

BOOK_SNAPSHOT_MAX_STALENESS_MS is fixed at default cadence × tolerance, while BOOK_SNAPSHOT_INTERVAL_MS can change the loop interval. Phase 1c is documented to import the constant as the TTL, so custom intervals make staleness checks too strict or too loose relative to the actual mirror cadence.

Additional Locations (1)
Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit 6c5a09acee. Configure here.

### Staleness ignores configured cadence **Medium Severity** <!-- DESCRIPTION START --> `BOOK_SNAPSHOT_MAX_STALENESS_MS` is fixed at default cadence × tolerance, while `BOOK_SNAPSHOT_INTERVAL_MS` can change the loop interval. Phase 1c is documented to import the constant as the TTL, so custom intervals make staleness checks too strict or too loose relative to the actual mirror cadence. <!-- DESCRIPTION END --> <!-- BUGBOT_BUG_ID: e6f1ffcb-6fc9-4c1c-b687-4fe8d0383fbc --> <!-- LOCATIONS START indexer/src/indexer/book_snapshot.rs#L42-L45 indexer/src/config.rs#L195-L199 LOCATIONS END --> <details> <summary>Additional Locations (1)</summary> - [`indexer/src/config.rs#L195-L199`](https://gitlab.com/PlasticDigits/cl8y-dex-terraclassic/-//blob/6c5a09acee8cc131c6d1025bb49c66449bfa3a59/indexer/src/config.rs#L195-L199) </details> <div><a href="https://cursor.com/open?link=eyJ2ZXJzaW9uIjoxLCJ0eXBlIjoiQlVHQk9UX0ZJWF9JTl9DVVJTT1IiLCJkYXRhIjp7InJlZGlzS2V5IjoiYnVnYm90OmJkN2E0MTJmLWNmYTYtNDliZC04MDM3LWZkMGQzZmM0Nzk1ZiIsImVuY3J5cHRpb25LZXkiOiJ1WXBIQ1RjUk1TWndIRHlLSGFDeGNHTk45T2lVb2VHZWdOR0c1UzVtS1dvIiwiYnJhbmNoIjoiY3Vyc29yL2dpdGxhYi1pc3N1ZS13b3JrZmxvdy1kZmQ2IiwicmVwb093bmVyIjoiUGxhc3RpY0RpZ2l0cyIsInJlcG9OYW1lIjoiY2w4eS1kZXgtdGVycmFjbGFzc2ljIn19" 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=eyJ2ZXJzaW9uIjoxLCJ0eXBlIjoiQlVHQk9UX0ZJWF9JTl9XRUIiLCJkYXRhIjp7InJlZGlzS2V5IjoiYnVnYm90OmJkN2E0MTJmLWNmYTYtNDliZC04MDM3LWZkMGQzZmM0Nzk1ZiIsImVuY3J5cHRpb25LZXkiOiJ1WXBIQ1RjUk1TWndIRHlLSGFDeGNHTk45T2lVb2VHZWdOR0c1UzVtS1dvIiwiYnJhbmNoIjoiY3Vyc29yL2dpdGxhYi1pc3N1ZS13b3JrZmxvdy1kZmQ2IiwicmVwb093bmVyIjoiUGxhc3RpY0RpZ2l0cyIsInJlcG9OYW1lIjoiY2w4eS1kZXgtdGVycmFjbGFzc2ljIiwicHJOdW1iZXIiOjc1LCJjb21taXRTaGEiOiI2YzVhMDlhY2VlOGNjMTMxYzZkMTAyNWJiNDljNjY0NDliZmEzYTU5IiwicHJvdmlkZXIiOiJnaXRsYWIiLCJob3N0bmFtZSI6ImdpdGxhYi5jb20ifX0" 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 6c5a09acee8cc131c6d1025bb49c66449bfa3a59. Configure [here](https://www.cursor.com/dashboard/bugbot).</sup>
ghost1 commented 2026-06-05 10:15:50 +00:00 (Migrated from gitlab.com)

Reserves commit before book

High Severity

snapshot_single_pair upserts pair_reserves before the resting-book LCD walk and replace_pair_resting_orders. If anything fails afterward, the pair is skipped but reserves already reflect the new pool state while resting_limit_orders stays on the prior snapshot, breaking the intended per-pair consistent mirror for Phase 1c hybrid quotes.

Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit 6c5a09acee. Configure here.

### Reserves commit before book **High Severity** <!-- DESCRIPTION START --> `snapshot_single_pair` upserts `pair_reserves` before the resting-book LCD walk and `replace_pair_resting_orders`. If anything fails afterward, the pair is skipped but reserves already reflect the new pool state while `resting_limit_orders` stays on the prior snapshot, breaking the intended per-pair consistent mirror for Phase 1c hybrid quotes. <!-- DESCRIPTION END --> <!-- BUGBOT_BUG_ID: 0e82308c-1b95-475a-9c2d-3edc9808533f --> <!-- LOCATIONS START indexer/src/indexer/book_snapshot.rs#L125-L142 LOCATIONS END --> <div><a href="https://cursor.com/open?link=eyJ2ZXJzaW9uIjoxLCJ0eXBlIjoiQlVHQk9UX0ZJWF9JTl9DVVJTT1IiLCJkYXRhIjp7InJlZGlzS2V5IjoiYnVnYm90OmQzMzA0M2Q4LTJiOWEtNGUyMC1iMmNiLWQ1YTRkZDI2YWUyZSIsImVuY3J5cHRpb25LZXkiOiJ1MDNnbVpnUTVLcjM4U0ZEbmNTVnRJTmFvMHV3dlZHV1daenlyU1pwVG9vIiwiYnJhbmNoIjoiY3Vyc29yL2dpdGxhYi1pc3N1ZS13b3JrZmxvdy1kZmQ2IiwicmVwb093bmVyIjoiUGxhc3RpY0RpZ2l0cyIsInJlcG9OYW1lIjoiY2w4eS1kZXgtdGVycmFjbGFzc2ljIn19" 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=eyJ2ZXJzaW9uIjoxLCJ0eXBlIjoiQlVHQk9UX0ZJWF9JTl9XRUIiLCJkYXRhIjp7InJlZGlzS2V5IjoiYnVnYm90OmQzMzA0M2Q4LTJiOWEtNGUyMC1iMmNiLWQ1YTRkZDI2YWUyZSIsImVuY3J5cHRpb25LZXkiOiJ1MDNnbVpnUTVLcjM4U0ZEbmNTVnRJTmFvMHV3dlZHV1daenlyU1pwVG9vIiwiYnJhbmNoIjoiY3Vyc29yL2dpdGxhYi1pc3N1ZS13b3JrZmxvdy1kZmQ2IiwicmVwb093bmVyIjoiUGxhc3RpY0RpZ2l0cyIsInJlcG9OYW1lIjoiY2w4eS1kZXgtdGVycmFjbGFzc2ljIiwicHJOdW1iZXIiOjc1LCJjb21taXRTaGEiOiI2YzVhMDlhY2VlOGNjMTMxYzZkMTAyNWJiNDljNjY0NDliZmEzYTU5IiwicHJvdmlkZXIiOiJnaXRsYWIiLCJob3N0bmFtZSI6ImdpdGxhYi5jb20ifX0" 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 6c5a09acee8cc131c6d1025bb49c66449bfa3a59. Configure [here](https://www.cursor.com/dashboard/bugbot).</sup>
PlasticDigits commented 2026-06-05 10:16:15 +00:00 (Migrated from gitlab.com)

Security review (automated)

Commit reviewed: 6c5a09acee8cc131c6d1025bb49c66449bfa3a59
MR: !793 — Phase 1b: book_snapshot loop (mirror reserves + resting book, #322)
Scope: 11 files — new book_snapshot.rs, poller spawn, config/env, docs/runbook, integration tests (no new HTTP routes).

Method

  • Diff + sink tracing for injection, authz, SSRF, secret leakage, and LCD/DB amplification.
  • Compared book-walk patterns to existing limit_book_lcd.rs (HTTP path already has per-request LCD budgets).
  • Checked prior MR discussion threads — none found.

Outcome

FINDINGS: 0 (no medium, high, or critical issues on this diff)

Inline threads: none

Notes (below medium / not reported as findings)

  • SQL: upsert_pair_reserves / replace_pair_resting_orders use parameterized sqlx binds; side in reads is limited to "bid"/"ask" for ORDER BY only.
  • Trust model: LCD queries use pair.contract_address from factory-synced pairs rows (same trust boundary as existing indexer LCD usage); not a new external input surface.
  • Availability: Full-book walks are unbounded per pair (documented LCD budget). Deep books or a stuck walk can delay mirror freshness for other pairs in the same cycle; Phase 1c is designed to degrade to LCD on stale/missing mirror (BOOK_SNAPSHOT_MAX_STALENESS_MS). Operational concern, not an authn/authz or injection flaw introduced here.
  • Integrity: Per-pair failures skip and retain last good snapshot; resting-book replace is transactional (rollback tested).

Security review: no medium+ findings on this diff.

## Security review (automated) **Commit reviewed:** `6c5a09acee8cc131c6d1025bb49c66449bfa3a59` **MR:** !793 — Phase 1b: `book_snapshot` loop (mirror reserves + resting book, #322) **Scope:** 11 files — new `book_snapshot.rs`, poller spawn, config/env, docs/runbook, integration tests (no new HTTP routes). ### Method - Diff + sink tracing for injection, authz, SSRF, secret leakage, and LCD/DB amplification. - Compared book-walk patterns to existing `limit_book_lcd.rs` (HTTP path already has per-request LCD budgets). - Checked prior MR discussion threads — none found. ### Outcome **FINDINGS: 0** (no medium, high, or critical issues on this diff) **Inline threads:** none ### Notes (below medium / not reported as findings) - **SQL:** `upsert_pair_reserves` / `replace_pair_resting_orders` use parameterized `sqlx` binds; `side` in reads is limited to `"bid"`/`"ask"` for `ORDER BY` only. - **Trust model:** LCD queries use `pair.contract_address` from factory-synced `pairs` rows (same trust boundary as existing indexer LCD usage); not a new external input surface. - **Availability:** Full-book walks are unbounded per pair (documented LCD budget). Deep books or a stuck walk can delay mirror freshness for other pairs in the same cycle; Phase 1c is designed to degrade to LCD on stale/missing mirror (`BOOK_SNAPSHOT_MAX_STALENESS_MS`). Operational concern, not an authn/authz or injection flaw introduced here. - **Integrity:** Per-pair failures skip and retain last good snapshot; resting-book replace is transactional (rollback tested). Security review: **no medium+ findings** on this diff.
ghost1 commented 2026-06-05 10:19:27 +00:00 (Migrated from gitlab.com)

changed this line in version 2 of the diff

changed this line in [version 2 of the diff](/PlasticDigits/cl8y-dex-terraclassic/-/merge_requests/75/diffs?diff_id=1836432381&start_sha=6c5a09acee8cc131c6d1025bb49c66449bfa3a59#15904acd6fd27a3b11eae9f94cb5117502ea7d3b_45_44)
ghost1 commented 2026-06-05 10:19:27 +00:00 (Migrated from gitlab.com)

changed this line in version 2 of the diff

changed this line in [version 2 of the diff](/PlasticDigits/cl8y-dex-terraclassic/-/merge_requests/75/diffs?diff_id=1836432381&start_sha=6c5a09acee8cc131c6d1025bb49c66449bfa3a59#15904acd6fd27a3b11eae9f94cb5117502ea7d3b_142_135)
ghost1 commented 2026-06-05 10:19:28 +00:00 (Migrated from gitlab.com)

added 1 commit

  • 66b3ce95 - Fix book snapshot atomicity and staleness TTL cadence linkage

Compare with previous version

added 1 commit <ul><li>66b3ce95 - Fix book snapshot atomicity and staleness TTL cadence linkage</li></ul> [Compare with previous version](/PlasticDigits/cl8y-dex-terraclassic/-/merge_requests/75/diffs?diff_id=1836432381&start_sha=6c5a09acee8cc131c6d1025bb49c66449bfa3a59)
ghost1 commented 2026-06-05 10:19:49 +00:00 (Migrated from gitlab.com)

changed the description

changed the description
ghost1 commented 2026-06-05 10:20:30 +00:00 (Migrated from gitlab.com)

resolved all threads

resolved all threads
ghost1 commented 2026-06-05 10:20:30 +00:00 (Migrated from gitlab.com)

resolved all threads

resolved all threads
PlasticDigits commented 2026-06-05 10:21:21 +00:00 (Migrated from gitlab.com)

Security review (automated)

Commit reviewed: 66b3ce95acebdf069356b6f90d6b4c05f9f37cb5
MR: !793 — Phase 1b: book_snapshot loop (mirror reserves + resting book, #322)
Scope: 13 files — book_snapshot.rs, poller spawn, config/env, DB query transaction wiring, docs/runbook, integration tests (no new HTTP routes).

Method

  • Full diff review vs main, with sink tracing for injection, authz, SSRF, secret leakage, and LCD/DB amplification.
  • Re-checked prior automated review on 6c5a09a and Bugbot threads; the atomicity fix (single transaction for reserves + resting book) and cadence-linked staleness TTL are present in this commit.
  • Compared book-walk patterns to existing limit_book_lcd.rs (HTTP path already has per-request LCD budgets).

Outcome

FINDINGS: 0 (no medium, high, or critical issues on this diff)

Inline threads: none

Notes (below medium / not reported as findings)

  • SQL: upsert_pair_reserves / replace_pair_resting_orders_in_tx use parameterized sqlx binds; side in reads is limited to "bid"/"ask" for ORDER BY only.
  • Trust model: LCD queries use pair.contract_address from factory-synced pairs rows (same trust boundary as existing indexer LCD usage); not a new external input surface.
  • Integrity: Per-pair reserves + resting-book writes are now atomic (pool.begin() → upsert → replace → commit); rollback path tested in db_orderbook_mirror.rs.
  • Availability: Full-book walks are unbounded per pair (documented LCD budget). Deep books or a stuck walk can delay mirror freshness for other pairs in the same cycle; Phase 1c is designed to degrade to LCD on stale/missing mirror (book_snapshot_max_staleness_ms). Operational concern, not an authn/authz or injection flaw introduced here.

Security review: no medium+ findings on this diff.

## Security review (automated) **Commit reviewed:** `66b3ce95acebdf069356b6f90d6b4c05f9f37cb5` **MR:** !793 — Phase 1b: `book_snapshot` loop (mirror reserves + resting book, #322) **Scope:** 13 files — `book_snapshot.rs`, poller spawn, config/env, DB query transaction wiring, docs/runbook, integration tests (no new HTTP routes). ### Method - Full diff review vs `main`, with sink tracing for injection, authz, SSRF, secret leakage, and LCD/DB amplification. - Re-checked prior automated review on `6c5a09a` and Bugbot threads; the atomicity fix (single transaction for reserves + resting book) and cadence-linked staleness TTL are present in this commit. - Compared book-walk patterns to existing `limit_book_lcd.rs` (HTTP path already has per-request LCD budgets). ### Outcome **FINDINGS: 0** (no medium, high, or critical issues on this diff) **Inline threads:** none ### Notes (below medium / not reported as findings) - **SQL:** `upsert_pair_reserves` / `replace_pair_resting_orders_in_tx` use parameterized `sqlx` binds; `side` in reads is limited to `"bid"`/`"ask"` for `ORDER BY` only. - **Trust model:** LCD queries use `pair.contract_address` from factory-synced `pairs` rows (same trust boundary as existing indexer LCD usage); not a new external input surface. - **Integrity:** Per-pair reserves + resting-book writes are now atomic (`pool.begin()` → upsert → replace → `commit`); rollback path tested in `db_orderbook_mirror.rs`. - **Availability:** Full-book walks are unbounded per pair (documented LCD budget). Deep books or a stuck walk can delay mirror freshness for other pairs in the same cycle; Phase 1c is designed to degrade to LCD on stale/missing mirror (`book_snapshot_max_staleness_ms`). Operational concern, not an authn/authz or injection flaw introduced here. Security review: **no medium+ findings** on this diff.
ghost1 commented 2026-06-05 10:21:53 +00:00 (Migrated from gitlab.com)

Cursor Bugbot has reviewed your changes 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 66b3ce95ac. Configure here.

<!-- BUGBOT_REVIEW --> Cursor Bugbot has reviewed your changes and found 1 potential issue. <!-- BUGBOT_FIX_ALL --> <a href="https://cursor.com/open?link=eyJ2ZXJzaW9uIjoxLCJ0eXBlIjoiQlVHQk9UX0ZJWF9BTExfSU5fQ1VSU09SIiwiZGF0YSI6eyJyZWRpc0tleSI6ImJ1Z2JvdC1tdWx0aTo5MzRhZGZkMy0zMDEyLTRlYzUtYWIwMy1hY2FjYWU1M2RlMGIiLCJlbmNyeXB0aW9uS2V5IjoieFpfekxkSDhhSndfc2s5RjRYSU5SM1FhUnFwY0FUS3N3NXplbFhmMncxdyIsImJyYW5jaCI6ImN1cnNvci9naXRsYWItaXNzdWUtd29ya2Zsb3ctZGZkNiIsInJlcG9Pd25lciI6IlBsYXN0aWNEaWdpdHMiLCJyZXBvTmFtZSI6ImNsOHktZGV4LXRlcnJhY2xhc3NpYyJ9fQ" 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 66b3ce95acebdf069356b6f90d6b4c05f9f37cb5. Configure [here](https://www.cursor.com/dashboard/bugbot).</sup>
ghost1 commented 2026-06-05 10:21:54 +00:00 (Migrated from gitlab.com)

Book walk lacks cycle guard

Medium Severity

walk_resting_book_side follows next pointers until None with no visited-order check or iteration cap. A cyclic or corrupted on-chain book can keep the snapshot task in an endless LCD walk instead of failing the pair and keeping the last good mirror.

Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit 66b3ce95ac. Configure here.

### Book walk lacks cycle guard **Medium Severity** <!-- DESCRIPTION START --> `walk_resting_book_side` follows `next` pointers until `None` with no visited-order check or iteration cap. A cyclic or corrupted on-chain book can keep the snapshot task in an endless LCD walk instead of failing the pair and keeping the last good mirror. <!-- DESCRIPTION END --> <!-- BUGBOT_BUG_ID: 8a688812-4cbf-4ffc-833b-06b718b775da --> <!-- LOCATIONS START indexer/src/indexer/book_snapshot.rs#L208-L232 LOCATIONS END --> <div><a href="https://cursor.com/open?link=eyJ2ZXJzaW9uIjoxLCJ0eXBlIjoiQlVHQk9UX0ZJWF9JTl9DVVJTT1IiLCJkYXRhIjp7InJlZGlzS2V5IjoiYnVnYm90OmE0M2U0MTIzLTUyMDEtNGI5YS05MjhhLWQ5NmRiMTRiZjY4ZSIsImVuY3J5cHRpb25LZXkiOiJsMnFzT2hMVTRSUDM2bmhRQlFHajZYXzNJbUhqSkxZdjZsMTE0bXg4NXdvIiwiYnJhbmNoIjoiY3Vyc29yL2dpdGxhYi1pc3N1ZS13b3JrZmxvdy1kZmQ2IiwicmVwb093bmVyIjoiUGxhc3RpY0RpZ2l0cyIsInJlcG9OYW1lIjoiY2w4eS1kZXgtdGVycmFjbGFzc2ljIn19" 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=eyJ2ZXJzaW9uIjoxLCJ0eXBlIjoiQlVHQk9UX0ZJWF9JTl9XRUIiLCJkYXRhIjp7InJlZGlzS2V5IjoiYnVnYm90OmE0M2U0MTIzLTUyMDEtNGI5YS05MjhhLWQ5NmRiMTRiZjY4ZSIsImVuY3J5cHRpb25LZXkiOiJsMnFzT2hMVTRSUDM2bmhRQlFHajZYXzNJbUhqSkxZdjZsMTE0bXg4NXdvIiwiYnJhbmNoIjoiY3Vyc29yL2dpdGxhYi1pc3N1ZS13b3JrZmxvdy1kZmQ2IiwicmVwb093bmVyIjoiUGxhc3RpY0RpZ2l0cyIsInJlcG9OYW1lIjoiY2w4eS1kZXgtdGVycmFjbGFzc2ljIiwicHJOdW1iZXIiOjc1LCJjb21taXRTaGEiOiI2NmIzY2U5NWFjZWJkZjA2OTM1NmI2ZjkwZDZiNGMwNWY5ZjM3Y2I1IiwicHJvdmlkZXIiOiJnaXRsYWIiLCJob3N0bmFtZSI6ImdpdGxhYi5jb20ifX0" 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 66b3ce95acebdf069356b6f90d6b4c05f9f37cb5. Configure [here](https://www.cursor.com/dashboard/bugbot).</sup>
ghost1 commented 2026-06-05 10:32:51 +00:00 (Migrated from gitlab.com)

added 1 commit

  • af03fbda - fix(indexer): detect cycles when walking resting book for snapshot

Compare with previous version

added 1 commit <ul><li>af03fbda - fix(indexer): detect cycles when walking resting book for snapshot</li></ul> [Compare with previous version](/PlasticDigits/cl8y-dex-terraclassic/-/merge_requests/75/diffs?diff_id=1836462483&start_sha=66b3ce95acebdf069356b6f90d6b4c05f9f37cb5)
ghost1 commented 2026-06-05 10:33:03 +00:00 (Migrated from gitlab.com)

changed the description

changed the description
ghost1 commented 2026-06-05 10:33:05 +00:00 (Migrated from gitlab.com)

resolved all threads

resolved all threads
PlasticDigits commented 2026-06-05 10:35:46 +00:00 (Migrated from gitlab.com)

Security review (automated)

Commit reviewed: af03fbda73270bb119bf9cd0b09a75e9871e3c00
MR: !793 — Phase 1b: book_snapshot loop (mirror reserves + resting book, #322)
Scope: 13 files — book_snapshot.rs, poller spawn, config/env, DB query + migrations, docs/runbook, integration tests (no new HTTP routes).

Method

  • Full diff review vs main on the book_snapshot scope, with sink tracing for injection, authz, SSRF, secret leakage, and LCD/DB amplification.
  • Re-checked prior automated reviews on 6c5a09a / 66b3ce9, Bugbot threads, and the cycle-guard fix in this commit (walk_resting_book_side HashSet + early Err on revisited order_id).
  • Compared book-walk patterns to existing limit_book_lcd.rs (HTTP path has per-request LCD budgets; background mirror intentionally walks full books).

Outcome

FINDINGS: 0 (no medium, high, or critical issues on this diff)

Inline threads: none

Notes (below medium / not reported as findings)

  • SQL: upsert_pair_reserves / replace_pair_resting_orders_in_tx use parameterized sqlx binds; get_pair_resting_book interpolates only ASC/DESC from a "bid"/"ask" branch — side value itself is bound as $2.
  • Trust model: LCD queries use pair.contract_address from factory-synced pairs rows (same trust boundary as existing indexer LCD usage); not a new external input surface.
  • Integrity: Per-pair reserves + resting-book writes are atomic (pool.begin() → upsert → replace → commit); rollback path tested in db_orderbook_mirror.rs. resting_limit_orders.side CHECK constraint blocks invalid sides.
  • Availability: Cycle detection now fails the pair instead of looping indefinitely. Full-book walks remain unbounded per pair (documented LCD budget); very deep books can increase per-cycle LCD load and delay mirror freshness for later pairs in the same cycle — Phase 1c is designed to degrade to LCD on stale/missing mirror (book_snapshot_max_staleness_ms). Operational concern, not an authn/authz or injection flaw introduced here.

Security review: no medium+ findings on this diff.

## Security review (automated) **Commit reviewed:** `af03fbda73270bb119bf9cd0b09a75e9871e3c00` **MR:** !793 — Phase 1b: `book_snapshot` loop (mirror reserves + resting book, #322) **Scope:** 13 files — `book_snapshot.rs`, poller spawn, config/env, DB query + migrations, docs/runbook, integration tests (no new HTTP routes). ### Method - Full diff review vs `main` on the book_snapshot scope, with sink tracing for injection, authz, SSRF, secret leakage, and LCD/DB amplification. - Re-checked prior automated reviews on `6c5a09a` / `66b3ce9`, Bugbot threads, and the cycle-guard fix in this commit (`walk_resting_book_side` `HashSet` + early `Err` on revisited `order_id`). - Compared book-walk patterns to existing `limit_book_lcd.rs` (HTTP path has per-request LCD budgets; background mirror intentionally walks full books). ### Outcome **FINDINGS: 0** (no medium, high, or critical issues on this diff) **Inline threads:** none ### Notes (below medium / not reported as findings) - **SQL:** `upsert_pair_reserves` / `replace_pair_resting_orders_in_tx` use parameterized `sqlx` binds; `get_pair_resting_book` interpolates only `ASC`/`DESC` from a `"bid"`/`"ask"` branch — `side` value itself is bound as `$2`. - **Trust model:** LCD queries use `pair.contract_address` from factory-synced `pairs` rows (same trust boundary as existing indexer LCD usage); not a new external input surface. - **Integrity:** Per-pair reserves + resting-book writes are atomic (`pool.begin()` → upsert → replace → `commit`); rollback path tested in `db_orderbook_mirror.rs`. `resting_limit_orders.side` CHECK constraint blocks invalid sides. - **Availability:** Cycle detection now fails the pair instead of looping indefinitely. Full-book walks remain unbounded per pair (documented LCD budget); very deep books can increase per-cycle LCD load and delay mirror freshness for later pairs in the same cycle — Phase 1c is designed to degrade to LCD on stale/missing mirror (`book_snapshot_max_staleness_ms`). Operational concern, not an authn/authz or injection flaw introduced here. Security review: **no medium+ findings** on this diff.
PlasticDigits (Migrated from gitlab.com) merged commit 74c0449118 into main 2026-06-05 10:39:54 +00:00
PlasticDigits commented 2026-06-05 10:39:55 +00:00 (Migrated from gitlab.com)

mentioned in commit 74c0449118

mentioned in commit 74c0449118fc30040f3764f8a444d8ffcc1eafa5
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!793
No description provided.