4

It took me several days to find the bug.

I got a table like this

TABLE t {uint64_t key, name player1, name player2, uint64_t status};

When the contract received the specific action, it will go through the table to find if there is a row which status == 1 and player2 is empty, if not exist, it will emplace one. Now it has one row like this

{"key" : 1, "player1" : "bob", "player2" : "", "status" : 1}

When next player send the action, he will find the row above and modify the player2 and status. It supposed to be like this.

{"key" : 1, "player1" : "bob", "player2" : "jack", "status" : 2}

The bug comes when two players send action at the SAME time, they could both find the row that status == 1 and player2 is empty! Then the one who modify the table first will be overwritten by the next one. It will becomes like this, jack was overwritten by tom.

{"key" : 1, "player1" : "bob", "player2" : "tom", "status" : 2}

I'm wondering why would two player both find the same row, cause when a player find it he will modify the status to 2. So the next player will never find this row. Even if they both find the row, can we lock the table before we modify it?

I've been suffering from this bug for a long time, does anyone has some good advice?

2
  • I got the answer here that I can't deal this thing in the contract github.com/EOSIO/eos/issues/6344
    – eoser
    Commented Nov 20, 2018 at 3:42
  • This doesn't sound reasonable to me. I suspect this is more a bug in your code than a bug in the EOSIO system. Can you please post the smallest possible working example of the code that reproduces this bug in an edit to this question? Commented Feb 20, 2019 at 14:14

1 Answer 1

1

If I understand correctly, and your code is implemented correctly, this can only happen in the case of a fork in the chain.

Correctly implemented code would look something like this:

auto itr = getTableEntryWithPlayer2EmptyAndStatus1();
eosio::check(itr != mytable.end());
mytable.modify(itr, user, [&](auto & entry){
  entry.player2 = user;
  entry.status = 2;
});

With obvious additional checks depending on the details of what the code actually needs to do in your case.

If two people simultaneously submit the same action, then they will be executed in serial, and the 2nd user's action will fail and unwind because of the eosio::check line.

However, if the two users both submit the action and happen to send them to different forks of the chain, then both will succeed, and which one succeeds will depend on which endpoint you are looking at. This will remain the case until the fork is closed, which usually only takes a few seconds. The good thing is that when the fork is closed, the actions on one of the tables will unwind, so as long as you don't use anything external to the blockchain, and only use tables for storing and processing data, then this will not cause an issue for you. In the worst case scenario, it will lead to confusion for your customers, who originally might see that they "won" a game, only to later realise that they were on a fork and actually never even participated in the game.

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.