Skip to content
This repository was archived by the owner on Apr 30, 2026. It is now read-only.
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Binary file modified .DS_Store
Binary file not shown.
Binary file added flight-delay/.DS_Store
Binary file not shown.
7 changes: 7 additions & 0 deletions flight-delay/app/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
FROM node:11-alpine
COPY flightDelay.js /src/flightDelay.js
COPY entrypoint.sh /entrypoint.sh
RUN npm i axios ethers fs
RUN mkdir iexec_out
RUN chmod +x /entrypoint.sh
ENTRYPOINT ["/entrypoint.sh"]
2 changes: 2 additions & 0 deletions flight-delay/app/entrypoint.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
#!/bin/sh
node src/flightDelay.js $@
60 changes: 60 additions & 0 deletions flight-delay/app/flightDelay.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
const ethers = require('ethers');
const fs = require('fs');
const axios = require('axios');


const root = 'iexec_out';
const determinismFilePath = `${root}/determinism.iexec`;
const callbackFilePath = `${root}/callback.iexec`;
const errorFilePath = `${root}/error.iexec`;


/*****************************************************************************
* ARGUMENTS *
*****************************************************************************/

var [id, timestamp] = process.argv.slice(2).map(s => s);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the map is not really needed here :P

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Timestamp is not processed and just passed in the result. There is absolutelly no link between this timestamp and the results provided.

