IMHO it is inevitable to use storage, therefore the SSTORE
opcode :( Let's take this simple contract as an example. It uses mutexes to prevent reentrancy in the withdrawBalance()
function.
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;
}
}
This is a simple banking contract taken from this blog, which is highly recommended to read if you're interested in reentrancy bugs in smart contracts. As this example shows withdrawMutex
is stored in the permanent storage of the contract.
You simply can not have your mutex stored in memory, because it would be flushed out after each invocation of the contract. The mutex should hold its state possibly for many invocations of the contract, implying it should be stored in the permanent storage. Bad news for gas usage :(
However you could prevent reentrancy bugs using different strategies, like the one mentioned in the blog post. This simple tactic is much more gas-cost friendly:
function withdrawBalance() {
uint amountToWithdraw = userBalances[msg.sender];
userBalances[msg.sender] = 0;
if (amountToWithdraw > 0) {
if (!(msg.sender.send(amountToWithdraw))) { throw; }
}
So just get your ordering correct! ;)