Offchain reporting: Difference between revisions
From Chainlink Community Wiki
No edit summary |
No edit summary |
||
(5 intermediate revisions by 2 users not shown) | |||
Line 1: | Line 1: | ||
__TOC__ | __TOC__ | ||
<h2><span style="color:#36c">Price Feeds | <h2><span style="color:#36c">Price Feeds Off-chain Reporting (OCR)</span></h2> | ||
<p>Migrating from FluxMonitor to Offchain Reporting was by far the most impressive upgrade, and the increase in efficiency was, and is, simply amazing.</p> | <p>Migrating from FluxMonitor to Offchain Reporting was by far the most impressive upgrade, and the increase in efficiency was, and is, simply amazing.</p> | ||
<p>The migration resulted in an exponential increase in efficiency and an overall easier experience as a Chainlink Node Operator.</p> | <p>The migration resulted in an exponential increase in efficiency and an overall easier experience as a Chainlink Node Operator.</p> | ||
<p> </p> | <p> </p> | ||
<h2>Migration to | <h2> <span style="color:#36c">Migration to Off-chain Reporting: ETH / USD </span> </h2> | ||
* '''Oct 21:''' The Proxy is updated to point to the OCR Contract, with the FluxMonitor contract as a failover.</p> | |||
---- | |||
* '''11 May 21:''' The first transmissions for the ETH / USD Price Feed were broadcast on-chain.</p> | |||
---- | |||
* '''06 May 21:''' The ETH / USD AccessControlledOffchainAggregator contract was deployed | |||
** at address '''[https://etherscan.io/address/0x37bc7498f4ff12c19678ee8fe19d713b87f6a9e6 0x37bC7498f4FF12C19678ee8fE19d713b87F6a9e6]''' | |||
** at transaction hash '''[https://etherscan.io/tx/0x3442b045dde57720c72992dbc2dfd4ea0c608bdbe1d561cb584c1e2681e9edc7 0x3442b045dde57720c72992dbc2dfd4ea0c608bdbe1d561cb584c1e2681e9edc7]''' | |||
---- | |||
== <span style="color:#36c">Off-chain Reporting (OCR) Operational Overview: </span> == | |||
==== <span style="color:#36c">The OCR feeds are designed with access control and high availability in mind. </span> ==== | |||
== <span style="color:#36c"> | |||
In case of a contract or network failure, there are failover points designed into the system. | In case of a contract or network failure, there are failover points designed into the system. | ||
=== In the details below, we will be looking at the ETH / USD price feed. === | === <span style="color:#36c">In the details below, we will be looking at the ETH / USD price feed. </span> === | ||
The following example is specific to a standard price feed. It’s important to note that there are numerous specific use-case contracts in production that may have different designs, but the general contract design will remain applicable. | The following example is specific to a standard price feed. It’s important to note that there are numerous specific use-case contracts in production that may have different designs, but the general contract design will remain applicable. | ||
[[File:OcrOverview.png|center|frameless|1100x1100px]] | [[File:OcrOverview.png|center|frameless|1100x1100px]] | ||
<p> | <p> | ||
=== Important Definitions: === | === <span style="color:#36c">Important Definitions: </span> === | ||
==== Deviation Threshold: ==== | ==== <span style="color:#36c">Deviation Threshold: </span> ==== | ||
The value that an asset needs to deviate from the previously reported value. | The value that an asset needs to deviate from the previously reported value. | ||
Maintaining the example of the ETH / USD price feed, if the <span style="font-family:Courier;">AccessControlledOffchainAggregators</span> most recently reported value of Ethereum is <span style="font-family:Courier;">451804000000</span> or <span style="font-family:Courier;">$4,518.04</span>, and a single Chainlink node operator’s calculated value deviates from that value greater than <span style="font-family:Courier;">0.5%</span> (<span style="font-family:Courier;">± 2259020000</span> or <span style="font-family:Courier;">± $22.59</span>), a new round will be fired and the new aggregate value will be written to chain. | Maintaining the example of the ETH / USD price feed, if the <span style="font-family:Courier;">AccessControlledOffchainAggregators</span> most recently reported value of Ethereum is <span style="font-family:Courier;">451804000000</span> or <span style="font-family:Courier;">$4,518.04</span>, and a single Chainlink node operator’s calculated value deviates from that value greater than <span style="font-family:Courier;">0.5%</span> (<span style="font-family:Courier;">± 2259020000</span> or <span style="font-family:Courier;">± $22.59</span>), a new round will be fired and the new aggregate value will be written to chain. | ||
==== Heartbeat: ==== | ==== <span style="color:#36c">Heartbeat: </span> ==== | ||
Maximum age of an on-chain asset value. | Maximum age of an on-chain asset value. | ||
If the deviation threshold is not reached within the time of the heartbeat, the feed will be updated. The heartbeat for the ETH / USD feed is one hour. For example, if the price of Ethereum is stable | If the deviation threshold is not reached within the time of the heartbeat, the feed will be updated. The heartbeat for the ETH / USD feed is one hour. For example, if the price of Ethereum is stable and does not deviate greater than <span style="font-family:Courier;">0.5%</span> within an hour, a new round will be fired off regardless of the deviation threshold. | ||
==== Observer: ==== | ==== <span style="color:#36c">Observer: </span> ==== | ||
For each round of a price feed, each Chainlink node is responsible for submitting observations. These observations are an aggregate of multiple data sources; typically a median. | For each round of a price feed, each Chainlink node is responsible for submitting observations. These observations are an aggregate of multiple data sources; typically a median. | ||
For each round a Chainlink node participates in, they’re rewarded an observation payment. | For each round a Chainlink node participates in, they’re rewarded an observation payment. [[The Link token|LINK token]] | ||
==== Transmitter: ==== | ==== <span style="color:#36c">Transmitter: </span> ==== | ||
While every Chainlink node is expected to participate in observations, only one Chainlink node is (initially) pseudo-randomly elected to transmit the agreed upon aggregate value that is calculated from all observations. | While every Chainlink node is expected to participate in observations, only one Chainlink node is (initially) pseudo-randomly elected to transmit the agreed upon aggregate value that is calculated from all observations. | ||
Line 52: | Line 49: | ||
If the initial transmitter is unable to transmit the transaction within a reasonable time, two additional transmitters are elected, creating a race condition. | If the initial transmitter is unable to transmit the transaction within a reasonable time, two additional transmitters are elected, creating a race condition. | ||
=== Contract Descriptions: === | === <span style="color:#36c">Contract Descriptions: </span> === | ||
There are three major contracts to be aware of: | There are three major contracts to be aware of: | ||
Line 59: | Line 56: | ||
* <span style="font-family:Courier;">AccessControlledOffchainAggregator</span> | * <span style="font-family:Courier;">AccessControlledOffchainAggregator</span> | ||
==== EACAggregatorProxy: ==== | ==== <span style="color:#36c">EACAggregatorProxy: </span> ==== | ||
This is the proxy contract that consumer contracts will read data from. This contract is designed to enable an active passive data source. If we take a look at the aggregator that the ETH / USD proxy points to, we’ll see that it is the address of the ETH / USD <span style="font-family:Courier;">AccessControlledOffchainAggregator<span>. | This is the proxy contract that consumer contracts will read data from. This contract is designed to enable an active passive data source. If we take a look at the aggregator that the ETH / USD proxy points to, we’ll see that it is the address of the ETH / USD <span style="font-family:Courier;">AccessControlledOffchainAggregator<span>. | ||
[[File:EACAggregatorProxy.png]] | [[File:EACAggregatorProxy.png]] | ||
It gets a little obfuscated if we look at the <span style="font-family:Courier;">proposedAggregator</span>, as that shows as <span style="font-family:Courier;">0x0000000000000000000000000000000000000000</span>. However; this is tied to a flag contract, | It gets a little obfuscated if we look at the <span style="font-family:Courier;">proposedAggregator</span>, as that shows as <span style="font-family:Courier;">0x0000000000000000000000000000000000000000</span>. However; this is tied to a flag contract, the operation of which is outside the scope of this documentation. The important takeaway is that the proxy tentatively points at a backup price feed that is almost identical to the primary, but is highly secure. | ||
==== AccessControlledOffchainAggregator: ==== | ==== <span style="color:#36c">AccessControlledOffchainAggregator: </span> ==== | ||
This is the contract that | This is the contract that node operators provide price updates to. If we take a look at the ETH / USD example above (<span style="font-family:Courier;">0x37bC7498f4FF12C19678ee8fE19d713b87F6a9e6</span>), we can see quite a few getters for this contract. | ||
We’ll focus on three of these in this documentation. | We’ll focus on three of these in this documentation. | ||
Line 81: | Line 78: | ||
We can see that the <span style="font-family:Courier;">latestAnswer</span> is <span style="font-family:Courier;">451804000000</span>. | We can see that the <span style="font-family:Courier;">latestAnswer</span> is <span style="font-family:Courier;">451804000000</span>. | ||
* If we adjust for decimal places, we’ll know that the price of | * If we adjust for decimal places, we’ll know that the price of Ethereum during this round was <span style="font-family:Courier;">$4,518.04</span>. | ||
We can see that the contract is referencing another contract, the <span style="font-family:Courier;">requesterAccessController</span> at address <span style="font-family:Courier;">0x641b698ad1c6e503470520b0eecb472c0589dfe6</span>. | We can see that the contract is referencing another contract, the <span style="font-family:Courier;">requesterAccessController</span> at address <span style="font-family:Courier;">0x641b698ad1c6e503470520b0eecb472c0589dfe6</span>. | ||
==== SimpleWriteAccessController: ==== | ==== <span style="color:#36c">SimpleWriteAccessController: </span> ==== | ||
The <span style="font-family:Courier;">SimpleWriteAccessController</span>’s purposes is to control which contracts can read and/or write to a contract. Read and write access for both the <span style="font-family:Courier;">EACAggregatorProxy</span> and the <span style="font-family:Courier;">AccessControlledOffchainAggregator</span> can be managed by the <span style="font-family:Courier;">SimpleWriteAccessController</span>. | The <span style="font-family:Courier;">SimpleWriteAccessController</span>’s purposes is to control which contracts can read and/or write to a contract. Read and write access for both the <span style="font-family:Courier;">EACAggregatorProxy</span> and the <span style="font-family:Courier;">AccessControlledOffchainAggregator</span> can be managed by the <span style="font-family:Courier;">SimpleWriteAccessController</span>. | ||
Line 92: | Line 89: | ||
In short, this enables the contract owner to have data on-chain which is only accessible to the desired contracts. | In short, this enables the contract owner to have data on-chain which is only accessible to the desired contracts. | ||
=== Diagrams: === | === <span style="color:#36c">Diagrams: </span> === | ||
In this section, you will find diagrams representing the relationships between each of the above contracts. | In this section, you will find diagrams representing the relationships between each of the above contracts. | ||
==== SimpleWriteAccessController: ==== | ==== <span style="color:#36c">SimpleWriteAccessController: </span> ==== | ||
Here we can see the ConsumerContract requesting the latest round data from the <span style="font-family:Courier;">EACAggregatorProxy</span> contract. Before the <span style="font-family:Courier;">EACAggregatorProxy</span> provides the response to the <span style="font-family:Courier;">ConsumerContract</span>, it will check the <span style="font-family:Courier;">SimpleReadAccessControl</span> function in the <span style="font-family:Courier;">SimpleWriteAccessControl</span> contract. It will compare the requesting <span style="font-family:Courier;">ConsumerContract</span>’s address against the <span style="font-family:Courier;">AccessList</span>. If the requesting <span style="font-family:Courier;">ConsumerContract</span> address is found in the <span style="font-family:Courier;">AccessList</span>, it will be able to read the lastestRound response from the <span style="font-family:Courier;">EACAggregatorProxy</span>. | Here we can see the ConsumerContract requesting the latest round data from the <span style="font-family:Courier;">EACAggregatorProxy</span> contract. Before the <span style="font-family:Courier;">EACAggregatorProxy</span> provides the response to the <span style="font-family:Courier;">ConsumerContract</span>, it will check the <span style="font-family:Courier;">SimpleReadAccessControl</span> function in the <span style="font-family:Courier;">SimpleWriteAccessControl</span> contract. It will compare the requesting <span style="font-family:Courier;">ConsumerContract</span>’s address against the <span style="font-family:Courier;">AccessList</span>. If the requesting <span style="font-family:Courier;">ConsumerContract</span> address is found in the <span style="font-family:Courier;">AccessList</span>, it will be able to read the lastestRound response from the <span style="font-family:Courier;">EACAggregatorProxy</span>. | ||
[[File:SimpleWriteAccessControllerDiagram.png]] | [[File:SimpleWriteAccessControllerDiagram.png]] | ||
==== EACAggregatorProxy Failover Design: ==== | ==== <span style="color:#36c">EACAggregatorProxy Failover Design: </span> ==== | ||
Here we can see the proxy contract pointing at the primary aggregator. In case of a failure event, the proxy contract can be pointed to another aggregator contract, without the consumer having to make any adjustments on their end. | Here we can see the proxy contract pointing at the primary aggregator. In case of a failure event, the proxy contract can be pointed to another aggregator contract, without the consumer having to make any adjustments on their end. | ||
[[File:ProxyFailoverdiagram.png]] | [[File:ProxyFailoverdiagram.png]] | ||
==== AccessControlledOffchainAggregator ==== | ==== <span style="color:#36c">AccessControlledOffchainAggregator </span> ==== | ||
The <span style="font-family:Courier;">AccessControlledOffchainAggregator</span> is the contract that each Chainlink node interacts with. | The <span style="font-family:Courier;">AccessControlledOffchainAggregator</span> is the contract that each Chainlink node interacts with. | ||
Line 112: | Line 109: | ||
For the purpose of this document, we will highlight a couple of them: | For the purpose of this document, we will highlight a couple of them: | ||
===== Calculating the Aggregate Value for Each Round ===== | ===== <span style="color:#36c">Calculating the Aggregate Value for Each Round </span> ===== | ||
In the following diagram, we can see each of the Chainlink nodes querying three different data sources. | In the following diagram, we can see each of the Chainlink nodes querying three different data sources. | ||
Line 121: | Line 118: | ||
Each Chainlink node signs the calculated aggregate value. | Each Chainlink node signs the calculated aggregate value. | ||
This aggregated, median of medians, value | This aggregated, median of medians, is the value the contract will receive as the latestAnswer | ||
[[File:PriceFeedFullView.png]] | [[File:PriceFeedFullView.png]] |
Latest revision as of 07:10, 11 May 2022
Price Feeds Off-chain Reporting (OCR)
Migrating from FluxMonitor to Offchain Reporting was by far the most impressive upgrade, and the increase in efficiency was, and is, simply amazing.
The migration resulted in an exponential increase in efficiency and an overall easier experience as a Chainlink Node Operator.
Migration to Off-chain Reporting: ETH / USD
- Oct 21: The Proxy is updated to point to the OCR Contract, with the FluxMonitor contract as a failover.
- 11 May 21: The first transmissions for the ETH / USD Price Feed were broadcast on-chain.
- 06 May 21: The ETH / USD AccessControlledOffchainAggregator contract was deployed
- at address 0x37bC7498f4FF12C19678ee8fE19d713b87F6a9e6
- at transaction hash 0x3442b045dde57720c72992dbc2dfd4ea0c608bdbe1d561cb584c1e2681e9edc7
Off-chain Reporting (OCR) Operational Overview:
The OCR feeds are designed with access control and high availability in mind.
In case of a contract or network failure, there are failover points designed into the system.
In the details below, we will be looking at the ETH / USD price feed.
The following example is specific to a standard price feed. It’s important to note that there are numerous specific use-case contracts in production that may have different designs, but the general contract design will remain applicable.
Important Definitions:
Deviation Threshold:
The value that an asset needs to deviate from the previously reported value.
Maintaining the example of the ETH / USD price feed, if the AccessControlledOffchainAggregators most recently reported value of Ethereum is 451804000000 or $4,518.04, and a single Chainlink node operator’s calculated value deviates from that value greater than 0.5% (± 2259020000 or ± $22.59), a new round will be fired and the new aggregate value will be written to chain.
Heartbeat:
Maximum age of an on-chain asset value.
If the deviation threshold is not reached within the time of the heartbeat, the feed will be updated. The heartbeat for the ETH / USD feed is one hour. For example, if the price of Ethereum is stable and does not deviate greater than 0.5% within an hour, a new round will be fired off regardless of the deviation threshold.
Observer:
For each round of a price feed, each Chainlink node is responsible for submitting observations. These observations are an aggregate of multiple data sources; typically a median.
For each round a Chainlink node participates in, they’re rewarded an observation payment. LINK token
Transmitter:
While every Chainlink node is expected to participate in observations, only one Chainlink node is (initially) pseudo-randomly elected to transmit the agreed upon aggregate value that is calculated from all observations.
The transmitter is compensated for their observation, reimbursed for the gas fees spent on broadcasting the transaction, and is rewarded a transmitter bonus.
If the initial transmitter is unable to transmit the transaction within a reasonable time, two additional transmitters are elected, creating a race condition.
Contract Descriptions:
There are three major contracts to be aware of:
- EACAggregatorProxy
- SimpleWriteAccessController
- AccessControlledOffchainAggregator
EACAggregatorProxy:
This is the proxy contract that consumer contracts will read data from. This contract is designed to enable an active passive data source. If we take a look at the aggregator that the ETH / USD proxy points to, we’ll see that it is the address of the ETH / USD AccessControlledOffchainAggregator.
It gets a little obfuscated if we look at the proposedAggregator, as that shows as 0x0000000000000000000000000000000000000000. However; this is tied to a flag contract, the operation of which is outside the scope of this documentation. The important takeaway is that the proxy tentatively points at a backup price feed that is almost identical to the primary, but is highly secure.
AccessControlledOffchainAggregator:
This is the contract that node operators provide price updates to. If we take a look at the ETH / USD example above (0x37bC7498f4FF12C19678ee8fE19d713b87F6a9e6), we can see quite a few getters for this contract.
We’ll focus on three of these in this documentation.
- description
- latestAnswer
- requesterAccessController
We can see that the description is ETH / USD.
We can see that the latestAnswer is 451804000000.
- If we adjust for decimal places, we’ll know that the price of Ethereum during this round was $4,518.04.
We can see that the contract is referencing another contract, the requesterAccessController at address 0x641b698ad1c6e503470520b0eecb472c0589dfe6.
SimpleWriteAccessController:
The SimpleWriteAccessController’s purposes is to control which contracts can read and/or write to a contract. Read and write access for both the EACAggregatorProxy and the AccessControlledOffchainAggregator can be managed by the SimpleWriteAccessController.
To manage access, there is an AccessList in the SimpleWriteAccessController that will track which contracts are able to read and/or write to/from the EACAggregatorProxy and AccessControlledOffchainAggregator.
In short, this enables the contract owner to have data on-chain which is only accessible to the desired contracts.
Diagrams:
In this section, you will find diagrams representing the relationships between each of the above contracts.
SimpleWriteAccessController:
Here we can see the ConsumerContract requesting the latest round data from the EACAggregatorProxy contract. Before the EACAggregatorProxy provides the response to the ConsumerContract, it will check the SimpleReadAccessControl function in the SimpleWriteAccessControl contract. It will compare the requesting ConsumerContract’s address against the AccessList. If the requesting ConsumerContract address is found in the AccessList, it will be able to read the lastestRound response from the EACAggregatorProxy.
EACAggregatorProxy Failover Design:
Here we can see the proxy contract pointing at the primary aggregator. In case of a failure event, the proxy contract can be pointed to another aggregator contract, without the consumer having to make any adjustments on their end.
AccessControlledOffchainAggregator
The AccessControlledOffchainAggregator is the contract that each Chainlink node interacts with.
At the contract level, the AccessControlledOffchainAggregator tracks quite a bit of information.
For the purpose of this document, we will highlight a couple of them:
Calculating the Aggregate Value for Each Round
In the following diagram, we can see each of the Chainlink nodes querying three different data sources.
Of these three data sources, each Chainlink node will calculate the median value and report it to the other nodes.
Once a minimum threshold of nodes have reported their values, a median of medians is then calculated.
Each Chainlink node signs the calculated aggregate value.
This aggregated, median of medians, is the value the contract will receive as the latestAnswer
https://www.youtube.com/watch?v=hN91vq6yHRM
Off-chain Reporting resources
Timeline
Date | Media | Author | Title |
---|---|---|---|
24 Feb 21 | Article | Chainlink | Chainlink Achieves Major Scalability Upgrade With Mainnet Launch of Off-Chain Reporting (OCR) |
Educational
Date | Media | Author | Title |
---|---|---|---|
N/A | Official Docs | Chainlink | Off-Chain Reporting- Official Chainlink Documentation |
08 Oct 22 | Video | Chainlink | Looking Under the Hood of OCR 2.0: Product Deep Dive | Lorenz Breidenbach at SmartCon 2022 |
24 Feb 21 | Research | Chainlink | Chainlink Off-chain Reporting Protocol |
Feb 21 | Research | Smart Contract Research Forum | Research Summary - Chainlink Off-Chain Reporting Protocol |
27 Jan 21 | Article | Reputation Link | Off-Chain Reporting - Technical Overview |