本博客整理于 cryptozombies.io/zh/lesson
查看原教程请参考如上链接,本博客做个人记录!
从零开始写预言机(三)
CATALOGUE
1、概览
在前面我们已经实现的预言机功能里面,只有一个预言机合约,并且只从一个api中获取数据。这显然是不够安全可靠的!接下来我们将着力于提升该预言机的去中心化性!(从多个预言机中获取来自多个api中的数据!)
代码部分,要修改的部分仅为EthOracleContract.sol合约
2、EthOracleContract.sol
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;
}
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);
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);
}
// 该函数供caller调用,实现发起一个数据请求!
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;
}
// 调用该函数将ethPrice写入callerContract合约中
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 requestIdToResponse[_id];
CallerContractInterface callerContractInstance;
callerContractInstance = CallerContractInterface(_callerAddress);
callerContractInstance.callback(computedEthPrice, _id);
emit SetLatestEthPriceEvent(computedEthPrice, _callerAddress);
}
}
}
在这部分里面,我们加入了owners与oracles的权限管理。
引入了openzeppelin-solidity中的Roles库
可以添加多个预言机合约,并且删除了(一)中的有关onlyOwner的函数修饰符的内容,改用在函数内用require判断是否是owners、是否是oracle
并且在setLatestEthPrice函数中,添加了THRESHOLD的变量,表明有一定数量的预言机返回eth价格后才将结果写回调用者合约