Вы можете увидеть какие-либо уязвимые места в этом контракте?

Возможно, у этого контракта есть некоторые проблемы, потому что я вижу что-то странное в своем журнале контрактов. Кто-то выполняет еще одну контрактную функцию, чтобы играть с этим мини-приложением для азартных игр, и за 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;
  }

}

Ответы (4)

Согласно документации , 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прежде чем начать играть...

Но uint(block.blockhash(block.number-1)) невозможно предсказать, верно?
Я никогда не использовал эти команды в своих проектах. Но насколько я понимаю определение у block.numberвас получается номер самого последнего блока. Так block.number-1же как и номер блока, добавленного перед текущим. Поэтому с block.blockhash(block.number-1)вами получается что-то, основанное на информации прошлого . Я что-то упускаю?
если я читаю block.blockhash(block.number) каждый раз выдает ноль, потому что текущий блок не добывается правильно?
Хорошо. Я только что попробовал, и вы правы в этом вопросе. Так block.number-1что имеет смысл. Извините за недопонимание. Но даже если вы вычисляете число на основе уже имеющихся данных, верно?
Да, но когда вы выполняете транзакцию, родительский хеш уже впереди или нет?
Честно говоря, я не уверен. Но я думаю, что эти вычисления выполняются в EVM и, следовательно, до того, как транзакция действительно будет добыта.
Да, я также создаю один контракт для атаки на игровой слот, и я могу выполнить функцию, чтобы увидеть, является ли результат победителем, и сразу после этого выполнить игровой слот. И все транзакции происходят в одном и том же блоке в одно и то же время и с одним и тем же результатом. В нем отсутствует sha3 и начальное значение, а не публичный контракт, и мне кажется, что оно безопасно, потому что никто не может видеть начальное значение. вы согласны?

Ваша лотерея не совсем случайна.

Любое решение пользователя, влияющее на результат, дает этому пользователю несправедливое преимущество. Примеры включают:

  1. Использование хэша блока, метки времени или другого значения, определенного майнером. Имейте в виду, что у майнера есть выбор, публиковать блок или нет, поэтому у него может быть один шанс на приз за каждый добытый блок.

Смотрите этот ответ для более подробного объяснения: Как я могу безопасно сгенерировать случайное число в своем смарт-контракте? .

утверждать(msg.value < 100000000000000000); требуют(msg.value < 100000000000000000);

Это значит, что я тоже могу играть с 0 вэй и все равно выигрывать, верно?

Может ты имел ввиду >?

да, но вы выигрываете 0 * 2 = 0, верно?
хм, ты прав

Для генерации случайного числа вы используете временную метку текущего блока и хэш предыдущего блока. Хэш предыдущего блока известен, поэтому он не добавляет случайности. Текущая временная метка блока известна майнеру, поэтому он может вызывать flipCoin всякий раз, когда временная метка ему подходит.

Обратите внимание, что вы не можете получить текущий хэш блока в Solidity. Как указано в документах:

block.blockhash(uint blockNumber) возвращает (bytes32): хэш данного блока - работает только для 256 самых последних блоков, исключая текущий

Вместо этого вы должны разделить игру на 2 этапа:

  • в flipCoinвы записываете block.numberи msg.valueдля отправителя.
  • вы передаете вознаграждение , withdrawRewardесли хэш блока для записанного номера блока делает его победителем. Здесь вы также должны подтвердить, что номер записанного блока меньше текущего номера блока.

В этом случае можно использовать блокчейн, так как вознаграждение в размере 0,1 эфира намного ниже, чем вознаграждение за блок. Для генерации случайного числа вы также должны включить адрес отправителя в начальное число, в противном случае злоумышленник может отправить несколько запросов flipCoin, которые в сумме превысят вознаграждение за блок, что даст стимул для манипулирования хешем блока и даст возможность для мошенничества.