Median and Proof Feeds
High-level Overview
-
MedianFeeds:
- tiny epoch (5 min)
- in the Chunk chain (free gas) with fallback to cheap network
- direct transaction from each validator
- not multiparty, as it requires time for sync
- multiparty update metric set, validator set
- calculates the median for each metric
- stores the signed Merkle root tree for the previous epoch
- can serve as both the primary and backup source; the signatures used for the primary source are also valid for backups
-
Validators:
- collect values for a large number of metrics
- write all values to the MedianFeeds
- at the end of each round
- retrieve values of all metrics on the block at the end of the epoch
- create a Merkle tree and obtain the root
- via multiparty mechanism sign the Merkle tree root for each target network
- send the root, signatures to MedianFeeds
-
ProofFeeds:
- accept root, signatures, proof, metric value, timestamp, epoch
- check that signature of the root is correct
- check that the Merkle proof is valid for the root
- the verified value can be stored and returned via ICoreMultidataFeedsReader
-
js SDK:
- allows dApps to retrieve metric values from the MedianFeeds
- collects values of all metrics on the block at the end of the last epoch
- generates a Merkle tree, root, and proof for the required metric
-
Metric values users:
- retrieve the epoch, Merkle root, signatures, Merkle proof, metric ID, metric value, and metric update timestamp from the JS SDK
- send it to own contract
- the contract verifies that the metric update timestamp is suitable for its purposes
- the contract calls MerkleFeeds and verifies that the metric value and metric update timestamp are correct
MedianFeeds
Standard ICoreMultidataFeedsReader
interface for reading.
Because implementation doesn't support price 2**256-1.
Update values: function update(uint32 epoch_, uint[] calldata metricIds_, uint256[] calldata prices_)
Set signed Merkle tree root for previous epoch:
function setSignedMerkleTreeRoot(
uint32 epoch_, bytes32 root_, uint8 v_, bytes32 r_, bytes32 s_
)
Read the last Merkle tree root:
struct SignedMerkleTreeRoot {
uint32 epoch;
uint8 v;
bytes32 r;
bytes32 s;
bytes32 root;
}
function getSignedMerkleTreeRoot() public view returns (SignedMerkleTreeRoot memory) {
return _signedMerkleTreeRoots;
}
Administrative functions (add validators, add/update metrics) are the same as in Multiparty oracle.
Could be called several times per epoch by each validator, but with different metricIDs. Sending batches of 100 values is recommended. In this case, typically, 1_138_512 - 9_262_586 gas will be spent (best to worst case; depends on the metrics for which the median is calculated).
Validators
Update the set of metrics and set of validators is the same as in MultiPartyFeeds.
Needs to be ported to Python or find an existing solution https://github.com/OpenZeppelin/merkle-tree.
In each epoch, every validator sends all available values to MedianFeeds.
At the end of each epoch of MedianFeeds (on the block of epoch end):
- retrieve Quote for all metrics
- calculate the Merkle tree root for all values (epoch, metric id, metric values, update ts ("uint32", "uint256", "uint256", "uint32")). See https://github.com/OpenZeppelin/merkle-tree
- sign the Merkle tree root for each contract in the target networks and send it to MedianFeeds (setSignedMerkleTreeRoot)
ProofFeeds
Two usage scenarios:
- check the proof retrieved from Multidata MedianFeeds
- check the proof and set values which will be available with
ICoreMultidataFeedsReader
and could be wrapped byChainlinkCompatibility
Useful read methods
Methods that check the correctness of the passed value, update timestamp with passed Merkle proof and stored Merkle tree root:
struct SignedMerkleTreeRoot {
uint32 epoch;
uint8 v;
bytes32 r;
bytes32 s;
bytes32 root;
}
struct CheckedData {
bytes32[] merkleTreeProof;
string metricName;
uint256 metricValue;
uint32 metricUpdateTs;
}
// throws if incorrect
function requireValidProof(
SignedMerkleTreeRoot memory signedMerkleTreeRoot_,
CheckedData memory checkedData_
) external view;
// returns result of check
function isProofValid(
SignedMerkleTreeRoot memory signedMerkleTreeRoot_,
CheckedData memory checkedData_
) external view returns (bool);
Update methods
function setValue(SignedMerkleTreeRoot calldata signedMerkleTreeRoot_, CheckedData calldata data_)
JS SDK
Need to implement method:
getMetricValueWithProof(metricId)
- stores addresses of MedianFeeds in the Chunk chain and backup chains (such as Gnosis)
- gets the latest available Merkle tree root from MedianFeeds in the Chunk and backup chains
- retrieves from MedianFeeds all quotes for Merkle tree epoch from previous step
- calculates the last block of epoch
- retrieves values from MedianFeeds on this block
- builds a Merkle tree https://github.com/OpenZeppelin/merkle-tree
- gets proofs for the metricID
- returns
- SignedMerkleTreeRoot
- CheckedData
Metric Values Users
- Contract (and dApp) must check the update timestamp obtained from the JS SDK
- call the methods
requireValidProof
orisProofValid
to check if data is valid - call
setValue
and then read withICoreMultidataFeedsReader
interface