1

According to the answer to my previous question here, all state reading in a smart contract is done on the current block - Is state read from the current or previous block for external function calls?

My question now is this: Say I have smart contract C with a uint variable called C that changes a lot of the logic in that smart contract depending upon its value (for a game or something similar).

Player A accesses this smart contract, reads its value as "4" from the current block, and increments C by 1. They then do a lot of interaction with the smart contract based upon the value of C now being "5" and broadcast this to the network.

Player B accesses this same smart contract, also reads the value as "4" from the current block but increments C by 5 instead of 1. They also do extensive interaction with the smart contract assuming the value of C to be 9 after its increment, and also broadcast this to the network.

From the perspective of Player A and Player B, they have both performed legal operations with the Contract assuming C to be of the value "4" at the current block, however in each scenario, the Contract would have updated its internal variables in vastly and potentially contradictory ways.

So what happens when player A and B both broadcast their interaction?

2

The Ethereum virtual machine is built in such a way that once a block is mined, the outcome and final state of the blockchain is deterministic. This is how thousands of independent nodes can come to consensus about the state of the blockchain, even though they may be around the world, with different latencies, computational speeds, etc...

It is important to remember that computation on Ethereum is not continuous, rather things happen block to block. Transactions in a block are ordered, and thus for any given block, events happen one after the other in their defined order.

Thus even if every transaction touched the same global variable, and that may affect what happens to a contract or a function, things will still happen in a deterministic way.

So to answer your question more directly:

So what happens when player A and B both broadcast their interaction?

Nodes will receive both transactions at possibly different times depending on network variables. Then they may be included into blocks at different priorities based on things like gas price. Finally once their transactions are placed into a block, mined, and then shared to other nodes, their transactions will occur as defined by that block in the order they were included.

  • So I understand this correctly to confirm - an external contract/player only reads state from the current block of contract C, not the block being mined, however when performing its interactions, the state of the variable C will be based upon the block being mined and dynamically changes in accordance with the order of transactions (as finally determined)? So in the example above, assuming player A was included first, uint C for player B would be treated as being "5" even though they're assuming it's 4? Thus C would then be incremented to be "10" at the end of it all? – Ectogold Nov 18 '18 at 3:59
  • 1
    Yeah, I think that is right, but your frame of thinking is still a bit off. What do you mean by "Player reads state from contract". When a user submits a transaction, they aren't reading any variables them self, or doing any calculations at all. All of the logic is being run on the miners computer, thus the variable itself will change on the local machine, per transaction. Furthermore, let's say that you are running some external website which reads data from the blockchain. All reads have to be in the context of a block, which will only give one answer, the final state of the variable. – Shawn Tabrizi Nov 18 '18 at 4:05
  • Yeah I think I'm getting it now thanks. So for instance my web3 app is telling the player the state of the variable in the current block, which is 4 (converted with some jquery and nice text to something more specific for display purposes). 4, but not 5, allows the player to perform some action. So as far as player A & B are both concerned, they can perform the action since web3 will read the current block uint as "4" for both players. However the results will be different than expected for player B whose action is included in the next block later than player A, is this correct? – Ectogold Nov 18 '18 at 4:56
  • Its hard to say for sure, but it sounds like you have programmed a game where the user cannot know quite what will happen with their transaction. For example, if you build a lotto game where the 100th person to transact wins, there is really no way to know which user that will be, and that may affect things like the amount of gas they send, or whether their transaction is even valid. Sounds like a flawed game for the blockchain. – Shawn Tabrizi Nov 18 '18 at 5:45
1

In my opinion, it will make more sense to think of the blockchain as a well-ordered sequence of inputs with some latency.

Say I have smart contract C with a uint variable called C

Okay

contract Concurrency {
  uint public c;
}

So far, so good.

Player A accesses this smart contract, reads its value as "4" from the current block, and increments C by 1. They then do a lot of interaction with the smart contract based upon the value of C now being "5" and broadcast this to the network.

This is conceptually misleading. c is still 4. Let's imagine a function that does that. It will have to accept an input (1) and do something with it.

function addUp(uint x) public {
  c = c + x;
}

Great. So player A sent Concurrent.addUp(1). What's c? It's still 4 and player 2 should know that. Why? Because the transaction hasn't been mined. It's "submitted", not "executed".

Player B accesses this same smart contract, also reads the value as "4" from the current block but increments C by 5 instead of 1. They also do extensive interaction with the smart contract assuming the value of C to be 9 after its increment, and also broadcast this to the network.

Okay, so Player B sends Concurrent.addUp(5). Again, this is only "submitted", not executed. Everyone agrees that the last known c is 4. These two players know their own transaction is in the queue and they also know there may be transactions from other players also in flight.

A block arrives.

A bunch of unrelated stuff, then from player B, +5 and then from player A, +1. I deviated from the expected order to illustrate that neither player controls that and there is no such assurance. So, c goes:

4 + 5 = 9;
9 + 1 = 10; // this executes in the context of the previous transaction

When this block is processed, then everyone will agree that c is 10.

To make this more illustrative, let's say the game is like 21, only in this game players go bust above 9 and 9 is a perfect play.

function addUp(uint x) public {
  c = c + x;
  if(c > 9) playerIsBust();
  if(c == 9) playerWins();
}

Now the order the network accepts the transactions is super-critical. Whichever player's move is processed first will win the game.

It's not a matter of a miner or a node computing c for everyone else's benefit. Everyone will figure that out for themselves when they see the inputs in a block. The miners disambiguate the order of the transactions. Miners even have latitude about which transactions to include and in what order.

The simple example creates a race condition, which means player A and player B might be in a bidding war to entice miners to include their transactions first. This is the role of the gasPrice. Players determine the priority they're willing to pay for.

The players can conduct themselves as if they know what c is, but it's probably an error. It's important not to proceed on faulty assumptions. They know that c is probably 4 and they have submitted something that's awaiting inclusion in a block. They know other players may have already done things they don't know about. If the contact is well-crafted, it won't allow any game story to unfold if it's against the rules of the game.

Another way to summarize would be to say that transactions execute in the context of all the transactions that came before. Blocks are just batches of transactions that arrive more or less together. The first one runs completely. The second runs in the state left by the first. And so on.

There's a similar question over here. Same idea, different example: Ethereum Smart Contract execution principle

Hope it helps.

  • Together with Shawn's answer this explains it clearly for me now, thank you! – Ectogold Nov 18 '18 at 6:38

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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