# Oracle Security Module

## 1. Summary

The `OSM` (Oracle Security Module) ensures that new price values propagated from medianizers are not taken up by the system until a specified delay has passed. Values are updated using `updateResult` and read using `read` and `getResultWithValidity`. Each collateral type will have its own unique `OSM`. Collateral type is defined as a specific asset type (whereas `ETH-A` and `ETH-B` refer to the same asset and thus have the same `OSM`).

## 2. Contract Variables & Functions <a href="#id-2-contract-details" id="id-2-contract-details"></a>

**Variables**

* `authorizedAccounts[usr: address]` - addresses allowed to call authed functions.
* `stopped` - flag that disables price feed updates if non-zero
* `priceSource` - address of medianizer that the OSM will read from
* `ONE_HOUR` - 3600 seconds
* `updateDelay` - time delay between `updateResult` calls; defaults to `ONE_HOUR`
* `lastUpdateTime` - time of last update (rounded down to nearest multiple of `updateDelay`)
* `currentFeed` - `Feed` struct that holds the current price value
* `nextFeed` - `Feed` struct that holds the next price value

**Modifiers**

* `isAuthorized` \*\*\*\* - checks whether an address is part of `authorizedAddresses` (and thus can call authed functions).

**Functions**

* `addAuthorization`/`removeAuthorization` - add or remove authorized users (via modifications to the `authorizedAccounts` mapping)
* `stop()`/`start()` - toggle whether the OSM price feed can be updated (by changing the value of `stopped`)
* `changePriceSource(address)` - change data source for prices (by setting `priceSource`)
* `changeDelay(uint16)` - change interval between price updates (by setting `updateDelay`)
* `restartValue()` - similar to `stop`, except it also sets `currentFeed`and `nextFeed` to a `Feed` struct with zero values
* `getResultWithValidity()` - returns the current feed value and a boolean indicating whether it is valid
* `getNextResultWithValidity()` - returns the next feed value (i.e. the one that will become the current value upon the next `updateResult()` call), and a boolean indicating whether it is valid
* `read()` - returns the current feed value; reverts if it was not set by some valid mechanism
* `updateResult()` - updates the current feed value and reads the next one

**Data Structures**

* `Feed` - struct used to store price feed data. Contains:
  * `value` - the feed value
  * `isValid` - whethet the feed value is valid

## 3. Walkthrough <a href="#id-3-key-mechanisms-and-concepts" id="id-3-key-mechanisms-and-concepts"></a>

In order for the `OSM` to work properly, an external actor must regularly call `updateResult()` to update the current price and read the next one. The contract stores the timestamp of the last `updateResult()` and will not allow another update until `block.timestamp` is at least `lastUpdateTime + updateDelay`. Values are read from the `priceSource`. In case of an oracle attack, governance can call `stop()` or`restartValue()`

## 4. OSM Variations

#### SelfFundedOSM

This contract pulls funds from the [StabilityFeeTreasury](https://github.com/reflexer-labs/geb/blob/master/src/single/StabilityFeeTreasury.sol) so it can reward addresses for calling`updateResult`.

**ExternallyFundedOSM**

This contract calls an [FSMWrapper](https://github.com/reflexer-labs/geb-fsm/blob/master/src/FSMWrapper.sol) in order to reward addresses that call `updateResult`.

## 5. Gotchas (Potential Sources of User Error)

N/A

## 6. Failure Modes (Bounds on Operating Conditions & External Risk Factors)

#### `updateCollateralPrice()` is not called promptly, allowing malicious prices to be swiftly uptaken

For several reasons, `updateCollateralPrice()` is always callable as soon as `block.timestamp / updateDelay` increments, regardless of when the last `updateCollateralPrice()` call occurred (because `lastUpdateTime` is rounded down to the nearest multiple of `updateDelay`). This means the contract does not actually guarantee that a time interval of at least `updateDelay` seconds has passed since the last `updateCollateralPrice()` call before the next one; rather this is only (approximately) guaranteed if the last `updateCollateralPrice()` call occurred shortly after the previous increase of `block.timestamp / updateDelay`. Thus, a malicious price value can be acknowledged by the system in a time potentially much less than `updateDelay`.

**This was a deliberate design decision. The arguments that favoured it, roughly speaking, are:**

* Providing a predictable time at which Prot holders should check for evidence of oracle attacks (in practice, `updateDelay` is 1 hour, so checks must be performed at the top of the hour)
* Allowing all OSMs to be reliably updated at the same time in a single transaction

The fact that `updateCollateralPrice` is public, and thus callable by anyone, helps mitigate concerns, though it does not eliminate them. For example, network congestion could prevent anyone from successfully calling `updateCollateralPrice()` for a period of time. If a Prot holder observes that `updateCollateralPrice` has not been promptly called, **the actions they can take include:**

1. Call `updateCollateralPrice()` themselves and decide if the next value is malicious or not
2. Call `stop()` or `restartValue()` (the former if only `nextFeed` is malicious; the latter if the malicious value is already in `currentFeed`)
3. Trigger emergency shutdown (if the integrity of the overall system has already been compromised or if it is believed the rogue oracle(s) cannot be fixed in a reasonable length of time)

In the future, the contract's logic may be tweaked to further mitigate this (e.g. by **only** allowing `updateCollateralPrice()` calls in a short time window each `updateDelay` period).

### Authorization Attacks and Misconfigurations

Various damaging actions can be taken by authorized individuals or contracts, either maliciously or accidentally:

* Revoking access of core contracts to the methods that read values, causing mayhem as prices fail to update
* Completely revoking all access to the contract
* Changing `src` to either a malicious contract or to something that lacks a `getResultWithValidity()` interface, causing transactions that `updateCollateralPrice()` the affected OSM to revert
* Calling disruptive functions like `stop` and `restartValue` inappropriately

The only solution to these issues is diligence and care regarding the `authorizedAccounts` of the OSM.


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://docs.reflexer.finance/system-contracts/oracle-module/fsm/oracle-security-module.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
