一、前言
本文继续前文的讲解。在前文中我们介绍了区块链中由公开变量做种子而引起的安全问题;有些合约使用区块哈希作为变量并将其放入合约函数中作为某种读博游戏的判定依旧。
由于这些随机数并非真正的“随机”,所以其安全隐患也是巨大的。本文我们继续介绍四种随机数漏洞类型。
二、基于区块哈希的随机数问题
block.blockhash(block.number-1)
。
许多合约使用blockhash作为生产随机数的变量,并传入上一个区块编号。这种方法同样存在问题,攻击者可以调用相同的方法来生成该随机数。
例如下面一个合约:
/**
*Submitted for verification at Etherscan.io on 2016-04-23
*/
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.
event log(uint256);
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);
}
}
点击收藏 | 0
关注 | 1