昨天透過 Lession 15 教學
學會了建構與 Biance API 溝通的 Oracle Web Component
然而在更新 ETH Price 的元件部份仍然是限制 Owner 才能做更新也就是仍是中心化 Oracle
今天將會透過 Lession 16 建構一個比較去中心化的 Oracle 元件
在昨天的教學使用 OpenZeppelin 的 Ownable Contract 來實作只允許 Owner 來呼叫 setLattestEthPrice 的功能
為了要讓 Oracle 更加去中心化,必須要來實作一個允許不同層級的存取的概念。也就是讓存取層級分為 owner 與 oracle: owner 能夠新增與移除 Oracle。 oracle 則可以透過 setLatestEthPrice 來更新 ETH 價格。
在 OpenZeppelin 提供了一個函式庫叫作 Roles 可以有這樣的功能
首先需要引入 Roles 如下:
import "openzeppelin-solidity/contracts/access/Roles.sol";
然後,可以先來看 Roles Contract 的內容
pragma solidity ^0.5.0;
/**
* @title Roles
* @dev Library for managing addresses assigned to a Role.
*/
library Roles {
struct Role {
mapping (address => bool) bearer;
}
/**
* @dev Give an account access to this role.
*/
function add(Role storage role, address account) internal {
require(!has(role, account), "Roles: account already has role");
role.bearer[account] = true;
}
/**
* @dev Remove an account's access to this role.
*/
function remove(Role storage role, address account) internal {
require(has(role, account), "Roles: account does not have role");
role.bearer[account] = false;
}
/**
* @dev Check if an account has this role.
* @return bool
*/
function has(Role storage role, address account) internal view returns (bool) {
require(account != address(0), "Roles: account is the zero address");
return role.bearer[account];
}
}
而要使用 Roles 除了 import 之外還需要使用以下語法
using Roles for Roles.Role;
一旦做了以上引用,就可使用 add, remove 與 has 來實作根據 Role 來限制存取
使用方式如下
oracles.add(_oracle); // Adds `_oracle` to the list of oracles
oracles.remove(_oracle); // Removes `_oracle` from the list of oracles
oracles.has(msg.sender); // Returns `true` if `msg.sender` is an `oracle`
pragma solidity 0.5.0;
// 1. On the next line, import from the `openzeppelin-solidity/contracts/access/Roles.sol` file
import "openzeppelin-solidity/contracts/access/Roles.sol";
import "./CallerContractInterface.sol";
contract EthPriceOracle { //2. Remove `is Ownable`
// 2. Continue here
using Roles for Roles.Role;
Roles.Role private owners;
Roles.Role private oracles;
uint private randNonce = 0;
uint private modulus = 1000;
mapping(uint256=>bool) pendingRequests;
event GetLatestEthPriceEvent(address callerAddress, uint id);
event SetLatestEthPriceEvent(uint256 ethPrice, address callerAddress);
function getLatestEthPrice() public returns (uint256) {
randNonce++;
uint id = uint(keccak256(abi.encodePacked(now, msg.sender, randNonce))) % modulus;
pendingRequests[id] = true;
emit GetLatestEthPriceEvent(msg.sender, id);
return id;
}
function setLatestEthPrice(uint256 _ethPrice, address _callerAddress, uint256 _id) public onlyOwner {
require(pendingRequests[_id], "This request is not in my pending list.");
delete pendingRequests[_id];
CallerContractInterface callerContractInstance;
callerContractInstance = CallerContractInterface(_callerAddress);
callerContractInstance.callback(_ethPrice, _id);
emit SetLatestEthPriceEvent(_ethPrice, _callerAddress);
}
}
現在 Contract 已經不繼承 Ownable
所以必須透㳀 constructor 來設定 owner
constructor 是一個特別函數只會在發佈時執行一次
以下是 constructor 語法
contract MyAwesomeContract {
constructor (address _owner) public {
// Do something
}
}
要呼叫時則需要使用以下語法
deployer.deploy(EthPriceOracle, '0xb090d88a3e55906de49d76b66bf4fe70b9d6d708')
而再 constructor 內需要使用以下語法設定 owner
owners.add(_owner);
owners.add(_owner);
更新如下
pragma solidity 0.5.0;
import "openzeppelin-solidity/contracts/access/Roles.sol";
import "./CallerContractInterface.sol";
contract EthPriceOracle {
using Roles for Roles.Role;
Roles.Role private owners;
Roles.Role private oracles;
uint private randNonce = 0;
uint private modulus = 1000;
mapping(uint256=>bool) pendingRequests;
event GetLatestEthPriceEvent(address callerAddress, uint id);
event SetLatestEthPriceEvent(uint256 ethPrice, address callerAddress);
// Start here
constructor(address _owner) public {
owners.add(_owner);
}
function getLatestEthPrice() public returns (uint256) {
randNonce++;
uint id = uint(keccak256(abi.encodePacked(now, msg.sender, randNonce))) % modulus;
pendingRequests[id] = true;
emit GetLatestEthPriceEvent(msg.sender, id);
return id;
}
function setLatestEthPrice(uint256 _ethPrice, address _callerAddress, uint256 _id) public onlyOwner {
require(pendingRequests[_id], "This request is not in my pending list.");
delete pendingRequests[_id];
CallerContractInterface callerContractInstance;
callerContractInstance = CallerContractInterface(_callerAddress);
callerContractInstance.callback(_ethPrice, _id);
emit SetLatestEthPriceEvent(_ethPrice, _callerAddress);
}
}
下一步需要實作把新的 oracle address 計錄下來給其他已知的 oracles
以下是執行流程
require(owners.has(msg.sender), "Not an owner");
更新如下
pragma solidity 0.5.0;
import "openzeppelin-solidity/contracts/access/Roles.sol";
import "./CallerContractInterface.sol";
contract EthPriceOracle {
using Roles for Roles.Role;
Roles.Role private owners;
Roles.Role private oracles;
uint private randNonce = 0;
uint private modulus = 1000;
mapping(uint256=>bool) pendingRequests;
event GetLatestEthPriceEvent(address callerAddress, uint id);
event SetLatestEthPriceEvent(uint256 ethPrice, address callerAddress);
// 1. Define `AddOracleEvent`
event AddOracleEvent(address oracleAddress);
constructor (address _owner) public {
owners.add(_owner);
}
// 2. Continue here
function addOracle(address _oracle) public {
require(owners.has(msg.sender), "Not an owner!");
}
function getLatestEthPrice() public returns (uint256) {
randNonce++;
uint id = uint(keccak256(abi.encodePacked(now, msg.sender, randNonce))) % modulus;
pendingRequests[id] = true;
emit GetLatestEthPriceEvent(msg.sender, id);
return id;
}
function setLatestEthPrice(uint256 _ethPrice, address _callerAddress, uint256 _id) public onlyOwner {
require(pendingRequests[_id], "This request is not in my pending list.");
delete pendingRequests[_id];
CallerContractInterface callerContractInstance;
callerContractInstance = CallerContractInterface(_callerAddress);
callerContractInstance.callback(_ethPrice, _id);
emit SetLatestEthPriceEvent(_ethPrice, _callerAddress);
}
}
require(!oracles.has(owner), "Already an oracle");
更新如下
pragma solidity 0.5.0;
import "openzeppelin-solidity/contracts/access/Roles.sol";
import "./CallerContractInterface.sol";
contract EthPriceOracle {
using Roles for Roles.Role;
Roles.Role private owners;
Roles.Role private oracles;
uint private randNonce = 0;
uint private modulus = 1000;
mapping(uint256=>bool) pendingRequests;
event GetLatestEthPriceEvent(address callerAddress, uint id);
event SetLatestEthPriceEvent(uint256 ethPrice, address callerAddress);
event AddOracleEvent(address oracleAddress);
constructor (address _owner) public {
owners.add(_owner);
}
function addOracle (address _oracle) public {
require(owners.has(msg.sender), "Not an owner!");
// Start here
require(!oracles.has(_oracle), "Already an oracle!");
oracles.add(_oracle);
emit AddOracleEvent(_oracle);
}
function getLatestEthPrice() public returns (uint256) {
randNonce++;
uint id = uint(keccak256(abi.encodePacked(now, msg.sender, randNonce))) % modulus;
pendingRequests[id] = true;
emit GetLatestEthPriceEvent(msg.sender, id);
return id;
}
function setLatestEthPrice(uint256 _ethPrice, address _callerAddress, uint256 _id) public onlyOwner {
require(pendingRequests[_id], "This request is not in my pending list.");
delete pendingRequests[_id];
CallerContractInterface callerContractInstance;
callerContractInstance = CallerContractInterface(_callerAddress);
callerContractInstance.callback(_ethPrice, _id);
emit SetLatestEthPriceEvent(_ethPrice, _callerAddress);
}
}
require(numOracles > 1, "Do not remove the last oracle!");
更新如下
pragma solidity 0.5.0;
import "openzeppelin-solidity/contracts/access/Roles.sol";
import "./CallerContractInterface.sol";
contract EthPriceOracle {
using Roles for Roles.Role;
Roles.Role private owners;
Roles.Role private oracles;
uint private randNonce = 0;
uint private modulus = 1000;
// 1. Initialize `numOracles`
uint private numOracles = 0;
mapping(uint256=>bool) pendingRequests;
event GetLatestEthPriceEvent(address callerAddress, uint id);
event SetLatestEthPriceEvent(uint256 ethPrice, address callerAddress);
event AddOracleEvent(address oracleAddress);
//2. Declare `RemoveOracleEvent`
event RemoveOracleEvent(address oracleAddress);
constructor (address _owner) public {
owners.add(_owner);
}
function addOracle (address _oracle) public {
require(owners.has(msg.sender), "Not an owner!");
require(!oracles.has(_oracle), "Already an oracle!");
oracles.add(_oracle);
numOracles++;
emit AddOracleEvent(_oracle);
}
function removeOracle (address _oracle) public {
require(owners.has(msg.sender), "Not an owner!");
require(oracles.has(_oracle), "Not an oracle!");
// 3. Continue here
require(numOracles > 1, "Do not remove the last oracle!");
oracles.remove(_oracle);
numOracles--;
emit RemoveOracleEvent(_oracle);
}
function getLatestEthPrice() public returns (uint256) {
randNonce++;
uint id = uint(keccak256(abi.encodePacked(now, msg.sender, randNonce))) % modulus;
pendingRequests[id] = true;
emit GetLatestEthPriceEvent(msg.sender, id);
return id;
}
function setLatestEthPrice(uint256 _ethPrice, address _callerAddress, uint256 _id) public onlyOwner {
require(pendingRequests[_id], "This request is not in my pending list.");
delete pendingRequests[_id];
CallerContractInterface callerContractInstance;
callerContractInstance = CallerContractInterface(_callerAddress);
callerContractInstance.callback(_ethPrice, _id);
emit SetLatestEthPriceEvent(_ethPrice, _callerAddress);
}
}
當新的 oracles 加入, Contract 會預期會產生新的一個 resposne 。
而之前是透過 pendingRequests 來紀錄有哪些 request 需要回應
為了要能正確的回應,每個 response 必須要紀錄以下資訊
並且會需要把這些資訊與 request id 做關聯
可以透過 mapping 來紀錄 id 對應到以上資訊所形成的一個 array
以上的資訊可以透過 struct 語法組成一個資料集
語法如下
struct MyStruct {
address anAddress;
uint256 aNumber;
}
初始化 struct 語法則如下
MyStruct memory myStructInstance;
myStructInstance = MyStruct(msg.sender, 200);
struct Response {
address oracleAddress;
address callerAddress;
uint256 ethPrice;
}
resp = Rresponse(msg.sender, _callerAddress, _ethPrice);
requestIdToResponse[_id].push(resp);
更新如下
pragma solidity 0.5.0;
import "openzeppelin-solidity/contracts/access/Roles.sol";
import "./CallerContractInterface.sol";
contract EthPriceOracle {
using Roles for Roles.Role;
Roles.Role private owners;
Roles.Role private oracles;
uint private randNonce = 0;
uint private modulus = 1000;
uint private numOracles = 0;
mapping(uint256=>bool) pendingRequests;
//1. Define `Response
struct Response {
address oracleAddress;
address callerAddress;
uint256 ethPrice;
}
mapping (uint256=>Response[]) public requestIdToResponse;
event GetLatestEthPriceEvent(address callerAddress, uint id);
event SetLatestEthPriceEvent(uint256 ethPrice, address callerAddress);
event AddOracleEvent(address oracleAddress);
event RemoveOracleEvent(address oracleAddress);
constructor (address _owner) public {
owners.add(_owner);
}
function addOracle (address _oracle) public {
require(owners.has(msg.sender), "Not an owner!");
require(!oracles.has(_oracle), "Already an oracle!");
oracles.add(_oracle);
numOracles++;
emit AddOracleEvent(_oracle);
}
function removeOracle (address _oracle) public {
require(owners.has(msg.sender), "Not an owner!");
require(oracles.has(_oracle), "Not an oracle!");
require (numOracles > 1, "Do not remove the last oracle!");
oracles.remove(_oracle);
numOracles--;
emit RemoveOracleEvent(_oracle);
}
function getLatestEthPrice() public returns (uint256) {
randNonce++;
uint id = uint(keccak256(abi.encodePacked(now, msg.sender, randNonce))) % modulus;
pendingRequests[id] = true;
emit GetLatestEthPriceEvent(msg.sender, id);
return id;
}
function setLatestEthPrice(uint256 _ethPrice, address _callerAddress, uint256 _id) public {
require(oracles.has(msg.sender), "Not an oracle!");
require(pendingRequests[_id], "This request is not in my pending list.");
// 2. Continue here
Response memory resp;
resp = Response(msg.sender, _callerAddress, _ethPrice);
requestIdToResponse[_id].push(resp);
delete pendingRequests[_id];
CallerContractInterface callerContractInstance;
callerContractInstance = CallerContractInterface(_callerAddress);
callerContractInstance.callback(_ethPrice, _id);
emit SetLatestEthPriceEvent(_ethPrice, _callerAddress);
}
}
一旦 Smart Contradct 接到了新的 ETH 價格, 就可以透過 callback 來把資訊更新到 caller Contract
語法如下
callerContractInstance.callback(_ethPrice, _id);
然而,將會根據 numOracles 來更新每個 Request
代表所拿到的價格並無法即時,因為 Oracle Contract 無法隨時更新只有 Caller 呼叫才會去拿取新的 ETH price
假設 fetch API 失敗或是網路中斷了,則 Oracle Contract 將會無法回應正確的 ETH Price 也就是,只能等網路正常能做到回覆
然而比較好的作法是透過一個門閥值,當這個數字回等於這個門閥值,
Smart Contract 則會計算出 ETH 價格給 caller Contract。
這樣會比一直 block response 在 pending resquest 好一點點
if (numResponse == THRESHOLD) {
}
delete pendingRequests[_id];
CallerContractInterface callerContractInstance;
callerContractInstance = CallerContractInterface(_callerAddress);
callerContractInstance.callback(_ethPrice, _id);
emit SetLatestEthPriceEvent(_ethPrice, _callerAddress);
更新如下
pragma solidity 0.5.0;
import "openzeppelin-solidity/contracts/access/Roles.sol";
import "./CallerContractInterface.sol";
contract EthPriceOracle {
using Roles for Roles.Role;
Roles.Role private owners;
Roles.Role private oracles;
uint private randNonce = 0;
uint private modulus = 1000;
uint private numOracles = 0;
uint private THRESHOLD = 0;
mapping(uint256=>bool) pendingRequests;
struct Response {
address oracleAddress;
address callerAddress;
uint256 ethPrice;
}
event GetLatestEthPriceEvent(address callerAddress, uint id);
event SetLatestEthPriceEvent(uint256 ethPrice, address callerAddress);
event AddOracleEvent(address oracleAddress);
event RemoveOracleEvent(address oracleAddress);
event SetThresholdEvent (uint threshold);
constructor (address _owner) public {
owners.add(_owner);
}
function addOracle (address _oracle) public {
require(owners.has(msg.sender), "Not an owner!");
require(!oracles.has(_oracle), "Already an oracle!");
oracles.add(_oracle);
numOracles++;
emit AddOracleEvent(_oracle);
}
function removeOracle (address _oracle) public {
require(owners.has(msg.sender), "Not an owner!");
require(oracles.has(_oracle), "Not an oracle!");
require (numOracles > 1, "Do not remove the last oracle!");
oracles.remove(_oracle);
numOracles--;
emit RemoveOracleEvent(_oracle);
}
function setThreshold (uint _threshold) public {
require(owners.has(msg.sender), "Not an owner!");
THRESHOLD = _threshold;
emit SetThresholdEvent(THRESHOLD);
}
function getLatestEthPrice() public returns (uint256) {
randNonce++;
uint id = uint(keccak256(abi.encodePacked(now, msg.sender, randNonce))) % modulus;
pendingRequests[id] = true;
emit GetLatestEthPriceEvent(msg.sender, id);
return id;
}
function setLatestEthPrice(uint256 _ethPrice, address _callerAddress, uint256 _id) public {
require(oracles.has(msg.sender), "Not an oracle!");
require(pendingRequests[_id], "This request is not in my pending list.");
Response memory resp;
resp = Response(msg.sender, _callerAddress, _ethPrice);
requestIdToResponse[_id].push(resp);
// Start here
uint numResponses = requestIdToResponse[_id].length;
if (numResponses == THRESHOLD) {
delete pendingRequests[_id];
CallerContractInterface callerContractInstance;
callerContractInstance = CallerContractInterface(_callerAddress);
callerContractInstance.callback(_ethPrice, _id);
emit SetLatestEthPriceEvent(_ethPrice, _callerAddress);
}
}
}
for (uint f = 0; f < requestIdToResponse[_id].length; f++) {
}
for (uint f = 0; f < requestIdToResponse[_id].length; f++) {
computedEthPrice += requestIdToResponse[_id][f].ethPrice;
}
更新如下:
pragma solidity 0.5.0;
import "openzeppelin-solidity/contracts/access/Roles.sol";
import "./CallerContractInterface.sol";
contract EthPriceOracle {
using Roles for Roles.Role;
Roles.Role private owners;
Roles.Role private oracles;
uint private randNonce = 0;
uint private modulus = 1000;
uint private numOracles = 0;
uint private THRESHOLD = 0;
mapping(uint256=>bool) pendingRequests;
struct Response {
address oracleAddress;
address callerAddress;
uint256 ethPrice;
}
event GetLatestEthPriceEvent(address callerAddress, uint id);
event SetLatestEthPriceEvent(uint256 ethPrice, address callerAddress);
event AddOracleEvent(address oracleAddress);
event RemoveOracleEvent(address oracleAddress);
event SetThresholdEvent (uint threshold);
constructor (address _owner) public {
owners.add(_owner);
}
function addOracle (address _oracle) public {
require(owners.has(msg.sender), "Not an owner!");
require(!oracles.has(_oracle), "Already an oracle!");
oracles.add(_oracle);
numOracles++;
emit AddOracleEvent(_oracle);
}
function removeOracle (address _oracle) public {
require(owners.has(msg.sender), "Not an owner!");
require(oracles.has(_oracle), "Not an oracle!");
require (numOracles > 1, "Do not remove the last oracle!");
oracles.remove(_oracle);
numOracles--;
emit RemoveOracleEvent(_oracle);
}
function setThreshold (uint _threshold) public {
require(owners.has(msg.sender), "Not an owner!");
THRESHOLD = _threshold;
emit SetThresholdEvent(THRESHOLD);
}
function getLatestEthPrice() public returns (uint256) {
randNonce++;
uint id = uint(keccak256(abi.encodePacked(now, msg.sender, randNonce))) % modulus;
pendingRequests[id] = true;
emit GetLatestEthPriceEvent(msg.sender, id);
return id;
}
function setLatestEthPrice(uint256 _ethPrice, address _callerAddress, uint256 _id) public {
require(oracles.has(msg.sender), "Not an oracle!");
require(pendingRequests[_id], "This request is not in my pending list.");
Response memory resp;
resp = Response(msg.sender, _callerAddress, _ethPrice);
requestIdToResponse[_id].push(resp);
uint numResponses = requestIdToResponse[_id].length;
if (numResponses == THRESHOLD) {
// Start here
uint computedEthPrice = 0;
for (uint f=0; f < requestIdToResponse[_id].length; f++) {
computedEthPrice += requestIdToResponse[_id][f].ethPrice;
}
computedEthPrice = computedEthPrice/numResponses;
delete pendingRequests[_id];
CallerContractInterface callerContractInstance;
callerContractInstance = CallerContractInterface(_callerAddress);
callerContractInstance.callback(_ethPrice, _id);
emit SetLatestEthPriceEvent(_ethPrice, _callerAddress);
}
}
}
using SafeMath for uint256;
修正 computedEthPrice += requestIdToResponse[_id][f].ethPrice; 改用 SafeMath 運算子
修正 computedEthPrice = computedEthPrice / numResponses; 使用 SafeMath 運算子
更新如下
pragma solidity 0.5.0;
import "openzeppelin-solidity/contracts/access/Roles.sol";
import "openzeppelin-solidity/contracts/math/SafeMath.sol";
import "./CallerContractInterface.sol";
contract EthPriceOracle {
using Roles for Roles.Role;
Roles.Role private owners;
Roles.Role private oracles;
// 1. Tell your contract to use `SafeMath` for `uint256`
using SafeMath for uint256;
uint private randNonce = 0;
uint private modulus = 1000;
uint private numOracles = 0;
uint private THRESHOLD = 0;
mapping(uint256=>bool) pendingRequests;
struct Response {
address oracleAddress;
address callerAddress;
uint256 ethPrice;
}
event GetLatestEthPriceEvent(address callerAddress, uint id);
event SetLatestEthPriceEvent(uint256 ethPrice, address callerAddress);
event AddOracleEvent(address oracleAddress);
event RemoveOracleEvent(address oracleAddress);
event SetThresholdEvent (uint threshold);
constructor (address _owner) public {
owners.add(_owner);
}
function addOracle (address _oracle) public {
require(owners.has(msg.sender), "Not an owner!");
require(!oracles.has(_oracle), "Already an oracle!");
oracles.add(_oracle);
numOracles++;
emit AddOracleEvent(_oracle);
}
function removeOracle (address _oracle) public {
require(owners.has(msg.sender), "Not an owner!");
require(oracles.has(_oracle), "Not an oracle!");
require (numOracles > 1, "Do not remove the last oracle!");
oracles.remove(_oracle);
numOracles--;
emit RemoveOracleEvent(_oracle);
}
function setThreshold (uint _threshold) public {
require(owners.has(msg.sender), "Not an owner!");
THRESHOLD = _threshold;
emit SetThresholdEvent(THRESHOLD);
}
function getLatestEthPrice() public returns (uint256) {
randNonce++;
uint id = uint(keccak256(abi.encodePacked(now, msg.sender, randNonce))) % modulus;
pendingRequests[id] = true;
emit GetLatestEthPriceEvent(msg.sender, id);
return id;
}
function setLatestEthPrice(uint256 _ethPrice, address _callerAddress, uint256 _id) public {
require(oracles.has(msg.sender), "Not an oracle!");
require(pendingRequests[_id], "This request is not in my pending list.");
Response memory resp;
resp = Response(msg.sender, _callerAddress, _ethPrice);
requestIdToResponse[_id].push(resp);
uint numResponses = requestIdToResponse[_id].length;
if (numResponses == THRESHOLD) {
uint computedEthPrice = 0;
for (uint f=0; f < requestIdToResponse[_id].length; f++) {
computedEthPrice = computedEthPrice.add(requestIdToResponse[_id][f].ethPrice);
}
computedEthPrice = computedEthPrice.div(numResponses);
delete pendingRequests[_id];
CallerContractInterface callerContractInstance;
callerContractInstance = CallerContractInterface(_callerAddress);
callerContractInstance.callback(_ethPrice, _id);
emit SetLatestEthPriceEvent(_ethPrice, _callerAddress);
}
}
}
更新如下
pragma solidity 0.5.0;
import "openzeppelin-solidity/contracts/access/Roles.sol";
import "openzeppelin-solidity/contracts/math/SafeMath.sol";
import "./CallerContractInterface.sol";
contract EthPriceOracle {
using Roles for Roles.Role;
Roles.Role private owners;
Roles.Role private oracles;
using SafeMath for uint256;
uint private randNonce = 0;
uint private modulus = 1000;
uint private numOracles = 0;
uint private THRESHOLD = 0;
mapping(uint256=>bool) pendingRequests;
struct Response {
address oracleAddress;
address callerAddress;
uint256 ethPrice;
}
event GetLatestEthPriceEvent(address callerAddress, uint id);
event SetLatestEthPriceEvent(uint256 ethPrice, address callerAddress);
event AddOracleEvent(address oracleAddress);
event RemoveOracleEvent(address oracleAddress);
event SetThresholdEvent (uint threshold);
constructor (address _owner) public {
owners.add(_owner);
}
function addOracle (address _oracle) public {
require(owners.has(msg.sender), "Not an owner!");
require(!oracles.has(_oracle), "Already an oracle!");
oracles.add(_oracle);
numOracles++;
emit AddOracleEvent(_oracle);
}
function removeOracle (address _oracle) public {
require(owners.has(msg.sender), "Not an owner!");
require(oracles.has(_oracle), "Not an oracle!");
require (numOracles > 1, "Do not remove the last oracle!");
oracles.remove(_oracle);
numOracles--;
emit RemoveOracleEvent(_oracle);
}
function setThreshold (uint _threshold) public {
require(owners.has(msg.sender), "Not an owner!");
THRESHOLD = _threshold;
emit SetThresholdEvent(THRESHOLD);
}
function getLatestEthPrice() public returns (uint256) {
randNonce++;
uint id = uint(keccak256(abi.encodePacked(now, msg.sender, randNonce))) % modulus;
pendingRequests[id] = true;
emit GetLatestEthPriceEvent(msg.sender, id);
return id;
}
function setLatestEthPrice(uint256 _ethPrice, address _callerAddress, uint256 _id) public {
require(oracles.has(msg.sender), "Not an oracle!");
require(pendingRequests[_id], "This request is not in my pending list.");
Response memory resp;
resp = Response(msg.sender, _callerAddress, _ethPrice);
requestIdToResponse[_id].push(resp);
uint numResponses = requestIdToResponse[_id].length;
if (numResponses == THRESHOLD) {
uint computedEthPrice = 0;
for (uint f=0; f < requestIdToResponse[_id].length; f++) {
computedEthPrice = computedEthPrice.add(requestIdToResponse[_id][f].ethPrice);
}
computedEthPrice = computedEthPrice.div(numResponses);
delete pendingRequests[_id];
// Delete `_id` from `requestIdToResponse
delete requestIdToResponse[_id];
CallerContractInterface callerContractInstance;
callerContractInstance = CallerContractInterface(_callerAddress);
callerContractInstance.callback(computedEthPrice, _id); // Update this line code
emit SetLatestEthPriceEvent(computedEthPrice, _callerAddress); // Update this line of code
}
}
}