8

Below I extracted the logic which calls a remote contract to calculate the amount of tokens to withdraw funds.

contact Example {

  mapping (address => uint256) balances;
  ERC20 token = ...; // remote token contract
  uint256 contract_eth_value = ...;

  ...

  function () payable {
    // User's address.
    address user = msg.sender;
    // Retrieve current token balance of contract.
    // HERE !!!!!!!!!!!!!!!!!!!!!!!!!
    uint256 contract_token_balance = token.balanceOf(address(this));
    // Disallow token withdrawals if there are no tokens to withdraw.
    uint256 tokens_to_withdraw = (balances[user] * contract_token_balance) / contract_eth_value;
    // Update the value of tokens currently held by the contract.
    contract_eth_value -= balances[user];
    // Update the user's balance prior to sending to prevent recursive call.
    balances[user] = 0;
    // Send the funds.  Throws on failure to prevent loss of funds.
    if(!token.transfer(user, tokens_to_withdraw)) throw;
  }
}

I've searched solidity documentation for an answer but with no success. What I'm wondering or better what I'm afraid about is that when a call to a remote contract is executed, the contract function looses atomicity. If what I'm saying is true then the call above could lead to race conditions and thus unexpected results. This case would certainly be non-atomic in some distributed databases but I'm not sure if that also stands for Ethereum-based applications (contracts).

I would appreciate if an expert could take some time and explain this situation in details. A link to the official documentation/statement on that is also welcome.

UPDATE: I found a similar debate here

4 Answers 4

12

All transactions in Ethereum are run serially. Just one after another. Everything your transaction does, including calling from one contract to another, happens within the context of your transaction and nothing else runs until your contract is done.

So race conditions are totally not a concern. You can call balanceOf() on another contract, put the result in a local variable, and use it with no worries that the balance in the other contract will change before you're done.

6
  • Thanks for the answer! So you are saying that the whole Ethereum system stops and waits for a single transaction to be mined/finished? So, one transaction processing at a time, system wide? If so then I understand that the retrieved token balance in the example above will be valid. Can you confirm that? Can you please point me to some official documentation which explains the execution as you explained? Thanks!
    – xpepermint
    Sep 6, 2017 at 8:00
  • 1
    Miners put transactions into blocks in whatever order they like. After blocks get valid hashes, all full nodes run the transactions in them, in the same order, one at a time. For official documentation see the white paper: github.com/ethereum/wiki/wiki/… Sep 6, 2017 at 19:18
  • And for the really official documentation see the yellow paper, which is a complete formal spec: github.com/ethereum/yellowpaper Sep 6, 2017 at 19:20
  • 1
    @DennisPeterson Instruction inside a smart contract are run serially. But can several transaction in the same block be run in parallel ? May 25, 2018 at 15:46
  • Not with today's Ethereum, pretty much the whole point of the blockchain is to put transactions in order one after another. Each block has the transactions in a definite order. Later with sharding, the transactions within a shard will run one after another, but the different shards will run in parallel. May 26, 2018 at 20:30
8

Strictly speaking race conditions are not a problem within an Ethereum transaction: The entire transaction is atomic, no other transaction can be inserted into the control flow while it's running, and the whole thing can be rolled back on error.

You do of course need to think about race conditions between things that happen between external calls to a transaction, for example when you are checking contract state using a JavaScript app and deciding what further transactions to send based on the results.

However, what you do need to worry about is reentrancy. When you call another contract, it can do whatever it likes - including calling back into functions in your contract. Calls from contracts that your contract called all form part of the same transaction, and they can insert themselves into the control flow and cause changes to the state of your contract, before your function has finished running. See this explanation in the ConsenSys smart contract best practices document for details.

2
  • 2
    Instruction inside a smart contract are run serially. But can several transaction in the same block be run in parallel ? May 25, 2018 at 15:46
  • No, transactions are run strictly in series. May 25, 2018 at 22:03
1

As others answers have correctly discussed, race conditions in individual transactions aren't a concern in Ethereum.

However, interactions with contracts that require multiple transactions are susceptible to race conditions. For example, all ERC20 tokens are vulnerable to race conditions in token price changes as well as transfers/approvals. This is because transactions are not guaranteed to be mined in the order that they are submitted to the blockchain.

