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