Indexer ingest: log ordering and PodiumTimerArmed decode (#346) #521

Merged
PlasticDigits merged 1 commit from issue-346-ingest-log-order-podium-timer-armed into main 2026-06-25 22:23:45 +00:00
PlasticDigits commented 2026-06-25 22:17:15 +00:00 (Migrated from gitlab.com)

Summary

Indexer ingest now sorts each block's eth_getLogs rows by (block_number, transaction_index, log_index) before decode/persist, so global last_buy_epoch assignment matches onchain execution order when RPC returns misordered logs (#278 risk, #346).

Adds full PodiumTimerArmed decode/persist path → idx_arena_podium_timer_armed (migration, reorg rollback, schema 2.20.0). Extends verify-last-buy-epoch-anvil.sh to assert timer-arm rows and fixes sync wait to use public GET /v1/status max_indexed_block.

Acceptance checklist

Criterion Command / step Result
Logs sorted before persist loop rg 'sort_rpc_logs_canonical' indexer/src/ingestion.rs + unit tests in ingestion_tests PASS
PodiumTimerArmed decoded and persisted cd indexer && cargo test postgres_stage2_persist_all_events (includes new event) PASS
Order independence for last_buy_epoch cd indexer && cargo test last_buy_epoch_order_independent_on_shuffled_logs PASS
cargo clippy clean cd indexer && cargo clippy --all-targets -- -D warnings PASS
Integration stage2 last-buy epoch tests cd indexer && cargo test --test integration_stage2 last_buy_epoch PASS
Anvil smoke (epoch + timer arm) bash scripts/verify-last-buy-epoch-anvil.sh PASS

Verification for third parties

export YIELDOMEGA_PG_TEST_URL=postgres://yieldomega:password@127.0.0.1:5433/yieldomega_indexer_test
bash scripts/bootstrap-cloud-postgres-native.sh
cd indexer && cargo clippy --all-targets -- -D warnings && cargo test
export PATH="$HOME/.foundry/bin:$PATH"
bash scripts/verify-last-buy-epoch-anvil.sh

After an Anvil buy that arms the Last Buy timer, confirm:

psql "$DATABASE_URL" -c "SELECT category, epoch, block_number FROM idx_arena_podium_timer_armed ORDER BY block_number DESC LIMIT 5;"

Related: #346 · parent #342

## Summary Indexer ingest now sorts each block's `eth_getLogs` rows by `(block_number, transaction_index, log_index)` before decode/persist, so global `last_buy_epoch` assignment matches onchain execution order when RPC returns misordered logs ([#278](https://gitlab.com/PlasticDigits/yieldomega/-/issues/278) risk, [#346](https://gitlab.com/PlasticDigits/yieldomega/-/issues/346)). Adds full `PodiumTimerArmed` decode/persist path → `idx_arena_podium_timer_armed` (migration, reorg rollback, schema **2.20.0**). Extends `verify-last-buy-epoch-anvil.sh` to assert timer-arm rows and fixes sync wait to use public `GET /v1/status` `max_indexed_block`. ## Acceptance checklist | Criterion | Command / step | Result | |-----------|----------------|--------| | Logs sorted before persist loop | `rg 'sort_rpc_logs_canonical' indexer/src/ingestion.rs` + unit tests in `ingestion_tests` | PASS | | `PodiumTimerArmed` decoded and persisted | `cd indexer && cargo test postgres_stage2_persist_all_events` (includes new event) | PASS | | Order independence for `last_buy_epoch` | `cd indexer && cargo test last_buy_epoch_order_independent_on_shuffled_logs` | PASS | | `cargo clippy` clean | `cd indexer && cargo clippy --all-targets -- -D warnings` | PASS | | Integration stage2 last-buy epoch tests | `cd indexer && cargo test --test integration_stage2 last_buy_epoch` | PASS | | Anvil smoke (epoch + timer arm) | `bash scripts/verify-last-buy-epoch-anvil.sh` | PASS | ## Verification for third parties ```bash export YIELDOMEGA_PG_TEST_URL=postgres://yieldomega:password@127.0.0.1:5433/yieldomega_indexer_test bash scripts/bootstrap-cloud-postgres-native.sh cd indexer && cargo clippy --all-targets -- -D warnings && cargo test export PATH="$HOME/.foundry/bin:$PATH" bash scripts/verify-last-buy-epoch-anvil.sh ``` After an Anvil buy that arms the Last Buy timer, confirm: ```bash psql "$DATABASE_URL" -c "SELECT category, epoch, block_number FROM idx_arena_podium_timer_armed ORDER BY block_number DESC LIMIT 5;" ``` Related: [#346](https://gitlab.com/PlasticDigits/yieldomega/-/issues/346) · parent [#342](https://gitlab.com/PlasticDigits/yieldomega/-/issues/342)
PlasticDigits commented 2026-06-25 22:19:17 +00:00 (Migrated from gitlab.com)

Security review — MR !521

Commit reviewed: 0c766d5f6ee099a7e94421ea97e1b141f18305a7
Scope: Indexer ingest log canonical sorting (sort_rpc_logs_canonical), PodiumTimerArmed decode/persist (idx_arena_podium_timer_armed), reorg rollback registration, schema 2.20.0 migration, Anvil verify script update.

Outcome: FINDINGS: 0 medium+

Summary: No new medium-or-higher vulnerabilities identified on this diff.

Reviewed attack paths:

  • SQL injection — persist.rs insert uses sqlx parameterized binds ($1…$6); no string concatenation from log fields.
  • Untrusted log injection — eth_getLogs is address-filtered to registry contracts; decode_rpc_log requires block_hash / tx_hash / log_index; PodiumTimerArmed is emitted only from TimeArena._armPodiumTimer (internal, idempotent arm). Spoofed rows require a malicious RPC or chain reorg (pre-existing indexer trust model).
  • Ordering / last_buy_epoch integrity — This MR closes the misordered-RPC mis-tagging risk (#278) by sorting per-block logs on (block_number, transaction_index, log_index) before the decode/persist loop. No new ordering bypass introduced.
  • Authn/authz / HTTP exposure — No new API routes; new table is ingest-only history (not served over HTTP in this MR).
  • Reorg safety — idx_arena_podium_timer_armed added to ARENA_INDEX_TABLES.
  • Shell / SSRF / XSS — Verify script changes are local smoke (curl to indexer status, fixed SQL); no production attack surface.

Prior security-review threads: none on this MR.

Inline threads: none (no findings to anchor).

## Security review — MR !521 **Commit reviewed:** `0c766d5f6ee099a7e94421ea97e1b141f18305a7` **Scope:** Indexer ingest log canonical sorting (`sort_rpc_logs_canonical`), `PodiumTimerArmed` decode/persist (`idx_arena_podium_timer_armed`), reorg rollback registration, schema 2.20.0 migration, Anvil verify script update. **Outcome:** `FINDINGS: 0` medium+ **Summary:** No new medium-or-higher vulnerabilities identified on this diff. Reviewed attack paths: - **SQL injection** — `persist.rs` insert uses sqlx parameterized binds (`$1`…`$6`); no string concatenation from log fields. - **Untrusted log injection** — `eth_getLogs` is address-filtered to registry contracts; `decode_rpc_log` requires `block_hash` / `tx_hash` / `log_index`; `PodiumTimerArmed` is emitted only from `TimeArena._armPodiumTimer` (internal, idempotent arm). Spoofed rows require a malicious RPC or chain reorg (pre-existing indexer trust model). - **Ordering / `last_buy_epoch` integrity** — This MR **closes** the misordered-RPC mis-tagging risk ([#278](https://gitlab.com/PlasticDigits/yieldomega/-/issues/278)) by sorting per-block logs on `(block_number, transaction_index, log_index)` before the decode/persist loop. No new ordering bypass introduced. - **Authn/authz / HTTP exposure** — No new API routes; new table is ingest-only history (not served over HTTP in this MR). - **Reorg safety** — `idx_arena_podium_timer_armed` added to `ARENA_INDEX_TABLES`. - **Shell / SSRF / XSS** — Verify script changes are local smoke (`curl` to indexer status, fixed SQL); no production attack surface. Prior security-review threads: none on this MR. **Inline threads:** none (no findings to anchor).
PlasticDigits commented 2026-06-25 22:23:46 +00:00 (Migrated from gitlab.com)

mentioned in commit fd6da03571

mentioned in commit fd6da035719d8096b21c7c91b8032f2f7dea2c69
PlasticDigits (Migrated from gitlab.com) merged commit fd6da03571 into main 2026-06-25 22:23:46 +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/yieldomega!521
No description provided.