Requested oracle versions, which have expired, must return this oracle version as invalid, but they return it as a normal version with previous version's price instead
Each market action requests a new oracle version which must be commited by the keepers. However, if keepers are unable to commit requested version's price (for example, no price is available at the time interval, network or keepers are down), then after a certain timeout this oracle version will be commited as invalid, using the previous valid version's price.\\nThe issue is that when this expired oracle version is used by the market (using oracle.at), the version returned will be valid (valid = true), because oracle returns version as invalid only if `price = 0`, but the `commit` function sets the previous version's price for these, thus it's not 0.\\nThis leads to market using invalid versions as if they're valid, keeping the orders (instead of invalidating them), which is a broken core functionality and a security risk for the protocol.\\nWhen requested oracle version is commited, but is expired (commited after a certain timeout), the price of the previous valid version is set to this expired oracle version:\\n```\\nfunction _commitRequested(OracleVersion memory version) private returns (bool) {\\n    if (block.timestamp <= (next() + timeout)) {\\n        if (!version.valid) revert KeeperOracleInvalidPriceError();\\n        _prices[version.timestamp] = version.price;\\n    } else {\\n        // @audit previous valid version's price is set for expired version\\n        _prices[version.timestamp] = _prices[_global.latestVersion]; \\n    }\\n    _global.latestIndex++;\\n    return true;\\n}\\n```\\n\\nLater, `Market._processOrderGlobal` reads the oracle version using the `oracle.at`, invalidating the order if the version is invalid:\\n```\\nfunction _processOrderGlobal(\\n    Context memory context,\\n    SettlementContext memory settlementContext,\\n    uint256 newOrderId,\\n    Order memory newOrder\\n) private {\\n    OracleVersion memory oracleVersion = oracle.at(newOrder.timestamp);\\n\\n    context.pending.global.sub(newOrder);\\n    if (!oracleVersion.valid) newOrder.invalidate();\\n```\\n\\nHowever, expired oracle version will return `valid = true`, because this flag is only set to `false` if price = 0:\\n```\\nfunction at(uint256 timestamp) public view returns (OracleVersion memory oracleVersion) {\\n    (oracleVersion.timestamp, oracleVersion.price) = (timestamp, _prices[timestamp]);\\n    oracleVersion.valid = !oracleVersion.price.isZero(); // @audit <<< valid = false only if price = 0\\n}\\n```\\n\\nThis means that `_processOrderGlobal` will treat this expired oracle version as valid and won't invalidate the order.
Add validity map along with the price map to `KeeperOracle` when recording commited price.
Market uses invalid (expired) oracle versions as if they're valid, keeping the orders (instead of invalidating them), which is a broken core functionality and a security risk for the protocol.
```\\nfunction _commitRequested(OracleVersion memory version) private returns (bool) {\\n    if (block.timestamp <= (next() + timeout)) {\\n        if (!version.valid) revert KeeperOracleInvalidPriceError();\\n        _prices[version.timestamp] = version.price;\\n    } else {\\n        // @audit previous valid version's price is set for expired version\\n        _prices[version.timestamp] = _prices[_global.latestVersion]; \\n    }\\n    _global.latestIndex++;\\n    return true;\\n}\\n```\\n