Solidity – Does ReentrancyGuard Lock Every Address During nonReentrant Function Call?

solidity

I am looking to secure a function within a contract from reEntrancy attacks, by attaching the nonReentrant modifier to it.

function myFunction(...) public nonRentrant {
// ...
}

My question is:

a) Does the lock status (_status) applies to all addresses potentially interacting with the contract?
or

b) Does it only applies only to the address interacting with the nonReentrant function?

Basically, what happen if two addresses make a call to the nonReentrant function at the same time.

I understand that it brings the function to a “lock state”, by switching the _status variable via the modifier. But what happen if two addresses (= 2 users) interact with the function at the same time?

modifier nonReentrant() {
    // On the first call to nonReentrant, _notEntered will be true
    require(_status != _ENTERED, "ReentrancyGuard: reentrant call");

    // Any calls to nonReentrant after this point will fail
    _status = _ENTERED;

    _;   // <== what happen here if the function get called by another address here while still executing

    // By storing the original value once again, a refund is triggered (see
    // https://eips.ethereum.org/EIPS/eip-2200)
    _status = _NOT_ENTERED;
}

Let’s imagine 2 users, A and B.

user A calls myFunction()

1.1 ==> the function gets locked via the modifier

1.2 ==> the function starts execution (running the code within)

user B calls myFunction() (while the contract is still executing call made by user A)

Will user B be able to execute the function?
Or does he has to wait for the call from user A to finish? (because _status = _ENTERED)?
In the 2nd scenario, I would imagine that my contract should have to keep track of all the addresses currently executing call via a mapping, so my code should look like below:

mapping (address => bool) private _locks;

modifier nonReentrant {
require(_locks[msg.sender] != true, "ReentrancyGuard: reentrant call");

_locks[msg.sender] = true;

_;

_locks[msg.sender] = false;
}

function myFunction(...) public nonReentrant {
   // ...
}

Best Answer

Your locking example will not happen, every transaction is processed sequentially on the miner's side and then ordered in the blocks.

One miner cannot process A and B at the same time. And even if it could, it shouldn't, because they could both alter the state of the same contract at the same time.

You won't encounter this kind of race condition.

So to answer your specific questions :

a) Does the lock status (_status) applies to all addresses potentially interacting with the contract?

b) Does it only applies only to the address interacting with the nonReentrant function?

Technically, yes, _status is a global variable. But as transactions can only be processed sequentially in practice it's more of a per-transaction lock.

Related Topic