State Channels for Dummies: Part 5

Generalized State Channels / Counterfactual

Note: This article requires some background knowledge on app-specific state channels. If you are not familiar with these, read this article of mine — it provides a good overview.

What is it?

Generalized state channels allow users to use the same state channel for multiple different purposes.

Why does it matter?

Without state channels, any application built on Ethereum suffers from high-cost and latency due to a heavy dependence on on-chain transactions. We can create much more efficient applications with application-specific state channels, where users only need to pay when they start and finish using an application. Generalized state channels improve on this further by requiring that users only pay when they are finished using an application.

Note: When I say ‘pay’, I’m referring to submitting an on-chain transaction.

Generalized State Channels

Much like with app-specific state channels, a sufficiently powerful multisignature wallet is sufficient to act as a state deposit holder for generalized state channels. The difference being that this is the only on-chain component that must be deployed for each additional application that users want to ‘install’.

App-Specific State Channels (Recap)

To recap, this differs greatly from what we have covered in previous articles, where we have followed this process for engaging in a state channel between multiple parties:

  1. Deploy a multisignature wallet with the relevant participants and some added ruleset (used to interpret state sent to it) to the chain.
  2. Pass signed messages between participants in a channel as they take various actions.
  3. When participants are ready to settle or exit the channel, they submit the latest state signed by all parties to the multisignature wallet, which uses its built-in ruleset to determine how to conditionally pay each channel participant.

What this setup looks like:

At any time, Bob and Alice can submit the latest state signed by both of them to the on-chain multisignature wallet to settle the channel.

Note that this multisignature wallet only supports the one application which fits its built-in ruleset. Again, generalized state channels extend this functionality by allowing for new applications to be installed on an existing state channel with the aid of ‘Counterfactual Instantiation’.

Counterfactual Instantiation

Counterfactual Instantiation is the process by which members of a channel agree to be bound by an off-chain smart contract. This is a bit of a strange concept, because it effectively means that ‘off-chain code is law’ (hat tip to Liam Horne for this one), so here’s a simple example:

Easy peasy

The create this channel, the only thing we need is a multisignature wallet. Arjun and myself deposit 5 ETH each into this wallet and decide that we want to create a payment channel between each other. So, we create the payment channel off-chain and both sign the contract code, agreeing to deploy it when we are ready to close the channel. We also both sign off on a conditional payment from the multisignature wallet that is based on the finalized state of the deployed payment channel.

So, in the same way that we had been able to keep state off-chain by signing it with all parties, we are able to do the same with code. So as soon as we all sign off on the code, we can sign state just as though it had been deployed on-chain. The main difference is that we only need to deploy the code when we want to settle the payment channel.

So when we want to settle, we:

  1. Deploy the payment channel contract.
  2. Submit the latest state signed by all parties to it.
  3. Submit the conditional payment to the multisignature wallet (which is based on the balances of the payment channel).
  4. Multisignature wallet pays out to all participants.

So, Counterfactual Instantiation allows us to act as though off-chain code is on-chain, because we can deploy it at any time and submit the latest state to it. But, there’s a problem with this — how can we sign state transactions for something without it’s on-chain address? This is where the Registry comes in handy!

Registry

At a high level, the registry smart contract allows us to:

  • Create an off-chain address (cfAddress) based on the contract’s bytecode.
  • Deploy a smart contract and map its cfAddress to its on-chain address.
  • Execute proxy calls to an on-chain contract using its cfAddress.

Registry pseudocode:

contract Registry {
mapping(bytes32 => address) resolver;
function cfAddress(bytes code) returns (bytes32) { ... }
function deploy(bytes code) { ... }
function proxyCall(Registry r, bytes32 addr, bytes data) { ... }
}

So, If you want to deploy a contract, you’ll be signing a transaction to the deploy function with the ‘initcode’ of the contract (Typically, this is the contract constructor bytecode concatenated with ABI encoded constructor arguments). This deploys the contract on-chain and adds it into the registry mapping.

In this fashion, you can create off-chain contracts which reference and depend upon each other using the resolver in the registry. So, for our previous example to have worked, we would have had to use a registry contract to deploy the payment channel as well as to resolve the payment channel’s address with the conditional payment from the multisignature wallet.

Dependencies

What this registry allows us to do is to have off-chain contracts dependent upon each other such that if contract A depends on contract B, A cannot be finalized unless contract B is finalized (conditional hierarchical finalization).

Here we see that our PC contract cannot be finalized without our Nonce contract also being finalized.

Now, this is a basic example where we require that one contract be based on the boolean condition of another. We can extend this to allow for what are known as ‘Atomic State Transitions’.

Atomic State Transitions