A better approach would be to not include timestamp in the arguments, and put a timestamp comming provided by the api (latest update for this flight ?)

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I couldn't find any freely available API's (didn't check the details of paid ones) with flight delay data that takes in timestamp as a parameter. To counter that problem I created my own API for the pr but that created a single point of failure. Can you suggest some workaround?


var appId = '0ddf43fd';
var appKey = 'c8663643d1b8d907717810f38f97ffb1';

// var timestampInMili = new Date().getTime();
// var timestamp = parseInt(timestampInMili/1000)
// console.log(longi);
// console.log(lati);
// console.log(timestamp);
//flightID = 1023084433
/*****************************************************************************
* HTTP QUERY *
*****************************************************************************/

let url = `https://api.flightstats.com/flex/flightstatus/rest/v2/json/flight/status/${id}?appId=${appId}&appKey=${appKey}`;
/*****************************************************************************
* EXECUTE *
*****************************************************************************/
axios.get(url)
.then(res=>{
// console.log(res);
var result = res.data;
var delay = parseInt(result.flightStatus.delays.departureGateDelayMinutes);
console.log(delay);
var iexeccallback = ethers.utils.defaultAbiCoder.encode(['uint256', 'uint256', 'uint256'], [timestamp, id, delay]);
var iexecconsensus = ethers.utils.keccak256(iexeccallback);
fs.writeFile(callbackFilePath, iexeccallback , (err) => {});
fs.writeFile(determinismFilePath, iexecconsensus, (err) => {});

})
.catch(err=>{
fs.writeFile(
errorFilePath,
err.toString(),
(error) => {}
);
fs.writeFile(
determinismFilePath,
ethers.utils.solidityKeccak256(['string'],[err.toString()]),
(error) => {}
);
throw new Error(err);
})

29 changes: 29 additions & 0 deletions flight-delay/chain.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
{
"default": "kovan",
"chains": {
"dev": {
"host": "http://localhost:8545",
"sms": "http://localhost:5000",
"id": "17",
"hub": "0x60E25C038D70A15364DAc11A042DB1dD7A2cccBC"
},
"ropsten": {
"host": "https://ropsten.infura.io/v3/f3e0664e01504f5ab2b4360853ce0dc7",
"id": "3"
},
"rinkeby": {
"host": "https://rinkeby.infura.io/v3/f3e0664e01504f5ab2b4360853ce0dc7",
"id": "4"
},
"kovan": {
"host": "https://kovan.infura.io/v3/f3e0664e01504f5ab2b4360853ce0dc7",
"id": "42",
"sms": "https://sms-kovan.iex.ec"
},
"mainnet": {
"host": "https://mainnet.infura.io/v3/f3e0664e01504f5ab2b4360853ce0dc7",
"id": "1",
"sms": "https://sms-mainnet.iex.ec"
}
}
}
5 changes: 5 additions & 0 deletions flight-delay/deployed.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{
"app": {
"42": "0x2bEe1A8d963D9f3d8Da6Db5A5e23aA4dB537811B"
}
}
35 changes: 35 additions & 0 deletions flight-delay/iexec.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
{
"description": "My iExec ressource description, must be at least 150 chars long in order to pass the validation checks. Describe your application, dataset or workerpool to your users",
"license": "MIT",
"author": "?",
"social": {
"website": "?",
"github": "?"
},
"logo": "logo.png",
"buyConf": {
"params": "",
"tag": "0x0000000000000000000000000000000000000000000000000000000000000000",
"trust": "0",
"callback": "0x0000000000000000000000000000000000000000"
},
"app": {
"owner": "0xf3Ba773Af9e30B8187E2Abbd0B4F3218273127D0",
"name": "flightDelay",
"type": "DOCKER",
"multiaddr": "registry.hub.docker.com/nikhil3000/flight-delay:1.0.1",
"checksum": "0x866f12aee5381b2ab2b029b678f0c2dbc125ee06db7f045eeaefcb0998a589f5",
"mrenclave": ""
},
"order": {
"apporder": {
"app": "0x2bEe1A8d963D9f3d8Da6Db5A5e23aA4dB537811B",
"appprice": "0",
"volume": "1000000",
"tag": "0x0000000000000000000000000000000000000000000000000000000000000000",
"datasetrestrict": "0x0000000000000000000000000000000000000000",
"workerpoolrestrict": "0x0000000000000000000000000000000000000000",
"requesterrestrict": "0x0000000000000000000000000000000000000000"
}
}
}
15 changes: 15 additions & 0 deletions flight-delay/orders.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
{
"42": {
"apporder": {
"app": "0x2bEe1A8d963D9f3d8Da6Db5A5e23aA4dB537811B",
"appprice": "0",
"volume": "1000000",
"tag": "0x0000000000000000000000000000000000000000000000000000000000000000",
"datasetrestrict": "0x0000000000000000000000000000000000000000",
"workerpoolrestrict": "0x0000000000000000000000000000000000000000",
"requesterrestrict": "0x0000000000000000000000000000000000000000",
"salt": "0x0285178da87007386921c7dc76fc009fa47ac4ab76986949d3234c6494c909cd",
"sign": "0xc2097916b26007f2b26a824aa4690901d86c79e21158bae2426054397474c0384ddf3c3be20ff065a2b64c96cb02af4ef25f7f0d6213704db98c769c6875a51c1c"
}
}
}
Binary file added flight-delay/smart-contract/.DS_Store
Binary file not shown.
6 changes: 6 additions & 0 deletions flight-delay/smart-contract/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
node_modules
build

package-lock.json
yarn.lock

75 changes: 75 additions & 0 deletions flight-delay/smart-contract/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
iExec Dencentralized Oracle System
==================================

About iExec
-----------

Thanks to iExec, it is possible to achieve onchain consensus about the result of an offchain application. Applications are represented by smart contracts and execution results can be made available onchain with all the necessary proof.

Building an oracle application
------------------------------

iExec applications produce different outputs.
* The consensus is achieved based on a deterministic value describing the application output. By default this is the hash of the result archive, but can be overriden by the content of `/iexec_out/determinism.iexec`. Upon successful verification, this is stored onchain in the `task.resultDigest` field.
* The actual result. By default this is the IPFS address of a (potentially encrypted) archive containing the outputs, but can be overridden by the content of `/iexec_out/callback.iexec`. Upon successful verification, this is stored onchain in the `task.results` field.

An iExec oracle application such as the one used in the price-oracle example uses these 2 elements to produce verified results to the blockchain.

Given a set of parameters, the application produces a self-describing result, encodes it in a way that can be interpreted onchain, stores it in `/iexec_out/callback.iexec` so that is can be accessed onchain, and stores the hash of this encoded value to perform the consensus.

For example, given the parameters "BTC USD 9 2019-04-11T13:08:32.605Z" the price-oracle application will:

1. Retreive the price of BTC in USD at 2019-04-11T13:08:32.605Z
2. Multiply this value by 10e9 (to capture price value more accurately as it will be represented by an integer onchain)
3. encode the date, the description ("btc-usd-9") and the value using `abi.encode`
4. Store this result in `/iexec_out/callback.iexec`
5. hash the result and store it in `/iexec_out/determinism.iexec`

iExec will then achieve PoCo consensus on the `/iexec_out/determinism.iexec` value, and will store both the `/iexec_out/determinism.iexec` and the `/iexec_out/callback.iexec` onchain.

Given a taskID, it is possible to retrieve all the details of the computation as described above. The oracle smart contract just needs to retrieve the information, verify the validity of the execution and process the encoded result. Thanks to the PoCo consensus, anyone can require a computation and ask the oracle to update itself in a trustless manner.

How to setup an oracle contract
-------------------------------

1. Record the address of the iExec Hub and Clerk contracts

2. Register the requirements needed for a result to be processed
* Which application (single, any, whitelist?)
* Which dataset (single, any, whitelist?)
* Which workerpool (single, any, whitelist?)
* Minimum level of trust
* Mandatory tag

How to update an oracle contract
--------------------------------

1. Send the taskID of a valid execution to the oracle smart contract.
2. The oracle smart contract retrieves details about this task from the iexec's smart contracts.
3. The oracle smart contract verifies the execution is valid (authorized app, dataset, workerpool, trust level and tags).
4. The oracle smart contract verifies the hash of the results correspond to the resultDigest that achieved consensus, thus verifying the validity of the result field.
5. The oracle smart contract decodes the results using `abi.decode`.
6. The oracle smart contract processes the results. In the case of the price oracle, this means storing the value if it is more recent than the one currently recorded.

How to read price from the iExec price oracle
---------------------------------------------

Just query the oracle `values` field with the id of the requested field. For example, to get the most recent price of BTC in USD with 9 place precision (as described above), query `values(keccak256(bytes("BTC-USD-9")))` and this will return a structure containing the value, the associate date, and the details of the request.

Deployed addresses
------------------

1. **Kovan:**

price oracle: `https://kovan.etherscan.io/address/0x3b9f1a9aecb1991f3818f45bd4cc735f4bee93ac`

app whitelist: `https://kovan.etherscan.io/address/0x651a09cdff5a6669ea8bf05be11eff4aa9cbfdaf`

whitelist contains:

* `0xf92f39545340ce2fd6f4248a689fca4f660ae42f`
* `0xe01bccbcab54c42f999b6ce88d63d3a5e96cfdb7`

Whitelist is administered by:

* `0x7bd4783FDCAD405A28052a0d1f11236A741da593`
68 changes: 68 additions & 0 deletions flight-delay/smart-contract/contracts/FlightDelayOracle.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
pragma solidity ^0.5.8;
pragma experimental ABIEncoderV2;

import "openzeppelin-solidity/contracts/ownership/Ownable.sol";
import "iexec-doracle-base/contracts/IexecDoracle.sol";

contract FlightDelayOracle is Ownable, IexecDoracle
{
struct TimedValue
{
bytes32 oracleCallID;
uint256 date;
uint256 flightId;
uint256 delay; //delay in minutes
}

mapping(bytes32 => TimedValue) public values;

event ValueUpdated(
bytes32 indexed id,
bytes32 indexed oracleCallID,
uint256 oldDate,
uint256 oldDelay,
uint256 newDate,
uint256 newDelay
);

// Use _iexecHubAddr to force use of custom iexechub, leave 0x0 for autodetect
constructor(address _iexecHubAddr)
public IexecDoracle(_iexecHubAddr)
{}

function updateEnv(
address _authorizedApp
, address _authorizedDataset
, address _authorizedWorkerpool
, bytes32 _requiredtag
, uint256 _requiredtrust
)
public onlyOwner
{
_iexecDoracleUpdateSettings(_authorizedApp, _authorizedDataset, _authorizedWorkerpool, _requiredtag, _requiredtrust);
}

function decodeResults(bytes memory results)
public pure returns(uint256, uint256 , uint256)
{ return abi.decode(results, (uint256, uint256 , uint256 )); }

function processResult(bytes32 _oracleCallID)
public
{
uint256 date;
uint256 flightId;
uint256 delay; //correct to 3 decimal places

// Parse results
(date, flightId, delay) = decodeResults(_iexecDoracleGetVerifiedResult(_oracleCallID));

// Process results
bytes32 id = keccak256(abi.encode(flightId));
require(values[id].date < date, "new-value-is-too-old");
emit ValueUpdated(id, _oracleCallID, values[id].date, values[id].delay, date, delay);
values[id].oracleCallID = _oracleCallID;
values[id].date = date;
values[id].flightId = flightId;
values[id].delay = delay;
}
}
24 changes: 24 additions & 0 deletions flight-delay/smart-contract/contracts/Migrations.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
pragma solidity ^0.5.8;

import "openzeppelin-solidity/contracts/ownership/Ownable.sol";

contract Migrations is Ownable
{
uint256 public lastCompletedMigration;

constructor()
public
{
}

function setCompleted(uint completed) public onlyOwner
{
lastCompletedMigration = completed;
}

function upgrade(address newAddress) public onlyOwner
{
Migrations upgraded = Migrations(newAddress);
upgraded.setCompleted(lastCompletedMigration);
}
}
25 changes: 25 additions & 0 deletions flight-delay/smart-contract/daemon/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
FROM node:8-alpine

# changing user
USER root

# add necessary packages
RUN apk add --no-cache git python make g++

# create a work directory inside the container
RUN mkdir /app
WORKDIR /app

# copy project files
COPY . .

# install utilities
RUN npm install -g yarn ts-node typescript

# install dependencies
RUN yarn

# making entrypoint executable
RUN chmod +x entrypoint.sh

ENTRYPOINT ["./entrypoint.sh"]
Loading