Возможно, у этого контракта есть некоторые проблемы, потому что я вижу что-то странное в своем журнале контрактов. Кто-то выполняет еще одну контрактную функцию, чтобы играть с этим мини-приложением для азартных игр, и за 2 игры он очищает все деньги xD.
Заранее спасибо :)
pragma solidity ^0.4.11;
contract MetaCoin {
event FlipCoinEvent(
uint value,
address owner
);
event PlaySlotEvent(
uint value,
address owner
);
function() public payable {}
function flipCoin() public payable {
assert(msg.value < 100000000000000000);
uint value = (block.timestamp + uint(block.blockhash(block.number-1)))%100 + 1;
if (value > 55){
msg.sender.transfer(msg.value * 2);
}
FlipCoinEvent(value, msg.sender);
}
function playSlot() public payable {
require(msg.value < 100000000000000000);
uint r = (block.timestamp + uint(block.blockhash(block.number-1)))%100 + 1;
if(r >0 && r<3){
PlaySlotEvent(3,msg.sender);
msg.sender.transfer(msg.value * 12);
}else if(r >3 && r<6){
PlaySlotEvent(2,msg.sender);
msg.sender.transfer(msg.value * 6);
}else if(r >6 && r<9){
PlaySlotEvent(1,msg.sender);
msg.sender.transfer(msg.value * 3);
}else{
PlaySlotEvent(0,msg.sender);
}
}
function getBalance() public constant returns(uint bal) {
bal = this.balance;
return bal;
}
}
Согласно документации , block.timestamp
, block.blockhash
и block.number
остаются неизменными до тех пор, пока следующий блок не будет добавлен в цепочку блоков. Поскольку время блока составляет от 15 до 17 секунд , легко можно «получить правильный блок», чтобы атаковать ваш смарт-контракт.
Можно просто написать смарт-контракт с функцией getChances()
, которая проверяет текущее значение (block.timestamp + uint(block.blockhash(block.number-1)))%100 + 1
и, если оно находится в правильном диапазоне (например, < 3), вызывает playSlot()
функцию вашего контракта.
Затем функцию getChances()
просто нужно вызывать оракулом каждый раз, когда новый блок добавляется в цепочку блоков.
Никакой магии.
Надеюсь, это поможет
РЕДАКТИРОВАТЬ :
Я сократил ваш смарт-контракт и написал еще один, чтобы лучше показать, что я имел в виду выше:
pragma solidity ^0.4.11;
contract MetaCoin {
event PlaySlotEvent(
uint value,
address owner
);
function playSlot() public returns (uint){
uint r = (block.timestamp + uint(block.blockhash(block.number-1)))%100 + 1;
PlaySlotEvent(r,msg.sender);
}
}
contract Test {
function getCurrentR() constant returns (uint) {
return (block.timestamp + uint(block.blockhash(block.number-1)))%100 + 1;
}
}
Если вы попробуете это в Remix , просто позвоните getCurrentR()
и сразу после этого позвоните playSlot()
. Вы увидите, что значение отличается только количеством секунд, которые вы ждете перед вызовом playSlot()
. Поэтому игрок может проверить, r
прежде чем начать играть...
Ваша лотерея не совсем случайна.
Любое решение пользователя, влияющее на результат, дает этому пользователю несправедливое преимущество. Примеры включают:
- Использование хэша блока, метки времени или другого значения, определенного майнером. Имейте в виду, что у майнера есть выбор, публиковать блок или нет, поэтому у него может быть один шанс на приз за каждый добытый блок.
Смотрите этот ответ для более подробного объяснения: Как я могу безопасно сгенерировать случайное число в своем смарт-контракте? .
утверждать(msg.value < 100000000000000000); требуют(msg.value < 100000000000000000);
Это значит, что я тоже могу играть с 0 вэй и все равно выигрывать, верно?
Может ты имел ввиду >?
Для генерации случайного числа вы используете временную метку текущего блока и хэш предыдущего блока. Хэш предыдущего блока известен, поэтому он не добавляет случайности. Текущая временная метка блока известна майнеру, поэтому он может вызывать flipCoin всякий раз, когда временная метка ему подходит.
Обратите внимание, что вы не можете получить текущий хэш блока в Solidity. Как указано в документах:
block.blockhash(uint blockNumber) возвращает (bytes32): хэш данного блока - работает только для 256 самых последних блоков, исключая текущий
Вместо этого вы должны разделить игру на 2 этапа:
flipCoin
вы записываете block.number
и msg.value
для отправителя.withdrawReward
если хэш блока для записанного номера блока делает его победителем. Здесь вы также должны подтвердить, что номер записанного блока меньше текущего номера блока.В этом случае можно использовать блокчейн, так как вознаграждение в размере 0,1 эфира намного ниже, чем вознаграждение за блок. Для генерации случайного числа вы также должны включить адрес отправителя в начальное число, в противном случае злоумышленник может отправить несколько запросов flipCoin, которые в сумме превысят вознаграждение за блок, что даст стимул для манипулирования хешем блока и даст возможность для мошенничества.
Хьюгофрейр
Джоффи
block.number
вас получается номер самого последнего блока. Такblock.number-1
же как и номер блока, добавленного перед текущим. Поэтому сblock.blockhash(block.number-1)
вами получается что-то, основанное на информации прошлого . Я что-то упускаю?Хьюгофрейр
Джоффи
block.number-1
что имеет смысл. Извините за недопонимание. Но даже если вы вычисляете число на основе уже имеющихся данных, верно?Хьюгофрейр
Джоффи
Хьюгофрейр