smartbugs-curated/dataset/unchecked_low_level_calls/0x89c1b3807d4c67df034fffb62...

222 lines
8.6 KiB
Solidity
Raw Normal View History

/*
* @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;
}
}
}