Флаг повторного входа без SSTORE?

Можно ли создать мьютекс, предотвращающий повторный вход, без использования операции SSTORE?

Я хотел бы создать флаг, чтобы предотвратить повторный вход в мой контракт, который будет вызывать другие контракты. Операция SSTOREзаписи в хранилище стоит дорого. Есть ли способ сохранить этот мьютекс в памяти или сделать это с помощью менее затратных операций?

Пишу на Solidity, но интересна любая версия. Похоже, эта проблема будет существовать для случаев использования, выходящих за рамки защиты от повторного входа. Ограничение комиссий путем сохранения их только комиссией Gsresetпосле инициализации — это самое дешевое, что я могу сейчас придумать, но, возможно, я что-то упускаю.

Ответы (2)

Есть ли способ сохранить этот мьютекс в памяти?

Память и стек в EVM изолированы для вызовов вложенных сообщений, что означает, что нет никакого способа получить доступ к мьютексу памяти, установленному во внешнем вызове/транзакции.

сделать это с менее дорогими операциями?

Единственный способ, который я могу придумать, - это использовать хранилище, поскольку, в отличие от памяти и стека, хранилище используется совместно для одного и того же контракта для разных вызовов сообщений. Обратите внимание, что при сбросе флага мьютекса хранилище сбрасывается, что означает, что часть газа будет возвращена. В желтой бумаге Gsclear = 15000 определяется:

Возврат предоставлен (добавлен в счетчик возврата), когда значение хранилища установлено равным нулю с ненулевого.

Так что в лучшем случае вам нужно будет платить только за Gsset - Gsclear = 5000газ. Я сказал «в лучшем случае», потому что возмещение ограничено максимум половиной (округленной в меньшую сторону) от общей использованной суммы, как указано в разделе 6.2. Ограничение будет не менее 10000, так как вам всегда нужно сначала установить мьютекс, который стоит 20000. Так что в худшем случае вы заплатите 10000 газа.

ИМХО, использование хранилища неизбежно, поэтому SSTOREкод операции :( Возьмем в качестве примера этот простой контракт. Он использует мьютексы для предотвращения повторного входа в withdrawBalance()функцию.

pragma solidity ^0.4.16;

contract EtherBank{
    mapping(address => uint) public userBalances;
    mapping(address => bool) public withdrawMutex;

    function getBalance(address user) constant returns(uint) {  
        return userBalances[user];
    }

    function addToBalance() {  
        userBalances[msg.sender] += msg.value;
    }

    function withdrawBalance() {  
        if ( withdrawMutex[msg.sender] == true) { throw; }
        withdrawMutex[msg.sender] = true;
        uint amountToWithdraw = userBalances[msg.sender];
        if (amountToWithdraw > 0) {
            if (!(msg.sender.send(amountToWithdraw))) { throw; }
         }
        userBalances[msg.sender] = 0;
        withdrawMutex[msg.sender] = false;
    }
}

Это простой банковский контракт, взятый из этого блога , который настоятельно рекомендуется прочитать, если вас интересуют баги повторного входа в смарт-контракты. Как показывает этот пример withdrawMutex, хранится в постоянном хранилище контракта.

Вы просто не можете хранить свой мьютекс в памяти, потому что он будет сбрасываться после каждого вызова контракта. Мьютекс должен сохранять свое состояние, возможно, для многих вызовов контракта, подразумевая, что он должен храниться в постоянном хранилище. Плохая новость для использования газа :(

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

function withdrawBalance() {  
        uint amountToWithdraw = userBalances[msg.sender];
        userBalances[msg.sender] = 0;
        if (amountToWithdraw > 0) {
        if (!(msg.sender.send(amountToWithdraw))) { throw; }
    }

Так что просто получить ваш заказ правильно! ;)