The set of transactions included in a block is a function of the transactions' gas price. Moreover, miners have the freedom to order the transactions within a block however they choose. This allows an attacker to attempt to front-run a contract operation composed of multiple transactions by submitting another transaction with a high gas cost that gets mined before the target's contract operation is completed. A malicious miner can even reorder the transactions in the block to ensure that its race condition transaction is included in the correct sequence.

For more information, as well as other examples of common smart contract vulnerabilities, consult this GitHub repo:

https://github.com/trailofbits/not-so-smart-contracts/tree/master/race_condition

1
  • This should be the correct answer. Dec 1, 2018 at 14:36
0

But in this paper, there is an example of a concurrency problem and a race condition in one smart contract by calling two concurrent transactions, creating a race condition, and a kind of attack scenario is introduced. Any idea about the example in this paper, quoted as the following:

"Published on [10], the Safe Remote Purchase smart contract contains 99 lines of source codes and aims to support safe and reliable transaction operation of untrusted buyers and sellers on the decentralized e-commerce platform. For convenience to analyze, in this smart contract we assume that there are only three accounts including the buyer, the seller and the contract where the contract is the third party. We firstly represent value as the price of the item involved in a single order. Then the contract is expected to work as follows. In the constructor function served for initialization, the account who sends the required ethers (2value as the guaranty) to the contract is considered as the seller. If the constructor function executes successfully, the state of the order becomes created. Then the seller waits for the purchase confirmation from buyer. Before the buyer confirms to purchase, the seller has the opportunity to call the abort() function to abort the order, then the guaranty will return to the seller’s account after the state of the order becomes inactive. The account who invokes the confirmPurchase() function is the buyer. As stated in confirmPurchase() function, only if the buyer successfully sends the deposit (2value) to the contract can the state of the order become locked, which finishes the confirmation procedure. The transaction will proceed smoothly until the buyer receives the item. At last, the buyer calls the confirmReceived() function. In this function, the state of the order becomes inactive, then 1value ethers are transferred back to the buyer account, and finally the remaining 3value ethers in the contract account are fully returned to the seller account."

enter image description here

"Figure 2 shows the flowchart of normal transaction described above. From the perspective of sequential programs, the above content is conformed to the expected design purpose of Safe Remote Purchase contract. However, for a concurrent program, the problems of data race may bring about undesired behaviors that can be exploited by adversaries, which is exactly the motivation of this study. As shown in Fig. 3, the vulnerability we have discovered lies in the line order of line 72, which sends Ethers to the contract account, and line 77,which changes the order state into locked. In the course of the executed line 72 and non-executed line 77, the order state is still created. Hence, in this specified period, the seller is allowed to call the abort() function. Once abort() is invoked, the state of the order becomes aborted and the total balance of the contract’s account, containing the guaranty from the seller and the deposit from the buyer, will be transferred to the seller’s account."

enter image description here

references:

1- "Formal Verification of Smart Contracts from the Perspective of Concurrency"

https://link.springer.com/chapter/10.1007/978-3-030-05764-0_4

2- Safe Remote Purchase contract. https://solidity.readthedocs.io/en/v0.4.24/ solidity-by-example.htmlsafe-remote-purchase

4
  • "In the course of the executed line 72 and non-executed line 77, the order state is still created. Hence, in this specified period, the seller is allowed to call the abort() function." Here they claim the seller can call the abort function, but it's not possible, so all they reasoning falls apart. My guess is that the authors didn't know how ethereum works, and just assumed that concurrency exist.
    – 0xSanson
    Mar 5 at 23:14
  • Thanks a lot for your comment, could you please explain more?
    – Atefeh
    Mar 7 at 0:17
  • The other answers here already explain it. Every transaction is run atomically in the order is included in the block. You can't run another function in the middle.
    – 0xSanson
    Mar 7 at 10:49
  • Indeed, transactions within a single block in Ethereum execute sequentially. However, I am wondering if exploring avenues like sharding introduces possibilities for concurrency or not. because it is discussed in various papers, including the one mentioned, I need to make sure if they make an absolutely wrong assumption or in some rare cases it can be possible. Could someone shed light on potential vulnerabilities associated with introducing such concurrency? Also, I'm curious about if other blockchains have this issue except Ethereum. Any insights would be greatly appreciated.
    – Atefeh
    Mar 22 at 22:31

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.