smartbugs-curated/dataset/bad_randomness/lucky_doubler.sol

192 lines
5.4 KiB
Solidity

/*
* @article: https://blog.positive.com/predicting-random-numbers-in-ethereum-smart-contracts-e5358c6b8620
* @source: https://etherscan.io/address/0xF767fCA8e65d03fE16D4e38810f5E5376c3372A8#code
* @vulnerable_at_lines: 127,128,129,130,132
* @author: -
*/
//added pragma version
pragma solidity ^0.4.0;
contract LuckyDoubler {
//##########################################################
//#### LuckyDoubler: A doubler with random payout order ####
//#### Deposit 1 ETHER to participate ####
//##########################################################
//COPYRIGHT 2016 KATATSUKI ALL RIGHTS RESERVED
//No part of this source code may be reproduced, distributed,
//modified or transmitted in any form or by any means without
//the prior written permission of the creator.
address private owner;
//Stored variables
uint private balance = 0;
uint private fee = 5;
uint private multiplier = 125;
mapping (address => User) private users;
Entry[] private entries;
uint[] private unpaidEntries;
//Set owner on contract creation
function LuckyDoubler() {
owner = msg.sender;
}
modifier onlyowner { if (msg.sender == owner) _; }
struct User {
address id;
uint deposits;
uint payoutsReceived;
}
struct Entry {
address entryAddress;
uint deposit;
uint payout;
bool paid;
}
//Fallback function
function() {
init();
}
function init() private{
if (msg.value < 1 ether) {
msg.sender.send(msg.value);
return;
}
join();
}
function join() private {
//Limit deposits to 1ETH
uint dValue = 1 ether;
if (msg.value > 1 ether) {
msg.sender.send(msg.value - 1 ether);
dValue = 1 ether;
}
//Add new users to the users array
if (users[msg.sender].id == address(0))
{
users[msg.sender].id = msg.sender;
users[msg.sender].deposits = 0;
users[msg.sender].payoutsReceived = 0;
}
//Add new entry to the entries array
entries.push(Entry(msg.sender, dValue, (dValue * (multiplier) / 100), false));
users[msg.sender].deposits++;
unpaidEntries.push(entries.length -1);
//Collect fees and update contract balance
balance += (dValue * (100 - fee)) / 100;
uint index = unpaidEntries.length > 1 ? rand(unpaidEntries.length) : 0;
Entry theEntry = entries[unpaidEntries[index]];
//Pay pending entries if the new balance allows for it
if (balance > theEntry.payout) {
uint payout = theEntry.payout;
theEntry.entryAddress.send(payout);
theEntry.paid = true;
users[theEntry.entryAddress].payoutsReceived++;
balance -= payout;
if (index < unpaidEntries.length - 1)
unpaidEntries[index] = unpaidEntries[unpaidEntries.length - 1];
unpaidEntries.length--;
}
//Collect money from fees and possible leftovers from errors (actual balance untouched)
uint fees = this.balance - balance;
if (fees > 0)
{
owner.send(fees);
}
}
//Generate random number between 0 & max
uint256 constant private FACTOR = 1157920892373161954235709850086879078532699846656405640394575840079131296399;
// <yes> <report> BAD_RANDOMNESS
function rand(uint max) constant private returns (uint256 result){
uint256 factor = FACTOR * 100 / max;
uint256 lastBlockNumber = block.number - 1;
uint256 hashVal = uint256(block.blockhash(lastBlockNumber));
return uint256((uint256(hashVal) / factor)) % max;
}
//Contract management
function changeOwner(address newOwner) onlyowner {
owner = newOwner;
}
function changeMultiplier(uint multi) onlyowner {
if (multi < 110 || multi > 150) throw;
multiplier = multi;
}
function changeFee(uint newFee) onlyowner {
if (fee > 5)
throw;
fee = newFee;
}
//JSON functions
function multiplierFactor() constant returns (uint factor, string info) {
factor = multiplier;
info = 'The current multiplier applied to all deposits. Min 110%, max 150%.';
}
function currentFee() constant returns (uint feePercentage, string info) {
feePercentage = fee;
info = 'The fee percentage applied to all deposits. It can change to speed payouts (max 5%).';
}
function totalEntries() constant returns (uint count, string info) {
count = entries.length;
info = 'The number of deposits.';
}
function userStats(address user) constant returns (uint deposits, uint payouts, string info)
{
if (users[user].id != address(0x0))
{
deposits = users[user].deposits;
payouts = users[user].payoutsReceived;
info = 'Users stats: total deposits, payouts received.';
}
}
function entryDetails(uint index) constant returns (address user, uint payout, bool paid, string info)
{
if (index < entries.length) {
user = entries[index].entryAddress;
payout = entries[index].payout / 1 finney;
paid = entries[index].paid;
info = 'Entry info: user address, expected payout in Finneys, payout status.';
}
}
}