sbv2-solana/website/docs/queue/architecture.mdx

148 lines
10 KiB
Plaintext
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

---
sidebar_position: 1
slug: .
title: Architecture
---
import { Box, Typography, Grid } from "@mui/material";
import MarkdownImage from "/src/components/MarkdownImage";
import SwitchboardPermission from "/idl/types/SwitchboardPermission.md";
# Oracle Queue Architecture
An oracle queue is an independent realm of oracles, responsible for allocating oracle resources for update requests from data feeds, randomness, or buffer relayers. Oracle queue's act as an aggregator for on-chain consumers looking to publish data on-chain by specifying an upfront reward a requester is required to pay when a new update is requested by an oracle. Oracles act as an off-chain compute resource that can be utilized by on-chain programs needing a decentralized way to source data.
Each oracle queue is independent and maintain their own configurations, which dictates its degree of security. Queue's can require update requesters to be pre-approved to use a queues resources or allow any requester access to a queue. Queue's also specify a minimum stake oracles must maintain in their escrow wallet before joining a queue, which acts as a deposit to incentivize honest oracle behavior.
Oracle queue's currently support update requests from the following resource types:
| Resource Type | Description |
| ------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **Data Feeds** | Utilize a batch of oracles to resolve a data point from a variety of source and determine the final result as the median of oracle responses <br />_Can be a price feed, sport result, or any data point found on the internet_ |
| **Randomness** | Utilize an oracle to publish and verify a Verifiable Randomness Function on-chain.<br />_Can be used for fair NFT launches, decentralized lottery, or any random assignment_ |
| **Buffer Relayer** | Utilize an oracle to relay and publish a buffer on-chain<br />_Can be used by programs needing a way to quickly source data on-chain that may not need as many security guarantees as a price feed _ |
## Configuration
<Grid container spacing={2}>
<Grid item md={4} sm={12}>
<ul>
<li>
<b>Oracle Queue: </b>Contains the Oracle Queue's configuration
parameters that influence its security model.
</li>
<br />
<li>
<b>Oracle Queue Buffer: </b>Stores the current list of oracles actively
heartbeating on-chain.
</li>
<br />
<li>
<b>Oracle: </b>Off-chain resource used by a queue to fulfill update
request.
</li>
<br />
<li>
<b>Crank: </b>Optional, Crank Account that permits data feeds to join
and request periodic updates.
</li>
<br />
<li>
<b>Crank Buffer: </b>Stores the list of data feeds on a crank along with
their next allowed update time.
</li>
</ul>
</Grid>
<Grid item md={8} sm={12}>
<MarkdownImage
img="/img/queue/Oracle_Queue_Accounts.jpg"
sx={{
display: "flex",
}}
/>
</Grid>
</Grid>
<hr />
:::tip
See [/idl/accounts/OracleQueueAccountData](/idl/accounts/OracleQueueAccountData) for the full list of an OracleQueueAccount's configuration parameters.
:::
## Oracle Queue
When creating a queue, an OracleQueueBuffer account must also be initialized with a size of 8 Bytes + (32 Bytes × `queue.maxSize`), where `queue.maxSize` is the maximum number of oracles the queue can support. The OracleQueueBuffer account `queue.dataBuffer` stores a list of oracle public keys in a round robin fashion, using `queue.currIdx` to track its position on the queue for allocating resource update request. Once a buffer is full, oracles must be removed before new oracles can join the network. An oracle can be assigned to many update request simultaneously but must continuously heartbeat on-chain to signal readiness.
An oracle with **PermitOracleHeartbeat** permissions _MUST_ periodically heartbeat on the queue to signal readiness, which adds the oracle to the queue and allows it to be assigned resource update requests. Oracle positions are periodically swapped in the OracleQueueBuffer account to mitigate oracles being assigned the same update requests on each iteration of the queue.
The queue uses `queue.gcIdx` to track its garbage collection index. When an oracle heartbeats on-chain, it passes the oracle account at index `queue.gcIdx`. If the oracle account has failed to heartbeat before `queue.oracleTimeout`, it is removed from the queue until its next successful heartbeat and will no longer be assigned resource update requests.
## Access Control
Oracle queue resources, such as oracles, aggregators, VRF accounts, or buffer relayer accounts, _MUST_ have an associated [PermissionAccount](/idl/accounts/PermissionAccountData) initialized before interacting with a queue. Permissions are granted by `queue.authority`, which could be a DAO controlled account to allow network participants to vote on new entrants.
Oracles _MUST_ have **PermitOracleHeartbeat** permissions before heartbeating on a queue. This is to prevent a malicious actor from spinning up a plethora of oracles until it obtains the super majority, at which point it could misreport data feed results and cause honest oracles to be slashed.
See the table below for the minimum required permissions for a resource based on the queues settings:
| Queue Setting | False | True |
| ---------------------------- | ------------------------------------------------------------------------------------------------------------ | ----------------------------------------------------------------------------------------- |
| `unpermissionedFeedsEnabled` | Aggregators & Buffer Relayers _MUST_ have **PermitOracleQueueUsage** permissions before requesting an update | Aggregators & Buffer Relayers require no explicit permissions before requesting an update |
| `unpermissionedVrfEnabled` | VRF Accounts _MUST_ have **PermitVrfRequests** permissions before requesting an update | VRF Accounts require no explicit permissions before requesting an update |
| `enableBufferRelayers` | Buffer Relayers are _NOT_ permitted to request updates | Buffer Relayers are permitted to request updates |
<!--
TODO:
- consecutiveFeedFailureLimit
- consecutiveOracleFailureLimit
- feedProbationPeriod
-->
## Crank
A queue can choose to create one or many cranks. A crank is a scheduling mechanism that allows data feeds to request periodic updates. A crank can be turned by anyone, and if successful, the crank turner will be rewarded for jump starting the system.
A data feed is only permitted to join a crank if it has sufficient permissions (as detailed above) and the crank has available capacity. Data feeds on a crank are ordered by their next available update time with some level of jitter to mitigate oracles being assigned to the same update request upon each iteration of the queue, which makes them susceptible to a malicous oracle. The maximum update interval for a feed on a crank is based on its `aggregator.minUpdateDelaySeconds` and can be calculated by:
$I_{Max} (seconds) = I_{minUpdateDelaySeconds} + [15\quad\% \quad I_{minUpdateDelaySeconds}]$
## Economic Security
An oracle queue uses economic incentives to entice oracles to act honestly, which dictate a queue's security model.
### Stake
The queue's `queue.minStake` is the raw token amount in the token mints base unit (_Ex: lamports or satoshis_) required by an oracle to heartbeat on a queue. If an oracle's staking wallet falls below the minStake requirement, it is removed from the queue.
DeFi protocols with a significant Total Value Locked (TVL) should require oracles with a higher minimum stake to fulfill their update request. Oracles with a higher degree of _skin-in-the-game_ have a greater incentive to respond honestly.
### Reward
The queue's specified `queue.reward` is the number of tokens an oracle or crank turner receives for successfully completing an on-chain action. For a crank turner this is turning the crank and invoking a data feed update. For an oracle this is responding to an update request within the reliable margin from the accepted result.
Queues should reward oracles enough such that the economic incentive over the lifecycle of the feed exceeds the opportunity cost to attack a protocol consuming the feed.
### Slashing
A queue may set `queue.slashingEnabled` to true in order to dissuade oracles from responding to update request outside a set margin of error.
A queue's `queue.varianceToleranceMultiplier` determines how many standard deviations an oracle must respond within before being slashed and forfeiting a portion of their stake. [Defaults to 2 std deviations]
DeFi protocols with a significant TVL should require their feeds to be on a queue with slashing enabled.
## Governance
An oracle queue can be governed by its network participants to control the various queue configuration parameters, such as:
- `queue.minStake` - require a higher up-front cost for oracles to entice honest behavior
- `queue.reward` - control the oracle reward payout for successfully fulfilling update request
- `queue.slashingEnabled` - to disincentivize malicious oracle behavior
- Permit new oracles to join the network
## More Information
- [/idl/accounts/OracleQueueAccountData](/idl/accounts/OracleQueueAccountData)
- [/idl/accounts/OracleQueueBuffer](/idl/accounts/OracleQueueBuffer)