What can we do to make it easier?

The most important first step to making state channel applications easier to reason about is to standardize the generic state channel functionality in a way that cleanly splits the state channel resolution logic from the application logic. It should be as easy as possible to write your application in a format that is state-channel compatible and, ideally, should feel like you’re writing regular smart contract code.

One way to accomplish this is to model an application as a state machine. The force-move game framework was an example of this and, generally, the EVM is already built upon the foundational idea of being a state machine with state updating each block based on transaction execution.

So what’s the difference between a regular smart contract and a state channel compatible smart contract? Essentially it boils down to the fact that we need a standard way to interface with the state transitions of your application regardless of the implementation of the public API. More simply put, we need an entry-point function to compute state transitions.

An Example — Tic Tac Toe

Let’s say we want to write a Tic Tac Toe application. Probably we’ll write a smart contract that has a public API with placeX and placeO functions, checking msg.sender to verify that the correct player is making their move.

An example Tic Tac Toe smart contract written in Solidity.

Modelling this game as a state machine, we can see that there would be 5 types of states and 2 kinds of valid transition types between them.

An example of a state machine for the Tic Tac Toe application. If it is X’s turn, X can take an action to win the game, end the game in a draw, or just place their piece and pass it over to O for their turn.

Coming back to the idea of a standard interface for updating the state of the application, we want to create a function that accepts some prior state of the state machine (e.g., X_TURN ) and an action that can be taken to get to a new state (e.g., PLACE_X ). Interestingly enough, this is the exact same pattern of some common web frameworks that exist today, such as Redux. They tend to call such a function a “reducer”. So let’s try to write the Tic Tac Toe application in this way:

A Tic Tac Toe application with a single reducer function that handles PLACE_X and PLACE_O actions instead of multiple functions called placeX and placeO. The reducer function “dispatches” the actions to helper functions.

With this in place, we now have a way to compute state updates that can be made to an application with a single common interface — the reduce interface. This is very useful when thinking about the types of attacks against which a state channel must be secured.

But what does the state channel contract need to do?

Naturally, the state channel object should use the application logic to determine if a transition is valid. The core state channel functionality can exist in a generic contract which handles the various attack scenarios that are possible but delegate to the application for the specific transition rules of its state machine.

The StateChannel can use the App Logic as a means of determining valid transitions but handle the authorization and resolution logic itself based on information provided to it by the app logic.

There are two major scenarios that need to be handled. If we use the common example of Alice and Bob exchanging messages with each other, then we can frame them as:

What if Bob submits outdated state?

The contract has to be able to implement a timeout period such that Alice has time to submit newer state than what Bob submits. If the state that Bob submits is provably old, then Bob should be punished. What if Bob stops responding?

The contract has to give Alice the ability to submit the latest signed state she has received from Bob in order to get her money out (after the timeout period elapses). In some situations, it might be possible for Alice to “make a move” that progresses the app’s state machine, and so she must be able to use the application’s reducer in that case.

To accomplish this, the contract needs to expose a basic API. Note that this is essentially the API for dealing with dispute cases. It’s the set of function calls that users of a state channel could use to ensure the off-chain state updates they’re signing have significance.

Roughly this API covers: