222 lines
8.6 KiB
Solidity
222 lines
8.6 KiB
Solidity
|
/*
|
||
|
* @source: etherscan.io
|
||
|
* @author: -
|
||
|
* @vulnerable_at_lines: 162,175,180,192
|
||
|
*/
|
||
|
|
||
|
pragma solidity ^0.4.9;
|
||
|
|
||
|
contract TownCrier {
|
||
|
struct Request { // the data structure for each request
|
||
|
address requester; // the address of the requester
|
||
|
uint fee; // the amount of wei the requester pays for the request
|
||
|
address callbackAddr; // the address of the contract to call for delivering response
|
||
|
bytes4 callbackFID; // the specification of the callback function
|
||
|
bytes32 paramsHash; // the hash of the request parameters
|
||
|
}
|
||
|
|
||
|
event Upgrade(address newAddr);
|
||
|
event Reset(uint gas_price, uint min_fee, uint cancellation_fee);
|
||
|
event RequestInfo(uint64 id, uint8 requestType, address requester, uint fee, address callbackAddr, bytes32 paramsHash, uint timestamp, bytes32[] requestData); // log of requests, the Town Crier server watches this event and processes requests
|
||
|
event DeliverInfo(uint64 requestId, uint fee, uint gasPrice, uint gasLeft, uint callbackGas, bytes32 paramsHash, uint64 error, bytes32 respData); // log of responses
|
||
|
event Cancel(uint64 requestId, address canceller, address requester, uint fee, int flag); // log of cancellations
|
||
|
|
||
|
address public constant SGX_ADDRESS = 0x18513702cCd928F2A3eb63d900aDf03c9cc81593;// address of the SGX account
|
||
|
|
||
|
uint public GAS_PRICE = 5 * 10**10;
|
||
|
uint public MIN_FEE = 30000 * GAS_PRICE; // minimum fee required for the requester to pay such that SGX could call deliver() to send a response
|
||
|
uint public CANCELLATION_FEE = 25000 * GAS_PRICE; // charged when the requester cancels a request that is not responded
|
||
|
|
||
|
uint public constant CANCELLED_FEE_FLAG = 1;
|
||
|
uint public constant DELIVERED_FEE_FLAG = 0;
|
||
|
int public constant FAIL_FLAG = -2 ** 250;
|
||
|
int public constant SUCCESS_FLAG = 1;
|
||
|
|
||
|
bool public killswitch;
|
||
|
|
||
|
bool public externalCallFlag;
|
||
|
|
||
|
uint64 public requestCnt;
|
||
|
uint64 public unrespondedCnt;
|
||
|
Request[2**64] public requests;
|
||
|
|
||
|
int public newVersion = 0;
|
||
|
|
||
|
// Contracts that receive Ether but do not define a fallback function throw
|
||
|
// an exception, sending back the Ether (this was different before Solidity
|
||
|
// v0.4.0). So if you want your contract to receive Ether, you have to
|
||
|
// implement a fallback function.
|
||
|
function () {}
|
||
|
|
||
|
function TownCrier() public {
|
||
|
// Start request IDs at 1 for two reasons:
|
||
|
// 1. We can use 0 to denote an invalid request (ids are unsigned)
|
||
|
// 2. Storage is more expensive when changing something from zero to non-zero,
|
||
|
// so this means the first request isn't randomly more expensive.
|
||
|
requestCnt = 1;
|
||
|
requests[0].requester = msg.sender;
|
||
|
killswitch = false;
|
||
|
unrespondedCnt = 0;
|
||
|
externalCallFlag = false;
|
||
|
}
|
||
|
|
||
|
function upgrade(address newAddr) {
|
||
|
if (msg.sender == requests[0].requester && unrespondedCnt == 0) {
|
||
|
newVersion = -int(newAddr);
|
||
|
killswitch = true;
|
||
|
Upgrade(newAddr);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
function reset(uint price, uint minGas, uint cancellationGas) public {
|
||
|
if (msg.sender == requests[0].requester && unrespondedCnt == 0) {
|
||
|
GAS_PRICE = price;
|
||
|
MIN_FEE = price * minGas;
|
||
|
CANCELLATION_FEE = price * cancellationGas;
|
||
|
Reset(GAS_PRICE, MIN_FEE, CANCELLATION_FEE);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
function suspend() public {
|
||
|
if (msg.sender == requests[0].requester) {
|
||
|
killswitch = true;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
function restart() public {
|
||
|
if (msg.sender == requests[0].requester && newVersion == 0) {
|
||
|
killswitch = false;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
function withdraw() public {
|
||
|
if (msg.sender == requests[0].requester && unrespondedCnt == 0) {
|
||
|
if (!requests[0].requester.call.value(this.balance)()) {
|
||
|
throw;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
function request(uint8 requestType, address callbackAddr, bytes4 callbackFID, uint timestamp, bytes32[] requestData) public payable returns (int) {
|
||
|
if (externalCallFlag) {
|
||
|
throw;
|
||
|
}
|
||
|
|
||
|
if (killswitch) {
|
||
|
externalCallFlag = true;
|
||
|
if (!msg.sender.call.value(msg.value)()) {
|
||
|
throw;
|
||
|
}
|
||
|
externalCallFlag = false;
|
||
|
return newVersion;
|
||
|
}
|
||
|
|
||
|
if (msg.value < MIN_FEE) {
|
||
|
externalCallFlag = true;
|
||
|
// If the amount of ether sent by the requester is too little or
|
||
|
// too much, refund the requester and discard the request.
|
||
|
if (!msg.sender.call.value(msg.value)()) {
|
||
|
throw;
|
||
|
}
|
||
|
externalCallFlag = false;
|
||
|
return FAIL_FLAG;
|
||
|
} else {
|
||
|
// Record the request.
|
||
|
uint64 requestId = requestCnt;
|
||
|
requestCnt++;
|
||
|
unrespondedCnt++;
|
||
|
|
||
|
bytes32 paramsHash = sha3(requestType, requestData);
|
||
|
requests[requestId].requester = msg.sender;
|
||
|
requests[requestId].fee = msg.value;
|
||
|
requests[requestId].callbackAddr = callbackAddr;
|
||
|
requests[requestId].callbackFID = callbackFID;
|
||
|
requests[requestId].paramsHash = paramsHash;
|
||
|
|
||
|
// Log the request for the Town Crier server to process.
|
||
|
RequestInfo(requestId, requestType, msg.sender, msg.value, callbackAddr, paramsHash, timestamp, requestData);
|
||
|
return requestId;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
function deliver(uint64 requestId, bytes32 paramsHash, uint64 error, bytes32 respData) public {
|
||
|
if (msg.sender != SGX_ADDRESS ||
|
||
|
requestId <= 0 ||
|
||
|
requests[requestId].requester == 0 ||
|
||
|
requests[requestId].fee == DELIVERED_FEE_FLAG) {
|
||
|
// If the response is not delivered by the SGX account or the
|
||
|
// request has already been responded to, discard the response.
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
uint fee = requests[requestId].fee;
|
||
|
if (requests[requestId].paramsHash != paramsHash) {
|
||
|
// If the hash of request parameters in the response is not
|
||
|
// correct, discard the response for security concern.
|
||
|
return;
|
||
|
} else if (fee == CANCELLED_FEE_FLAG) {
|
||
|
// If the request is cancelled by the requester, cancellation
|
||
|
// fee goes to the SGX account and set the request as having
|
||
|
// been responded to.
|
||
|
// <yes> <report> UNCHECKED_LL_CALLS
|
||
|
SGX_ADDRESS.send(CANCELLATION_FEE);
|
||
|
requests[requestId].fee = DELIVERED_FEE_FLAG;
|
||
|
unrespondedCnt--;
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
requests[requestId].fee = DELIVERED_FEE_FLAG;
|
||
|
unrespondedCnt--;
|
||
|
|
||
|
if (error < 2) {
|
||
|
// Either no error occurs, or the requester sent an invalid query.
|
||
|
// Send the fee to the SGX account for its delivering.
|
||
|
// <yes> <report> UNCHECKED_LL_CALLS
|
||
|
SGX_ADDRESS.send(fee);
|
||
|
} else {
|
||
|
// Error in TC, refund the requester.
|
||
|
externalCallFlag = true;
|
||
|
// <yes> <report> UNCHECKED_LL_CALLS
|
||
|
requests[requestId].requester.call.gas(2300).value(fee)();
|
||
|
externalCallFlag = false;
|
||
|
}
|
||
|
|
||
|
uint callbackGas = (fee - MIN_FEE) / tx.gasprice; // gas left for the callback function
|
||
|
DeliverInfo(requestId, fee, tx.gasprice, msg.gas, callbackGas, paramsHash, error, respData); // log the response information
|
||
|
if (callbackGas > msg.gas - 5000) {
|
||
|
callbackGas = msg.gas - 5000;
|
||
|
}
|
||
|
|
||
|
externalCallFlag = true;
|
||
|
// <yes> <report> UNCHECKED_LL_CALLS
|
||
|
requests[requestId].callbackAddr.call.gas(callbackGas)(requests[requestId].callbackFID, requestId, error, respData); // call the callback function in the application contract
|
||
|
externalCallFlag = false;
|
||
|
}
|
||
|
|
||
|
function cancel(uint64 requestId) public returns (int) {
|
||
|
if (externalCallFlag) {
|
||
|
throw;
|
||
|
}
|
||
|
|
||
|
if (killswitch) {
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
uint fee = requests[requestId].fee;
|
||
|
if (requests[requestId].requester == msg.sender && fee >= CANCELLATION_FEE) {
|
||
|
// If the request was sent by this user and has money left on it,
|
||
|
// then cancel it.
|
||
|
requests[requestId].fee = CANCELLED_FEE_FLAG;
|
||
|
externalCallFlag = true;
|
||
|
if (!msg.sender.call.value(fee - CANCELLATION_FEE)()) {
|
||
|
throw;
|
||
|
}
|
||
|
externalCallFlag = false;
|
||
|
Cancel(requestId, msg.sender, requests[requestId].requester, requests[requestId].fee, 1);
|
||
|
return SUCCESS_FLAG;
|
||
|
} else {
|
||
|
Cancel(requestId, msg.sender, requests[requestId].requester, fee, -1);
|
||
|
return FAIL_FLAG;
|
||
|
}
|
||
|
}
|
||
|
}
|