0

I've come across an example of how a race condition can happen in a scenario like this that I don't understand how it could occur:

Bob creates RaceCondition(100, token). Alice trusts RaceCondition with all its tokens. Alice calls buy(150) Bob sees the transaction, and calls changePrice(300). The transaction of Bob is mined before the one of Alice and as a result, Bob received 300 tokens.

The example code has something like this:

contract RaceCondition{
    address private owner;
    uint public price;
    ERC20 token;

    function RaceCondition(uint _price, ERC20 _token)
        public 
    {
        owner = msg.sender;
        price = _price;
        token = _token;
    }

    // If the owner sees someone calls buy
    // he can call changePrice to set a new price
    // If his transaction is mined first, he can
    // receive more tokens than excepted by the new buyer
    function buy(uint new_price) payable
        public
    {
        require(msg.value >= price);

        // we assume that the RaceCondition contract
        // has enough allowance
        token.transferFrom(msg.sender, owner, price);

        price = new_price;
        owner = msg.sender;
    }

    function changePrice(uint new_price){
        require(msg.sender == owner);
        price = new_price; 
    }

}

However, I don't understand how a race condition can happen with the given example.

  1. Is the example implying that even though Bob had sent in his transaction later than Alice, Bob likely paid a higher gas fee so that his transaction gets mined before Alice's?
    • If this was the case, are we assuming that Alice's and Bob's transactions are sent to the same block which could allow this to happen? But this would mean Bob has to make his transaction within split seconds right after Alice's transaction to be in the same block and also be in time enough to have his transaction mined before hers?
  2. The example mentioned that Bob can call changePrice after Alice called buy to receive more tokens than expected. But I don't see how this can happen:
    • Assuming Bob managed to get his call to changePrice(300) to be locked in before Alice's buy(150) call, Alice's buy(150) call would throw exception because require(msg.value >= price); would return false, wouldn't it? I.e., 150 >= 300 is false, hence, reverts, wouldn't it?

1 Answer 1

2

Looks like a horrible example.

To begin with, there is zero talk about how much Ethers the users are sending with their transactions. That's what matters, because the code makes checks against msg.value. It simply looks like the writer has mixed up msg.value with the price (or new_price) parameter.

I'm not sure if you can call anything in the blockchain a race condition. I guess, maybe, you can, if two transactions try to do the same thing which only one can do. But the term "race conditions" is usually used when there is some degree of parallelism, and the blockchain has zero parallelism when executing transactions.

Anyway, if in the example there was a function which buys as many tokens as the caller can get with X Ethers, it would be a different story. Then an attacker (owner) could enter a transaction which makes the token price higher, and this transaction gets mined before the buyer's. This basically happens in decentralized exchanges all the time, in a bit more complicated manner.

Furthemore, transaction are not "sent to a block". They are sent to a pool of transactions, and the miners decide in which block to include which transaction.

2
  • Am I right to understand that Alice will only be affected if the call buy(150) actually means buying 150 quantity of tokens and not at 150ethers? Because if it was 150ethers and Bob's transaction to increase the price got through first, Alice wouldn't be affected since 150 is less than 300 and that's not enough to buy. However, if it was 150 quantity of tokens and the price has gone up from 150 to 300ethers, Alice will end up buying at a higher price. Did I understand this security flaw correctly?
    – xenon
    Commented Aug 12, 2021 at 9:12
  • Sorry, I just recommend you forget about the example, since it mixes up terms. If someone sends a transaction where they pay X Ethers to get max tokens, the transaction can be abused so he gets less tokens than he was aiming for Commented Aug 12, 2021 at 9:22

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.