//tips
//smart contract
Oracleの追加はできたので削除の方法も確認しておく。
ただ、oracleを全て削除する事態は避けたいので、常にoracleの数は把握しておくようにする。
uint private numOracles = 0;
event RemoveOracleEvent(address oracleAddress);
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);
}
ここからさらに分散化が組み込まれるように、oracleコントラクトからスマートコントラクトに、更新させたethの値を返すsetLatestEthPriceを調整する。
というのもoracleが増加すればコントラクトのrequestに対して、より多くのresponseが返ってくることになる。
Mappingを使い、request idをキーにして必要な情報を詰めたstructを呼び出せるようにする形か。
To accomplish this, you're going to use a mapping that'll associate each request id to an array of structs containing oracleAddress, callerAddress, and ethPrice variables.
Structにしておけば下記のように呼び出すことができる。
MyStruct memory myStructInstance; // declare the struct
myStructInstance = new MyStruct(msg.sender, 200); // initialize it
myStructInstance.anAddress = otherAddress
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);
このようにsetLatestEthPriceに組み込む。
このようにoracleが複数接続されるようになってくると下記の運用も変える必要がある。
callerContractInstance.callback(_ethPrice, _id);
まずは複数あるoracleの中で一定の返信を受け付けた後にコントラクトにて計算を実行するようにする。
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) {
delete pendingRequests[_id];
CallerContractInterface callerContractInstance;
callerContractInstance = CallerContractInterface(_callerAddress);
callerContractInstance.callback(_ethPrice, _id);
emit SetLatestEthPriceEvent(_ethPrice, _callerAddress);
}
}
ただ、どのresponseの値になるのかは気になる。ここではoraclesのresponseの平均で対処するよう。これはわかりやすいが、単一のoracleの価格を操作するような攻撃に対して脆弱になる。
この問題に対しては数学的な外れ値を除外するアプローチを取れる。
One way to solve this would be to remove the outliers by using quartiles and interquartile ranges.
uint numResponses = requestIdToResponse[_id].length;
if (numResponses == THRESHOLD) {
uint computedEthPrice = 0;
for (uint f=0; f < requestIdToResponse[_id].length; f++) {
computedEthPrice += requestIdToResponse[_id][f].ethPrice;
}
computedEthPrice = computedEthPrice/numResponses;
このインクリメントを使うときは注意が必要でpublicでの警戒レベルと同じ程度の警戒を行いSafeMathを使うようにする。以前述べたように型制限をoverflowする可能性がある。
using SafeMath for uint256;
uint256 test = 4;
test = test.div(2); // test now equals 2
test = test.add(5); // test now equals 7
このようにすれば事前にsafemathが型の観点からrequireをかけてくれて、ダメなものは弾いてくれる。