Reinitialisation states and full state proofs on-chain
Closed this issue · 2 comments
The on-chain game state for a channel game keeps track of a latest board state known on-chain for each channel. This state is updated whenever a resolution or dispute is processed.
In addition to that, we should also keep track of a reinitialisation state, which is only updated when the game metadata changes (e.g. for Xayaships, the initial state after the second player joined). This state can be used to build state proofs on, without having to care about situations where the "ordinary" on-chain state might be altered by opponents.
Without it, the following issue can occur: Consider a game with three players, A, B and C. Their turns are in a round-robin fashion, i.e. A->B->C->A->B.... Now, say that the initial state of the game (the reinitialisation state) is some S0
. Player A makes a move, yielding S1
, and then player B makes a move to S2
. Now, the state proof that player B has for S2
is still based on S0
and the two moves made, since no signature of player C is available yet. But that also means that if player A now changes their move so that state S1'
results and puts that on-chain with a dispute, then player B cannot prove their state S2
anymore. Hence, player A would be able to change their move in retrospect after B made theirs!
With reinitialisation states, player A would still be able to change the on-chain state through a dispute, but the state proof for S2
would remain valid as player B can (and will) base it on the reinitialisation state S0
that won't be modified ever. After it was already every player's turn, that won't be needed anymore; then every player should keep a state proof with signatures of all other players, not relying on any on-chain states alone.
Unfortunately, this is not enough. If a player does not publish their moves off-chain and instead "just" by filing a dispute, this then again leads to a situation in which state proofs are based on a volatile on-chain state and can be invalidated by others.
The solution is to keep track of all states that ever were on-chain, so that state proofs can be based on any of them. Each state proof should indicate which (if any) on-chain states it uses as a basis, identified by a uint256
ID that could e.g. be the txid that put that particular state on-chain. Only when the channel metadata changes (the channel is reinitialised) those old states are cleared as well.
Archived game states also won't really work well. They are fine under normal circumstances - but if a block that put some state on-chain is reorged away, the state proof again becomes invalid.
What we should really do is this: Store the "reinitialisation base state" for a channel, which is only updated when some on-chain action modifies the channel substantially (e.g. with metadata). This is used as basis for proofs of the very first moves. Also for the current latest state, keep a full state proof in the on-chain game state (and get rid of the concept of "basing proofs on an on-chain state", except for the reinitialisation states). That way, a channel-management client will retrieve a full state proof for the latest state from the game state, which will be valid independent of any on-chain changes or reorgs (as long as there is no reinitialisation, which is a separate story anyway).