With this hierarchical dependency structure, we can do atomic state transitions where some contracts are made valid while others are made invalid simultaneously. The following example shows how we can turn a payment channel with 10 ETH in it into a payment channel with 8 ETH in it and a poker game with 2 ETH in it based on a nonce value.

Validity of contracts are based upon the nonce value

If the two parties of the payment channel both agree that they want to use 2 ETH from the payment channel to create a Poker game, they can both sign to update the balance of the payment channel from (5, 5) to (4, 4) and sign a new Poker contract with 2 ETH in it — both of which are conditionally valid based on the nonce value of the Nonce contract being 4.

State atomically transitions based on the new nonce value

After having both signed the conditional update to the payment channel as well as the new Poker contract, they can both sign to increment the nonce value in the Nonce contract. Once the nonce has been updated to 4, the old payment channel state is made invalid while the new poker contract and updated payment channel state are made valid.

With atomic state transitions comes the added bonus of easy upgradeability. If the two participants of the previous payment channel had wanted to fix a bug in the code, they could have patched the code when they were updating the balance. After updating the nonce, the old buggy code which was dependent upon that nonce would then be invalid.

Here’s an example of what these Counterfactual contracts would look like with a ‘Root Nonce’ contract:

With this structure, we can conditionally update the validity of all off-chain contracts with a single ‘Root Nonce’ contract. This allows us to iteratively add and remove off-chain contracts from our state channel based upon the needs of the participants.

Settling a Channel

Say that Arjun and myself wanted to settle our Connect Four game above. We would do the following:

  1. Deploy the Root Nonce, ETH Payment Channel (PC), ETH Conditional Payment Channel (CPC), and the Connect Four smart contract.
  2. Submit our latest signed state to the Connect Four contract.
  3. Finalize the CPC (based on the Connect Four Contract).
  4. Finalize the PC (based on the CPC).
  5. Submit a signed conditional payment to the multisignature wallet (pays out based on the finalized state of the PC).

This allows us to keep the other state channels open and we can even reuse the code that we have already deployed to chain for future games of Connect Four. The next time we want to play, we would just need to sign state of our games and then submit it to the already deployed smart contract. The only thing that would need to be implemented was a way to un-finalize the existing on-chain code, which is an implementation detail.

Note: You don’t need to have so many dependencies for your contracts like I show here with Connect Four, CPC, and PC. However you wish to structure this is up to you and those involved in your state channels!

Now, say that Arjun’s friend Hunter wants to play Connect Four with Arjun, but only has a state channel open with me. This is where the magic of Metachannels comes in to play:

Metachannels

Metachannels are virtual channels over state channels. In order to create one, you need to have two participants that want to interact over a state channel that both have an existing state channel with a common connection. Both participants would agree to create a new contract between each other and the common connection would create a proxy contract with both participants that would point to their newly created contract. This would look like the following:

Virtual Generalized State Channels = Metachannels!

And we can extend this model ad-infinitum to be able to play Connect Four with whomever we wished.

Benefits of this Approach

  • Bootstrapping — Because all we need is a multisignature wallet, we can easily build on all of the existing ones which are on-chain.
  • Privacy — Nobody aside from the participants knows what is going on within a state channel, to the outside world the participants just have a multisignature wallet with each other.
  • Upgradeability — If there is a bug in some off-chain code, all parties can agree to fix it in the channel without any on-chain operations.
  • Reusability — This approach encourages generic on-chain libraries (effectively) that all state channels can use.

Who is Working on This?

This article is a summary of this presentation that Liam Horne gave in July at Binary District’s ‘Off the chain’ master workshop. Liam is a part of Counterfactual, which focuses on “Practical engineering work to make state channels work for real applications today”. If you are interested in learning more about their research or leveraging their framework / design patterns, I have linked to their website and Github below.

Part 6: Stale State Griefing

In my next post, I’m going to be diving in to stale state griefing prevention within a state channel. Stale state griefing is where participants act maliciously towards an offline party by submitting an earlier channel state which is more advantageous to themselves. The main focus of this posting will be Pisa.

Further Reading

I highly encourage everyone to check out all of the presentations from that Binary District’s master workshop, they are the best source of information I have found on these topics to date.

Interested in Being Featured?

If you have any interesting projects that the community would benefit from leveraging / understanding, I would love to see your implementation and how you’re approaching things.

Feedback

If there’s something about this that I can improve, please don’t hesitate to let me know in the comments section, below. And if you’ve gained something from this article, please spam the clap button and share it (these simple actions really make a huge difference). Here’s the link to do so:

https://medium.com/@eolszewski/state-channels-for-dummies-part-5-6238f83f8da3

Find me on Twitter or Github!