feat(security): C-2 remaining — emergency rate reset + spot divergence alerter + operator trip #27

Closed
opened 2026-08-08 06:01:28 +00:00 by PlasticDigits · 4 comments
PlasticDigits commented 2026-08-08 06:01:28 +00:00 (Migrated from gitlab.com)

Summary

Finish the remaining C-2 recommendations from audits/INTERNAL_KIMIK3_1786162831.md that were explicitly out of scope for #22 / MR !40 (circuit-breaker pause only):

  1. Governance emergency rate reset / monotonic bypass with strict guardrails.
  2. Off-chain spot-vs-exchangeRateStored divergence alerter.
  3. Optional oracle-service operator trip of the circuit breaker on detected decrease (governance-only unpause) — called out as follow-up in #22 verification notes.

Audit ref: C-2 recommendations #2–#4 (pause/#1 shipped in #22)
Depends on: #22 pause fail-closed (merged !40)
Related: #23 liveness confirm; #24 poll/silence; H-2 governance key risk


Current codebase

Component Path Behavior today
Oracle pause → window halt ust1-oracle State.paused + window ensure_oracle_usable Shipped (#22) — deposit/withdraw revert with OraclePaused immediately
Monotonic policy ust1-common oracle_policy.rs Hard-rejects rate decreases (MonotonicViolation) — no emergency bypass
Oracle UpdateRate contracts/ust1-oracle Operator-only; blocked when paused; no governance “set rate” / reset path
Oracle service on decrease oracle-service/src/main.rs Policy reject → skip broadcast; logs; does not call SetPaused
Spot / secondary price oracle-service None — only Venus exchangeRateStored via BSC consensus
Docs docs/DEPLOYMENT.md, skills/oracle-circuit-breaker Explicitly list emergency reset / operator auto-trip / spot alerter as out of scope

Gap: Pause stops the bleed once a human trips it, but (a) there is still no way to mark the rate down after a real vFDUSD collapse without a new governance path, (b) ops get no automated secondary-market divergence signal, and (c) the oracle bot that first sees a decrease cannot trip the on-chain breaker.


Why this is needed

  1. After Venus/FDUSD/bridge collapse, monotonicity correctly blocks marking down — without a guarded reset, markets stay paused forever or reopen at a toxic stale rate.
  2. Spot divergence is the early-warning signal that exchangeRateStored is no longer an economic price; pause alone is reactive and human-gated.
  3. #22 verification noted operator auto-trip remains out of scope; the service already detects decreases and only skips — extending to a narrow pause trigger closes the detection→halt loop.
  4. Completing C-2 is required before treating the collapse drain class as fully mitigated (pause is necessary but not sufficient).

Constraints / guardrails

  1. Do not weaken normal-path monotonicity, daily cap, throttle, or window rolling limits.
  2. Emergency rate reset must be governance-only, clearly evented, and rate-limited (e.g. once per N days / timelock / requires oracle paused first — pick and document). Prefer “set absolute rate while paused” over silent bypass of daily cap without audit trail.
  3. Operator (or service) may trip pause on detected decrease / divergence; unpause remains governance-only (no permanent DoS by compromised operator).
  4. Spot alerter is fail-noisy, not fail-open for swaps: alerting must not invent on-chain rates; optional auto-trip pause is allowed only via the same ACL as (3).
  5. Secondary price source must be HTTPS-only, allowlisted, timeout-bounded; redact credentialed URLs (H-6).
  6. No change to InstantWithdrawCw20 wire format or deposit math in this issue (H-1 is separate).
  7. Migrate-friendly: additive execute msgs / config; multitest + migrate tests if storage changes.
  8. Document economic assumption still holds until reset: UST1 safety ⊆ Venus FDUSD + bridge + monotonic tradeoff.

Relevant files

  • contracts/ust1-oracle/src/{msg,contract,state,error,multitest}.rs — emergency reset / trip msgs + ACL
  • smartcontracts-terraclassic/packages/ust1-common/src/oracle_policy.rs — gated bypass helper if shared
  • contracts/ust1-window/src/contract.rs — ensure still fail-closed while paused; no special casing for reset beyond reading new rate when unpaused
  • oracle-service/src/{main,bsc,config,liveness}.rs — decrease → trip; spot feed + divergence alert
  • docs/DEPLOYMENT.md — emergency reset runbook + alerter env
  • skills/oracle-circuit-breaker/SKILL.md — extend or add sibling skill
  • Integration / multitest suites

  1. On-chain: ExecuteMsg::EmergencySetRate { rate } (name flexible) — governance-only; require paused == true; set rate + last_update_sec; emit rich attributes; enforce cooldown / one-shot latch as designed.
  2. On-chain (optional narrow): TripCircuitBreaker {} callable by operator or governance to set paused=true; unpause stays governance SetPaused { false }.
  3. Oracle service: on MonotonicViolation / configured decrease: optionally broadcast trip (if ACL exists); always emit structured alert event.
  4. Spot alerter: poll allowlisted secondary price (document source choice); if |spot - venus_implied| > threshold, log ORACLE_SPOT_DIVERGENCE and optionally trip pause; never treat spot as UpdateRate input.
  5. Update DEPLOYMENT: detect → trip → investigate → emergency set rate → unpause ordering.

Acceptance criteria

  • Governance can set an emergency rate under documented guardrails while oracle is paused; unauthorized callers cannot.
  • Normal UpdateRate path still rejects decreases when not in emergency path.
  • Operator/service can trip pause on decrease (if implemented) but cannot unpause; governance can.
  • Spot divergence beyond threshold produces a structured alert (and optional auto-trip); swaps still governed by on-chain pause/staleness.
  • Window continues to fail closed while paused; after reset + unpause + fresh rate, deposit/withdraw work at new rate.
  • Docs/skills/runbook updated; tests cover ACL + cooldown + happy/unhappy paths.

Test plan (all paths)

  1. Emergency set while paused: governance sets rate → State reflects new rate; window still halted until unpause.
  2. Emergency set while unpaused: rejected (or documented alternate — prefer require paused).
  3. Unauthorized emergency set / trip / unpause: Unauthorized.
  4. Operator trip → governance unpause: markets resume only after governance.
  5. Normal UpdateRate decrease: still MonotonicViolation (no bypass).
  6. Cooldown: second emergency set inside window → reject.
  7. Service decrease: mock Venus rate down → alert (+ trip tx if enabled); no successful UpdateRate; liveness not falsely advanced (#23 semantics).
  8. Spot divergence: mock spot vs Venus → alert; below threshold → quiet.
  9. Window: paused+fresh age → OraclePaused; after reset+unpause → swaps at new rate; stale still OracleStale.

Test plan (attack / hack / abuse vectors)

  1. Compromised operator DoS: spam trip → markets frozen; unpause requires governance; test operator cannot unpause / cannot EmergencySetRate.
  2. Malicious governance reset up: emergency path could set toxic high rate — document as trusted-gov; optional absolute bounds / max jump on emergency set.
  3. Spot feed manipulation: alerter must not write rate on-chain; auto-trip only pauses (fail-closed), never marks up from spot.
  4. Alert fatigue / grief via flapping spot: threshold + hysteresis / cooldown on auto-trip.
  5. Bypass pause via direct UpdateRate after reset: operator UpdateRate still subject to mono/throttle/daily unless explicitly designed; emergency path must not leave a standing “mono off” flag.
  6. Collapse drain race: decrease detected → trip before rolling limit drained; integration-style sequence test.

Verification criteria

  • Multitest/integration prove: trip → halt → emergency rate → unpause → resume; ACL matrix green.
  • Oracle-service tests (wiremock) for decrease alert/trip and spot divergence alert.
  • DEPLOYMENT runbook lists ordered emergency steps; C-2 remaining items no longer listed as out of scope.
  • #22 follow-ups (operator auto-trip; rate reset; spot alerter) closed by this issue when merged.
## Summary Finish the **remaining C-2** recommendations from [`audits/INTERNAL_KIMIK3_1786162831.md`](../audits/INTERNAL_KIMIK3_1786162831.md) that were explicitly out of scope for [#22](https://gitlab.com/PlasticDigits/ust1-window/-/issues/22) / MR !40 (circuit-breaker pause only): 1. Governance **emergency rate reset** / monotonic bypass with strict guardrails. 2. Off-chain **spot-vs-`exchangeRateStored` divergence alerter**. 3. Optional oracle-service **operator trip** of the circuit breaker on detected decrease (governance-only unpause) — called out as follow-up in #22 verification notes. **Audit ref:** C-2 recommendations #2–#4 (pause/#1 shipped in #22) **Depends on:** [#22](https://gitlab.com/PlasticDigits/ust1-window/-/issues/22) pause fail-closed (merged !40) **Related:** [#23](https://gitlab.com/PlasticDigits/ust1-window/-/issues/23) liveness confirm; [#24](https://gitlab.com/PlasticDigits/ust1-window/-/issues/24) poll/silence; H-2 governance key risk --- ## Current codebase | Component | Path | Behavior today | |-----------|------|----------------| | Oracle pause → window halt | `ust1-oracle` `State.paused` + window `ensure_oracle_usable` | **Shipped (#22)** — deposit/withdraw revert with `OraclePaused` immediately | | Monotonic policy | `ust1-common` `oracle_policy.rs` | Hard-rejects rate decreases (`MonotonicViolation`) — no emergency bypass | | Oracle `UpdateRate` | `contracts/ust1-oracle` | Operator-only; blocked when paused; no governance “set rate” / reset path | | Oracle service on decrease | `oracle-service/src/main.rs` | Policy reject → **skip broadcast**; logs; **does not** call `SetPaused` | | Spot / secondary price | oracle-service | **None** — only Venus `exchangeRateStored` via BSC consensus | | Docs | `docs/DEPLOYMENT.md`, `skills/oracle-circuit-breaker` | Explicitly list emergency reset / operator auto-trip / spot alerter as out of scope | **Gap:** Pause stops the bleed once a human trips it, but (a) there is still no way to mark the rate down after a real vFDUSD collapse without a new governance path, (b) ops get no automated secondary-market divergence signal, and (c) the oracle bot that first sees a decrease cannot trip the on-chain breaker. --- ## Why this is needed 1. After Venus/FDUSD/bridge collapse, monotonicity correctly blocks marking down — without a guarded reset, markets stay paused forever or reopen at a toxic stale rate. 2. Spot divergence is the early-warning signal that `exchangeRateStored` is no longer an economic price; pause alone is reactive and human-gated. 3. #22 verification noted operator auto-trip remains out of scope; the service already detects decreases and only skips — extending to a narrow pause trigger closes the detection→halt loop. 4. Completing C-2 is required before treating the collapse drain class as fully mitigated (pause is necessary but not sufficient). --- ## Constraints / guardrails 1. **Do not weaken** normal-path monotonicity, daily cap, throttle, or window rolling limits. 2. Emergency rate reset must be **governance-only**, clearly evented, and rate-limited (e.g. once per N days / timelock / requires oracle paused first — pick and document). Prefer “set absolute rate while paused” over silent bypass of daily cap without audit trail. 3. Operator (or service) may **trip pause** on detected decrease / divergence; **unpause remains governance-only** (no permanent DoS by compromised operator). 4. Spot alerter is **fail-noisy, not fail-open for swaps**: alerting must not invent on-chain rates; optional auto-trip pause is allowed only via the same ACL as (3). 5. Secondary price source must be HTTPS-only, allowlisted, timeout-bounded; redact credentialed URLs (H-6). 6. No change to InstantWithdrawCw20 wire format or deposit math in this issue (H-1 is separate). 7. Migrate-friendly: additive execute msgs / config; multitest + migrate tests if storage changes. 8. Document economic assumption still holds until reset: UST1 safety ⊆ Venus FDUSD + bridge + monotonic tradeoff. --- ## Relevant files - `contracts/ust1-oracle/src/{msg,contract,state,error,multitest}.rs` — emergency reset / trip msgs + ACL - `smartcontracts-terraclassic/packages/ust1-common/src/oracle_policy.rs` — gated bypass helper if shared - `contracts/ust1-window/src/contract.rs` — ensure still fail-closed while paused; no special casing for reset beyond reading new rate when unpaused - `oracle-service/src/{main,bsc,config,liveness}.rs` — decrease → trip; spot feed + divergence alert - `docs/DEPLOYMENT.md` — emergency reset runbook + alerter env - `skills/oracle-circuit-breaker/SKILL.md` — extend or add sibling skill - Integration / multitest suites --- ## Recommended direction 1. **On-chain:** `ExecuteMsg::EmergencySetRate { rate }` (name flexible) — governance-only; require `paused == true`; set `rate` + `last_update_sec`; emit rich attributes; enforce cooldown / one-shot latch as designed. 2. **On-chain (optional narrow):** `TripCircuitBreaker {}` callable by operator **or** governance to set `paused=true`; unpause stays governance `SetPaused { false }`. 3. **Oracle service:** on `MonotonicViolation` / configured decrease: optionally broadcast trip (if ACL exists); always emit structured alert event. 4. **Spot alerter:** poll allowlisted secondary price (document source choice); if `|spot - venus_implied| > threshold`, log `ORACLE_SPOT_DIVERGENCE` and optionally trip pause; never treat spot as `UpdateRate` input. 5. Update DEPLOYMENT: detect → trip → investigate → emergency set rate → unpause ordering. --- ## Acceptance criteria - [ ] Governance can set an emergency rate under documented guardrails while oracle is paused; unauthorized callers cannot. - [ ] Normal `UpdateRate` path still rejects decreases when not in emergency path. - [ ] Operator/service can trip pause on decrease (if implemented) but **cannot** unpause; governance can. - [ ] Spot divergence beyond threshold produces a structured alert (and optional auto-trip); swaps still governed by on-chain pause/staleness. - [ ] Window continues to fail closed while paused; after reset + unpause + fresh rate, deposit/withdraw work at new rate. - [ ] Docs/skills/runbook updated; tests cover ACL + cooldown + happy/unhappy paths. --- ## Test plan (all paths) 1. **Emergency set while paused:** governance sets rate → State reflects new rate; window still halted until unpause. 2. **Emergency set while unpaused:** rejected (or documented alternate — prefer require paused). 3. **Unauthorized emergency set / trip / unpause:** Unauthorized. 4. **Operator trip → governance unpause:** markets resume only after governance. 5. **Normal UpdateRate decrease:** still MonotonicViolation (no bypass). 6. **Cooldown:** second emergency set inside window → reject. 7. **Service decrease:** mock Venus rate down → alert (+ trip tx if enabled); no successful UpdateRate; liveness not falsely advanced (#23 semantics). 8. **Spot divergence:** mock spot vs Venus → alert; below threshold → quiet. 9. **Window:** paused+fresh age → OraclePaused; after reset+unpause → swaps at new rate; stale still OracleStale. --- ## Test plan (attack / hack / abuse vectors) 1. **Compromised operator DoS:** spam trip → markets frozen; unpause requires governance; test operator cannot unpause / cannot EmergencySetRate. 2. **Malicious governance reset up:** emergency path could set toxic high rate — document as trusted-gov; optional absolute bounds / max jump on emergency set. 3. **Spot feed manipulation:** alerter must not write rate on-chain; auto-trip only pauses (fail-closed), never marks up from spot. 4. **Alert fatigue / grief via flapping spot:** threshold + hysteresis / cooldown on auto-trip. 5. **Bypass pause via direct UpdateRate after reset:** operator UpdateRate still subject to mono/throttle/daily unless explicitly designed; emergency path must not leave a standing “mono off” flag. 6. **Collapse drain race:** decrease detected → trip before rolling limit drained; integration-style sequence test. --- ## Verification criteria - Multitest/integration prove: trip → halt → emergency rate → unpause → resume; ACL matrix green. - Oracle-service tests (wiremock) for decrease alert/trip and spot divergence alert. - DEPLOYMENT runbook lists ordered emergency steps; C-2 remaining items no longer listed as out of scope. - #22 follow-ups (operator auto-trip; rate reset; spot alerter) closed by this issue when merged.
PlasticDigits commented 2026-08-08 06:01:29 +00:00 (Migrated from gitlab.com)

marked as related to #22

marked as related to #22
PlasticDigits commented 2026-08-08 06:01:53 +00:00 (Migrated from gitlab.com)

mentioned in issue #28

mentioned in issue #28
PlasticDigits (Migrated from gitlab.com) closed this issue 2026-08-08 09:48:07 +00:00
PlasticDigits commented 2026-08-08 09:48:08 +00:00 (Migrated from gitlab.com)

Rejected - in the case of a vFDUSD exploit, the system needs to be paused, evaluated, and upgraded, so the existing pause system is sufficent.

Rejected - in the case of a vFDUSD exploit, the system needs to be paused, evaluated, and upgraded, so the existing pause system is sufficent.
PlasticDigits commented 2026-08-08 09:58:36 +00:00 (Migrated from gitlab.com)

mentioned in merge request !45

mentioned in merge request !45
Sign in to join this conversation.
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/ust1-window#27
No description provided.