I have been interested in thinking through a race condition in which transactions could be executed on the same block in non-deterministic order on the Flow Blockchain.
Let's say you have two transactions which mutate the state of a resource by calling a method on that resource.
As a contrived example:
pub resource Example {
pub let flag: Bool
...
pub fun flipTrue() {
if this.flag == true {
panic("this flag is true, we are not allowed to set it true again")
}
// side effect A
this.flag = true;
}
pub fun flipFalse() {
if this.flag == false {
panic("we are not allowed to do this if the flag is already false");
}
// side effect B
this.flag = false;
}
}
Let's say initially, the flag is false. Then on the same block (if this is possible), two transactions are run, one calling flipTrue
the other flipFalse
, let's call them FlipTrueTx and FlipFalseTx.
I am not sure how to predict what would happen here (pardon me if it is possible to build a trivial test harness for this on the emulator).
Naively, it would seem we would expect one of:
- Something would prevent either transaction from occurring - both would panic.
- The transactions can run in either order, and both are "visible" to the other. So the results would be non-deterministic.
- The transactions can run in either order, and they are not visible to the other. In this case, we would only ever expect
FlipTrueTx
to run successfully, andFlipFalseTx
would always panic.
Would be happy to provide additional information to flesh out the scenario if this isn't clear.
So far, I have attempted reading through relevant documentation and did not immediately find information on this point. I believe left to my own devices, my next step would be to attempt to test this directly. However, building a test harness might not be reliable in the emulator, and potentially quite difficult in testnet given the timing requirements. I was taking a look at the Flow fvm design to try to get a sense for how transactions within the same block were handled. If I am reading correctly, it would seem each execution node must be processing transactions for the entire block on a single thread, but would welcome confirmation.