Frequently making transactions to progress the Ethereum Virtual Machine is needlessly expensive and slow. Most applications using Ethereum today work by updating a storage variable of an on-chain contract for which users pay transaction fees and spend time waiting for block confirmations.
Naturally, this is quite slow. In order to use an application, we’re forcing users to manually submit database updates to the world’s most secure, decentralised, and trust-free…. mobile phone from 1999.
Thankfully, there’s a better way.
We can write applications that are exactly as secure by shifting some of the responsibility onto the clientside code instead of relying on Ethereum to do everything for us. Generally, we call these “Layer 2” technologies. At L4, we’ve written extensively about the different ways of doing this to help explain the idea.
Most of the “Ethereum doesn’t scale!” narrative isn’t due to the underlying blockchain being unsuitable. More accurately, it’s because it’s very difficult for developers to use layer 2 technologies like state channels. We need better developer tooling on top of ethereum, which will make it easier to write applications in an efficient way.
Counterfactual is an open-source project that is tackling this problem head on. Our goal is to make it easy for developers to build their applications using state channels on ethereum. You can read more about the goals of the project on our website, and check out the code on our github.
Why is it difficult to use state channels today?
Today, if a developer wants to write their application to be decentralized using Ethereum, they’re likely to implement each of:
- A public API — public or external functions on their contract
- Authorization logic — usually by checking
- Resolution logic — to decide how funds are distributed
For the most part, this is a good level of abstraction for a developer to be thinking at when designing a decentralized application. We’ve seen thousands of developers build applications using Ethereum over the last year using this pattern, so it’s probably best to not radically change it.
Thinking about state channels from this point of view gives you far more empathy for app developers. Most of the time when trying to design a state channel application, you’ll run into all kinds of roadblocks like:
- It’s not clear how the public API should be written since you aren’t signing Ethereum transactions anymore, you’re signing generic state objects
- The authorization logic requires each user of the state channel to sign an object for each update and verify these signatures using
- The resolution logic is now more complicated by the fact that there is a built-in “challenge” or “dispute” period every time new state is submitted
And worst of all, there are zero established standards for the entire state channel protocol, making it hard for frameworks or common libraries to emerge.
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
placeO functions, checking
msg.sender to verify that the correct player is making their move.
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.
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:
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.
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
reducerin 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:
- Creating a dispute. One party submitting the latest signed copy of the state and optionally taking an action on the app that will logically progress the state to the next state.
- Progressing a dispute. One party responding to the dispute from another party by taking an action on the state that has been submitted.
- Cancelling a dispute. Both parties agreeing to cancel the dispute and resume off-chain normally.
Where are the funds stored?
This is where many of the features described in our paper become useful. We put the funds in a generic multisignature wallet and use it as the primary contract that makes commitments for distributing those funds based on the outcome of the state channel.
This gives us many useful perks. One of the nicest is the ability to take advantage of the technique of counterfactual instantiation, also described in detail in the paper. When we add that to the mix, we can keep the state channel contract and the application logic contract off-chain.
After we set this up, we get yet another immensely powerful feature: zero on-chain transactions for installing and uninstalling applications.
Effectively, since we can make an unlimited number of calls from the multisignature wallet for conditional transfers, these commitments can be made for any number of applications.
In future posts, talks, and discussions we’ll outline our vision for the software that will live on top of the contracts layer. State channels, to be usable in production, are going to require a significant amount of coordination from the entire ecosystem. Some of the specific areas we’re currently working on and look forward to further collaborating with others on are:
- Standardizing research terminology, sharing insights, and following through on other fascinating research problems in state channels and layer 2 scaling.
- Integrating generalized state channel patterns into existing state channel systems and applications based on off-chain paradigms.
- Working with web3 frameworks to make off-chain concerns well-known for the future development of their developer APIs.
- Working with wallet providers to help provide clear protocol specifications and standards around how state channels can exist in their domain.
How can I learn more and contribute?
As a good place to start, we encourage you to check out our contracts code on GitHub which contains a reference implementation for the technique.
We will continue working with great teams and individuals interested in the future of decentralized application development going forward, and would love to invite others to collaborate with us.
This is only the start of the journey. To see the vision of a fully generalized state channels network that is accessible for all users of the technology, we need to do much more.
State channels will become the protocol for peer-to-peer value transfer. They will need to be standardized for use in every type of client, in wallets, and across blockchains.
If you’re interested in working with us on this vision of the future, reach out at firstname.lastname@example.org or join us on GitHub. :)