Skip to content

Commit e548ceb

Browse files
64ixLe-Caignec
andauthored
feat: Add iExec DOracle protocol documentation (#40)
Co-authored-by: Le-Caignec <[email protected]>
1 parent 3d41073 commit e548ceb

File tree

3 files changed

+333
-0
lines changed

3 files changed

+333
-0
lines changed

.vitepress/sidebar.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -113,6 +113,10 @@ export function getSidebar() {
113113
text: '💸 Pay Per Task Model',
114114
link: '/get-started/protocol/pay-per-task',
115115
},
116+
{
117+
text: 'iExec DOracle',
118+
link: '/get-started/protocol/iexec-doracle',
119+
},
116120
{
117121
text: '⚙️ Workers & Workerpools',
118122
collapsed: true,

README.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -191,3 +191,5 @@ for input parameters:
191191
- Give recap of Workerpool address fo chains
192192
- Talk about ENS on Bellecour(it's not supported on arbitrum)
193193
- Rework Advanced iApp building guides. (from "old" protocol doc)
194+
- Rework src\get-started\protocol\iexec-doracle.md (transfer to guide or
195+
rewrite)
Lines changed: 327 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,327 @@
1+
---
2+
description: A flexible and secure Oracle Solution
3+
---
4+
5+
# iExec DOracle
6+
7+
> A flexible and secure Oracle Solution
8+
9+
## Why do we need Oracles?
10+
11+
The Ethereum blockchain provides a global trustless computer: given some input
12+
and the smart contract code, the blockchain guarantees execution according to
13+
the Ethereum specification, by replicating the execution across thousands of
14+
nodes and implementing a consensus mechanism across all these nodes. Hence the
15+
execution of the contract is decentralized, and will happen without the need to
16+
trust any single actor.
17+
18+
Unfortunately decentralizing the execution is not sufficient. To be of any
19+
real-world use, a smart contract most often requires access to external,
20+
real-world information. For example an insurance smart contract could require
21+
data about the weather to trigger payment, or a hedging contract could require
22+
pricing data. This information is already available in the digital world: the
23+
web 2.0 is full of nice APIs that provide all kinds of data. It is however not
24+
straightforward to put this information on the blockchain: if the update message
25+
comes from a single wallet, then this wallet controls the whole execution
26+
outcome. It means the smart contract has to trust an off-chain actor \(the owner
27+
of an Ethereum wallet\) to provide such information, which defeats the purpose
28+
of decentralization: now the information provider becomes the trusted third
29+
party that decentralization was supposed to do away with in the first place!
30+
31+
Oracles are systems designed to solve this problem: providing the blockchain
32+
with data from the real world in the most secure and robust way possible. It
33+
turns out we at iExec have been working on this problem for a long time. Indeed
34+
an update to an Oracle \(for example the price of a stock or the average
35+
temperature for a day\) can be seen as the result of a specific type of
36+
off-chain computation, one that would involve calling an API and processing the
37+
response to return the final result. As a result the iExec infrastructure is
38+
perfectly suited to build an efficient and secure Oracle system: the iExec
39+
dOracle.
40+
41+
## The iExec solution: the Decentralized Oracle \(dOracle\)
42+
43+
For two years iExec has been working on the design of the
44+
[Proof of Contribution protocol](/get-started/protocol/proof-of-contribution),
45+
which provides a flexible and highly robust solution to the problem of off-chain
46+
computation. At its core it is a simple Schelling game between off-chain
47+
computation providers \(Workers\): a given number of Workers are randomly chosen
48+
in a much bigger group, and are assigned the same computation. Each of them
49+
proposes a result, and the result that is proposed by the biggest number of
50+
workers is taken as the overall computation result \(see PoCo documentation for
51+
more details\).
52+
53+
The PoCo is both flexible and robust: the trust level for the computation \(e.g.
54+
for the Oracle update in the dOracle case\) can be set arbitrarily, and
55+
determines the number of replications. It also includes a coherent on-chain
56+
incentive mechanism, that protects the whole system against any \(financially
57+
sustainable\) attack. Last but not least, it is cheap and scalable: the more
58+
Workers join the iExec platform, the more secure and the cheaper running a
59+
dOracle will be. iExec dOracle relies on random sampling among all the Workers
60+
on the iExec platform, along with an on-chain consensus algorithm and an
61+
integrated trust score system to make an attack on the dOracle result
62+
exponentially expensive.
63+
64+
iExec dOracle builds on top of the decentralized cloud computing platform
65+
developed by iExec to allow developers to easily create robust and secure
66+
decentralized oracle. Building an Oracle with iExec is therefore extremely
67+
simple: just create a dApp with the logic of the Oracle \(querying an API,
68+
processing different results into a final one\); the iExec platform will
69+
automatically replicate it across many different workers; then the PoCo will
70+
realize a consensus on the different values. The whole process is simple and as
71+
secure as you wish - provided enough money is paid for each execution / oracle
72+
update.
73+
74+
### Why you should use iExec dOracle
75+
76+
iExec dOracle allows you to create your own Oracle, with custom logic, while
77+
benefiting from the security guarantees of the whole iExec platform.
78+
79+
- It is secure. You can set the desired level of trust for your dOracle
80+
execution. The conjunction of random sampling and iExec’s built-in incentive
81+
and reputation systems makes your dOracle highly secured.
82+
- It is easy-to-use. It relies on 2 years of research and development to make
83+
the iExec platform simple and developer friendly. Creating your own
84+
personalized Decentralized Oracle only takes a simple dockerized application
85+
and a few lines of Solidity!
86+
- It is cheap. It does not rely on bribing or incentivizing honest behavior,
87+
only on random sampling and a powerful reputation system to make attack
88+
impractical.
89+
90+
## How does it work?
91+
92+
### Background: task execution on the iExec platform
93+
94+
The iExec architecture is two-sided: the on-chain part is a set of smart
95+
contracts that implement the PoCo, handle the incentive and adjudication
96+
systems; and the off-chain part is made of workers, that provide computing
97+
resources to execute the tasks, and schedulers, that dispatch the tasks to
98+
execute between the workers of the worker-pool they manage. Each side of the
99+
iExec platform \(worker-pool, computation requester\) create and sign orders
100+
that describe the kind of transaction they are willing to enter \(type of
101+
hardware, minimum price, etc…\). When several orders of different types are
102+
compatible they are matched together on the blockchain, to create a deal. Once a
103+
deal is made, the scheduler that is part of the deal will choose a set of
104+
workers in his workerpool to execute the task. Each worker will download the
105+
dApp \(a docker container\) and run it. Upon execution of the task, each worker
106+
sends back two values on the blockchain:
107+
108+
- a hash of the result.
109+
- after consensus is reached, the corresponding result.
110+
111+
A normal execution ends when the deal is finalized; all the stakeholders are
112+
paid, and the computation requester is free to download the data pointed to by
113+
the results field of the `Deal` object on the blockchain.
114+
115+
### iExec d'Oracle: general architecture
116+
117+
An iExec dOracle can be seen as an “on-chain API”: fundamentally it is a simple
118+
value-store smart contract, with accessors for other smart contracts to get its
119+
data, and an internal mechanism to update the data in the most secure way
120+
possible. The dOracle architecture is composed of two parts: an on-chain smart
121+
contract and a classical iExec dApp \(packaged in a docker container\).
122+
123+
**Off-chain component:**
124+
125+
The off-chain part of a dOracle is a classical iExec dApp, that will be executed
126+
on the iExec platform and be replicated on several workers as part of an iExec
127+
computation deal. It contains the oracle logic, for example to query a web API
128+
and process the result. Whenever an operator wishes to update the dOracle, it
129+
requests a computation like in a normal iExec deal, specifying the dOracle app
130+
as dApp, and the parameters if applicable. The dOracle result is written in the
131+
`${IEXEC_OUT}/computed.json` file by the dApp, under the `callback_data` key.
132+
133+
```bash
134+
$ cat ${IEXEC_OUT}/computed.json
135+
{ 'callback-data': '0x48656c6c6f2c20776f726c6421'}
136+
```
137+
138+
When the computation ends the worker will send both this `callback-data`
139+
\(containing the oracle result\) on the blockchain. The `callback-data` value is
140+
stored in the `resultsCallback` field of the `Task` object in the `IexecProxy`
141+
smart contract.
142+
143+
**On-chain component:**
144+
145+
The on-chain part is the dOracle contract. Anyone can request an update of its
146+
internal state by sending the id of a task corresponding to the execution of the
147+
corresponding dApp. With this id, the dOracle contract will query the blockchain
148+
and retrieve the deal object. It then checks that the execution passes the
149+
dOracle requirements \(trust level, execution tag, that the app is right\). If
150+
it does the dOracle contract then decodes the value in the results field and
151+
update its fields accordingly. The value is then accessible like a normal value
152+
on a smart contract.
153+
154+
## Example: development and workflow of a price-feed application
155+
156+
A simple example of dOracle is available on Github. The following section goes
157+
through its different components, explaining what each of them does.
158+
159+
### The PriceFeed dApp
160+
161+
The PriceFeed dApp is a simple Node.js script, available at
162+
[Kaiko PriceFeed Source](https://github.com/iExecBlockchainComputing/iexec-apps/blob/master/offchain-computing/offchain-tee-kaiko-pricefeed/src/app.py).
163+
Given a set of parameters, the application encodes its result so that it can be
164+
interpreted by the corresponding dOracle smart contract, stores it in
165+
`${IEXEC_OUT}/computed.json`, and stores the hash of this encoded value to
166+
perform the consensus. The Worker will then send these values on-chain as part
167+
of the task finalization, where they will be accessible by the dOracle smart
168+
contract.
169+
170+
For example, given the parameters `"BTC USD 9 2019-04-11T13:08:32.605Z"` the
171+
price-oracle application will:
172+
173+
- Retrieve the price of BTC in USD at 2019-04-11T13:08:32.605Z
174+
- Multiply this value by `10e9` \(to capture the price value more accurately as
175+
it will be represented by an integer onchain\)
176+
- Encode the date, the description \(`"btc-usd-9"`\) and the value using
177+
`abi.encode`
178+
- Store this result in `${IEXEC_OUT}/computed.json` under the `callback-data`
179+
key
180+
181+
iExec will then achieve PoCo consensus on the hash of the `callback-data` value,
182+
and will then submit `callback-data` values on-chain, in the `Task` object on
183+
the `IexecProxy` smart contract.
184+
185+
Once your oracle dApp is written, you can build it into a Docker image and make
186+
it available on the iExec platform as explained here.
187+
188+
### The dOracle generic contract
189+
190+
Every dOracle must inherit from the `IexecDoracle` contract \(source available
191+
on [Github](https://github.com/iExecBlockchainComputing/iexec-doracle-base) and
192+
[npm](https://www.npmjs.com/package/iexec-doracle-base)\).
193+
194+
This contract stores the following fields:
195+
196+
```text
197+
IexecInterfaceToken public iexecproxy;
198+
address public m_authorizedApp;
199+
address public m_authorizedDataset;
200+
address public m_authorizedWorkerpool;
201+
bytes32 public m_requiredtag;
202+
uint256 public m_requiredtrust;
203+
```
204+
205+
In particular, the `m_authorizedApp` must be the address of the smart contract
206+
of the dOracle dApp, and the `m_requiredtag` describes the parameters of the
207+
iExec `Task` necessary to validate the dOracle update.
208+
209+
The dOracle exposes mainly three internal functions, that may be used by the
210+
contracts that inherit from it:
211+
212+
A constructor:
213+
214+
```text
215+
constructor(address _iexecproxy) public
216+
```
217+
218+
A function to initialize/update the settings:
219+
220+
```text
221+
function _iexecDoracleUpdateSettings(
222+
address _authorizedApp
223+
, address _authorizedDataset
224+
, address _authorizedWorkerpoo
225+
, bytes32 _requiredtag
226+
, uint256 _requiredtrust
227+
)
228+
internal
229+
```
230+
231+
The update function, that takes in input a task id, and reads the `Task` object
232+
data from the `IexecProxy` smart contract to perform the required checks (the
233+
execution must be completed; the app, the dataset, and the workerpool must be
234+
authorized; the trust level and tags mus be valid). The `IexecProxy` already
235+
checked that the hash of the `resultsCallback` is equal to the `resultDigest`
236+
\(over which the consensus was reached\). If the task passes the checks then it
237+
returns the `results` field of the `Task` object, i.e. the result of the dOracle
238+
dApp computation.
239+
240+
```text
241+
function _iexecDoracleGetVerifiedResult(bytes32 _doracleCallId)
242+
internal view returns (bytes memory)
243+
```
244+
245+
A dOracle smart contract should inherit from the generic `IexecDOracle`
246+
contract, and expose two main functionalities:
247+
248+
- An update function, that will call the internal \(and inherited\)
249+
`_iexecDoracleGetVerifiedResult` function and process its result to update the
250+
dOracle contract internal state.
251+
- One or several accessor functions, that allows other smart contract to access
252+
the oracle value\(s\).
253+
254+
### The PriceOracle dOracle contract
255+
256+
In the PriceFeed example, the
257+
[PriceOracle](https://github.com/iExecBlockchainComputing/iexec-doracle-base/blob/bb4c04dc77c822d16d7ca8baed99f5626e44d7be/contracts/example/PriceOracle.sol)
258+
smart contract is made of three parts:
259+
260+
- Its internal state description: a `TimedValue` struct storing the oracle data
261+
for a given value, and a `values` field that maps an index of the form
262+
`“BTC-USD-9”` to the corresponding `TimedValue` struct value.
263+
264+
```text
265+
struct TimedValue
266+
{
267+
bytes32 oracleCallID;
268+
uint256 date;
269+
uint256 value;
270+
string details;
271+
}
272+
273+
mapping(bytes32 => TimedValue) public values;
274+
```
275+
276+
This also allows to read the resulting prices. For example, to get the most
277+
recent price of BTC in USD with 9 place precision \(as described above\), query
278+
`values(keccak256(bytes("BTC-USD-9")))` from the dOracle contract and this will
279+
return a structure containing the value, the associated date, and the details of
280+
the request.
281+
282+
- The update function `processResult`, that takes the task id of an execution of
283+
the dOracle dApp, calls the internal `_iexecDoracleGetVerifiedResult` and
284+
processes the result to update the `values` map.
285+
286+
```text
287+
function processResult(bytes32 _oracleCallID)
288+
public
289+
{
290+
uint256 date;
291+
string memory details;
292+
uint256 value;
293+
294+
// Parse results
295+
(date, details, value) = decodeResults(_iexecDoracleGetVerifiedResult(_oracleCallID));
296+
297+
// Process results
298+
bytes32 id = keccak256(bytes(details));
299+
require(values[id].date < date, "new-value-is-too-old");
300+
emit ValueChange(id, _oracleCallID, values[id].date, values[id].value, date, value);
301+
values[id].oracleCallID = _oracleCallID;
302+
values[id].date = date;
303+
values[id].value = value;
304+
values[id].details = details;
305+
}
306+
```
307+
308+
The PriceFeed dOracle also declares an event `ValueChange`, that is fired
309+
whenever an update is made.
310+
311+
- An `updateEnv` function, that can be used by the owner of the dOracle to
312+
update its parameters. It simply calls the `_iexecDoracleUpdateSettings`
313+
function of its parent `IexecDoracle` contract.
314+
315+
```text
316+
function updateEnv(
317+
address _authorizedApp
318+
, address _authorizedDataset
319+
, address _authorizedWorkerpool
320+
, bytes32 _requiredtag
321+
, uint256 _requiredtrust
322+
)
323+
public onlyOwner
324+
{
325+
_iexecDoracleUpdateSettings(_authorizedApp, _authorizedDataset, _authorizedWorkerpool, _requiredtag, _requiredtrust);
326+
}
327+
```

0 commit comments

Comments
 (0)