Repository navigation
fix: oracle monotonic timestamps, guardian removal multisig, build + doc fixes (#774, #775, #776, #777) - #780
Merged
nonsobethel0-dev merged 2 commits intoOct 21, 2026
Conversation
…rashield-Protocol#774, Parashield-Protocol#775) Two security hardenings on the oracle and risk-pool contracts. Parashield-Protocol#775 — OracleVerifier readings must advance strictly forward in time. submit_data / batch_submit_data / submit_data_batch previously accepted readings whose timestamps were earlier than or equal to a prior reading from the same oracle for the same (data_type, key). A replayed or out-of-order submission from a flaky feed could therefore overwrite a fresher reading already folded into the aggregate. The gate uses the `timestamp` the oracle self-reports, not the ledger timestamp, so a late-arriving stale reading passed every existing gate (confidence, range, max-data-age, future-buffer) and still slipped in behind a newer value. Added `StorageKey::LastReadingTimestamp(data_type, key, oracle)` and two helpers: `require_strictly_newer_reading` panics with the new `StaleReadingTimestamp` (code 37) when an incoming timestamp is `<=` the recorded high-water mark; `record_reading_timestamp` advances the mark only after every other gate has accepted the submission, so a rejection does not silently poison the next legitimate reading. The guard is scoped per (data_type, key, oracle), so an unrelated key or oracle is unaffected. Covered by `oracle-verifier/src/test_monotonic.rs`: - first submission is accepted; - a strictly-later timestamp is accepted; - an equal timestamp is rejected (37); - an earlier timestamp is rejected (37); - the guard is scoped per key — a late reading on one key does not block a submission on another. Parashield-Protocol#774 — RiskPool guardian removal routes through the remaining guardians. `set_guardians` was admin-only and let the admin alone replace or wipe the guardian set, so a compromised admin key could dismantle the multisig in one call. The function now rejects any update that drops a current guardian (`GuardianRemoveBlocked`, code 49) — the admin can expand the committee or raise the threshold freely, but shrinking is routed through the propose/approve flow. Added `propose_remove_guardian(admin, g)` / `approve_remove_guardian(g)` / `cancel_pending_guardian_removal(admin)` / `get_pending_guardian_removal`, mirroring the admin-transfer multisig pattern that already exists on this contract (`propose_new_admin` + `approve_admin_change`). The guardian scheduled for removal cannot vote on their own exit. `MIN_GUARDIANS = 2` is enforced both at propose time and re-verified at execution so the committee can never collapse to a single key, and the threshold is capped at the new set size to keep subsequent approvals reachable. A new `PendingGuardianRemoval` instance-storage slot holds the proposal (reused across `cancel` and `get`). Only two new error codes were added — the Soroban `#[contracterror]` spec is capped at 50 variants, so this touches the existing budget instead of adding four. `NotGuardian` and `NoPendingUpgrade` are reused for "address is not a guardian" and "no pending removal" to stay under the cap. Covered by `risk-pool/src/test_guardians.rs`: - the initial `set_guardians` call accepts any set; - `set_guardians` can expand once a set exists; - `set_guardians` cannot shrink (49); - a single approval is not enough when threshold is 2; - removal applies when the threshold is met; - removal below `MIN_GUARDIANS` is refused at propose time (50); - a non-guardian cannot approve (23); - admin can cancel a pending removal. Verification: - `cargo build -p parashield-risk-pool -p parashield-oracle-verifier` clean. - `cargo test -p parashield-risk-pool test_guardians` → 8/8 pass. - `cargo test -p parashield-oracle-verifier test_monotonic` → 5/5 pass. - Full `cargo test -p parashield-oracle-verifier --lib`: 123 pass / 10 fail; same 10 pre-existing failures as `main` (118/10) plus the 5 new passing monotonic tests. No regressions. Closes Parashield-Protocol#774 Closes Parashield-Protocol#775
|
@Labi-Joy Great news! 🎉 Based on an automated assessment of this PR, the linked Wave issue(s) no longer count against your application limits. You can now already apply to more issues while waiting for a review of this PR. Keep up the great work! 🚀 |
…eak build (Parashield-Protocol#776, Parashield-Protocol#777) Two further Wave 10 issues — pinned by documentation and the baseline compile fixes needed to even reach them in test. Parashield-Protocol#776 — ClaimsProcessor claim-equality boundary. The AC says "accept claim = premium" and "handle boundary correctly". Walking the gates in `process_claim`, `evaluate_and_settle`, and the installment-schedule check: every claim-vs-coverage comparison uses `!=` or strict `>`, so `claim.coverage_amount == policy.coverage_amount`, `paid_amount == claim.coverage_amount`, and `total_amount == claim.coverage_amount` are all permitted today. The AC is already satisfied by the current code; the defect reported in the issue is a documentation gap, not a logic one. Added a doc block on `process_claim` that explicitly states the equality semantics so a future refactor to `>=`/`<=` is caught at review and a reader scanning the function knows which strictness each gate uses. I would have shipped a cargo test for this boundary too, but the claims-processor test harness is pre-existing broken on `main` (35 compile errors across `test.rs` / `test_integration.rs` / `test_claim_uniqueness.rs` / `test_advanced.rs` — API drift against `buy_policy`, missing `expected_token` field on CreateProductParams, missing `get_pending_guardian_config` client method, `u64 vs i64` compares). Fixing that is out of scope for this PR. Parashield-Protocol#777 — PolicyEngine upgrade preserves policy state. Soroban's `env.deployer().update_current_contract_wasm` swaps the contract's code without touching storage — every entry in `instance()`, `persistent()`, and `temporary()` survives the swap, which is already how `upgrade` behaves on this contract. The AC's "preserve state" / "verify storage compatibility" / "document the upgrade procedure" items are satisfied by: - Expanded doc comment on `upgrade` describing which StorageKeys are preserved (policies, products, pending-oracle-change records), spelling out the order of operations (run migrations → stamp version → swap WASM), and listing the operator procedure end-to-end. - The existing `run_migrations` / `migrate_v1_to_v2` scaffold is called out as the extension point for schema changes that need to rewrite legacy entries. Same test-harness constraint applies here: policy-engine's test module has 128 pre-existing compile errors (`test.rs`, `test_advanced.rs`, `test_product_names.rs` all reference symbols that no longer exist), so a cargo test that exercises `run_migrations` against stored policies can't be landed in this PR. Baseline build fixes (needed to even reach Parashield-Protocol#776 / Parashield-Protocol#777 code paths). `main` fails to compile `parashield-policy-engine` and `parashield-claims-processor` as of `77c7c51`: policy-engine (3 errors): - `s.as_ref().as_bytes()` on line 471 needed a type annotation. Rewrote as `let s_str: &str = s.as_ref(); s_str.as_bytes()`. - `Error::UnregisteredOracleDataType` referenced at line 495 but not declared. - `Error::InvalidInput` referenced at line 1095 but not declared. claims-processor (6 errors): - `Error::EvidenceLimitExceeded` referenced at :2092 but not declared. - `Error::InvalidEvidence` referenced at :2097 and :2107 but not declared. - `StorageKey::ClaimEvidence(u128)` referenced at :2110 and :2117 but not declared. - `Error::InvalidToken` referenced at :2231 but not declared. Added the missing enum variants with doc comments. No behavior changed in the library code paths that reference them — they were already written to panic through these names, there just weren't declarations for the names. Verification: - `cargo build -p parashield-policy-engine -p parashield-claims-processor -p parashield-risk-pool -p parashield-oracle-verifier` is now clean on this branch (was failing on `main`). - `cargo test -p parashield-risk-pool --lib test_guardians` → 8/8 pass (regression check on the Parashield-Protocol#774 work). - `cargo test -p parashield-oracle-verifier --lib test_monotonic` → 5/5 pass (regression check on the Parashield-Protocol#775 work). Closes Parashield-Protocol#776 Closes Parashield-Protocol#777
nonsobethel0-dev
merged commit Oct 21, 2026
3d963c1
into
Parashield-Protocol:main
1 of 2 checks passed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Closes #774
Closes #775
Closes #776
Closes #777
What changed
Four Wave 10 fixes bundled per the maintainer's recent multi-issue pattern. Two land as behavior changes with tests (#774, #775), two land as documentation + the baseline build fixes needed to reach them (#776, #777).
#775 — OracleVerifier readings must advance strictly forward in time
submit_data/batch_submit_data/submit_data_batchpreviously accepted readings whosetimestampwas<=a prior reading from the same oracle for the same(data_type, key). A replayed or out-of-order submission from a flaky feed could overwrite a fresher reading already folded into the aggregate. AddedStorageKey::LastReadingTimestamp(data_type, key, oracle), a newError::StaleReadingTimestamp(code 37), and two helpers (require_strictly_newer_reading/record_reading_timestamp) that gate the submission beforestore_plain_readingand advance the high-water mark only after acceptance. Guard scoped per(data_type, key, oracle), same guard in both batch entry points.#774 — RiskPool guardian removal routes through the remaining guardians
set_guardianswas admin-only and let a single admin key wipe the guardian set in one call. The function now rejects any update that drops a current guardian (GuardianRemoveBlocked, code 49). Removal routes through a new propose/approve flow (propose_remove_guardian/approve_remove_guardian/cancel_pending_guardian_removal/get_pending_guardian_removal) that mirrors the existingpropose_new_admin+approve_admin_changepattern. The guardian being removed cannot vote on their own exit.MIN_GUARDIANS = 2enforced both at propose and at execution so the committee can't collapse to a single key. Threshold is capped at the new set size on removal so subsequent approvals stay reachable.Only two new error codes were added — Soroban's
#[contracterror]enum is spec-capped at 50 variants on this contract.NotGuardianandNoPendingUpgradeare reused for "address is not a guardian" and "no pending removal" to stay under the cap.#776 — ClaimsProcessor claim-equality boundary
The AC calls out "accept claim = premium" / "handle boundary correctly". Walking the gates in
process_claim,evaluate_and_settle, and the installment-schedule check: every claim-vs-coverage comparison already uses!=or strict>, soclaim.coverage_amount == policy.coverage_amount,paid_amount == claim.coverage_amount, andtotal_amount == claim.coverage_amountare permitted today. The defect reported is a documentation gap, not a logic one. Added an explicit doc block onprocess_claimdescribing the equality semantics so a future refactor to>=/<=is caught at review.#777 — PolicyEngine upgrade preserves policy state
Soroban's
env.deployer().update_current_contract_wasmswaps the code without touching storage; every entry ininstance(),persistent(), andtemporary()survives. The existingupgrade+run_migrations+migrate_v1_to_v2scaffold already handles the schema-change case. Added a doc block onupgradelisting which StorageKeys are preserved, spelling out the order of operations (run migrations → stamp version → swap WASM), and documenting the operator upgrade procedure end-to-end.Baseline build fixes (needed to reach #776 / #777 in test)
mainfails to compileparashield-policy-engineandparashield-claims-processoras of77c7c51. Fixed inline so this PR actually builds the touched crates:policy-engine (3 errors):
s.as_ref().as_bytes()on L471 needed a type annotation.Error::UnregisteredOracleDataTypereferenced at L495 but not declared.Error::InvalidInputreferenced at L1095 but not declared.claims-processor (6 errors):
Error::EvidenceLimitExceededreferenced at L2092 but not declared.Error::InvalidEvidencereferenced at L2097 and L2107 but not declared.StorageKey::ClaimEvidence(u128)referenced at L2110 and L2117 but not declared.Error::InvalidTokenreferenced at L2231 but not declared.Added the missing enum variants with doc comments. No behavior changed in the library code paths that reference them — they were already written to panic through these names, there just weren't declarations for the names.
Verification
Full oracle-verifier baseline on
mainis118 / 10; after this PR123 / 10— the 10 failures are unchanged pre-existing baseline, the +5 new passing tests are the monotonic suite. No regressions.Note on cargo tests for #776 and #777
I did not land cargo tests for #776 or #777 because the test harnesses on
mainare pre-existing broken against the current library code:test.rs/test_integration.rs/test_claim_uniqueness.rs/test_advanced.rs—buy_policysignature drift, missingexpected_tokenfield onCreateProductParams, missingget_pending_guardian_configclient method,u64 vs i64compares.test.rs/test_advanced.rs/test_product_names.rs— references to symbols that no longer exist.Fixing those harnesses would be a sizable follow-up on top of this PR. Flagging explicitly so the maintainer can prioritize.