smartbugs-curated/dataset/bad_randomness/etheraffle.sol

175 lines
5.0 KiB
Solidity

/*
* @article: https://blog.positive.com/predicting-random-numbers-in-ethereum-smart-contracts-e5358c6b8620
* @source: https://etherscan.io/address/0xcC88937F325d1C6B97da0AFDbb4cA542EFA70870#code
* @vulnerable_at_lines: 49,99,101,103,114,158
* @author: -
*/
pragma solidity ^0.4.16;
contract Ethraffle_v4b {
struct Contestant {
address addr;
uint raffleId;
}
event RaffleResult(
uint raffleId,
uint winningNumber,
address winningAddress,
address seed1,
address seed2,
uint seed3,
bytes32 randHash
);
event TicketPurchase(
uint raffleId,
address contestant,
uint number
);
event TicketRefund(
uint raffleId,
address contestant,
uint number
);
// Constants
uint public constant prize = 2.5 ether;
uint public constant fee = 0.03 ether;
uint public constant totalTickets = 50;
uint public constant pricePerTicket = (prize + fee) / totalTickets; // Make sure this divides evenly
address feeAddress;
// Other internal variables
bool public paused = false;
uint public raffleId = 1;
// <yes> <report> BAD_RANDOMNESS
uint public blockNumber = block.number;
uint nextTicket = 0;
mapping (uint => Contestant) contestants;
uint[] gaps;
// Initialization
function Ethraffle_v4b() public {
feeAddress = msg.sender;
}
// Call buyTickets() when receiving Ether outside a function
function () payable public {
buyTickets();
}
function buyTickets() payable public {
if (paused) {
msg.sender.transfer(msg.value);
return;
}
uint moneySent = msg.value;
while (moneySent >= pricePerTicket && nextTicket < totalTickets) {
uint currTicket = 0;
if (gaps.length > 0) {
currTicket = gaps[gaps.length-1];
gaps.length--;
} else {
currTicket = nextTicket++;
}
contestants[currTicket] = Contestant(msg.sender, raffleId);
TicketPurchase(raffleId, msg.sender, currTicket);
moneySent -= pricePerTicket;
}
// Choose winner if we sold all the tickets
if (nextTicket == totalTickets) {
chooseWinner();
}
// Send back leftover money
if (moneySent > 0) {
msg.sender.transfer(moneySent);
}
}
function chooseWinner() private {
// <yes> <report> BAD_RANDOMNESS
address seed1 = contestants[uint(block.coinbase) % totalTickets].addr;
// <yes> <report> BAD_RANDOMNESS
address seed2 = contestants[uint(msg.sender) % totalTickets].addr;
// <yes> <report> BAD_RANDOMNESS
uint seed3 = block.difficulty;
bytes32 randHash = keccak256(seed1, seed2, seed3);
uint winningNumber = uint(randHash) % totalTickets;
address winningAddress = contestants[winningNumber].addr;
RaffleResult(raffleId, winningNumber, winningAddress, seed1, seed2, seed3, randHash);
// Start next raffle
raffleId++;
nextTicket = 0;
// <yes> <report> BAD_RANDOMNESS
blockNumber = block.number;
// gaps.length = 0 isn't necessary here,
// because buyTickets() eventually clears
// the gaps array in the loop itself.
// Distribute prize and fee
winningAddress.transfer(prize);
feeAddress.transfer(fee);
}
// Get your money back before the raffle occurs
function getRefund() public {
uint refund = 0;
for (uint i = 0; i < totalTickets; i++) {
if (msg.sender == contestants[i].addr && raffleId == contestants[i].raffleId) {
refund += pricePerTicket;
contestants[i] = Contestant(address(0), 0);
gaps.push(i);
TicketRefund(raffleId, msg.sender, i);
}
}
if (refund > 0) {
msg.sender.transfer(refund);
}
}
// Refund everyone's money, start a new raffle, then pause it
function endRaffle() public {
if (msg.sender == feeAddress) {
paused = true;
for (uint i = 0; i < totalTickets; i++) {
if (raffleId == contestants[i].raffleId) {
TicketRefund(raffleId, contestants[i].addr, i);
contestants[i].addr.transfer(pricePerTicket);
}
}
RaffleResult(raffleId, totalTickets, address(0), address(0), address(0), 0, 0);
raffleId++;
nextTicket = 0;
// <yes> <report> BAD_RANDOMNESS
blockNumber = block.number;
gaps.length = 0;
}
}
function togglePause() public {
if (msg.sender == feeAddress) {
paused = !paused;
}
}
function kill() public {
if (msg.sender == feeAddress) {
selfdestruct(feeAddress);
}
}
}