A little over a month ago, we enthusiastically announced the OVM, now known as OGS, a general-purpose layer 2 (L2) solution to scaling blockchains, while inheriting all of the security of the base chain (L1). This work, while extremely promising, was purely theoretical… until today!

We’re excited to announce that the rubber has hit the road in the form of a proof-of-concept State Channel implementation on the OVM . This work demonstrates how State Channels can function seamlessly on the OVM and, if you’ll extrapolate with me a bit, shows how all L2 solutions can be built on the OVM for better standardization, security, and interoperability with less development effort.

Hold up. What’s the OVM again?

The OVM is a general purpose framework for completely inheriting the benefits of the slow and costly base chain in cheap, light-speed L2 applications. Its differentiator is that it is blockchain-agnostic and L2-solution-agnostic, creating a standard that users, wallets, developers, etc. can follow to support many different L2 applications instead of a separate paradigm for each L2 approach.

How does it do this? We’ll dive deeper in a bit, but like many proposed scaling solutions, the OVM only uses L1 for what L1 is good at: achieving immutable, unanimous consensus based on a clearly defined set of rules. The mental model I always go with is that L1 is the judge and jury, and it is far more efficient to use it to retroactively handle claims that the law has not been followed than to proactively oversee everything to ensure that the law is always followed.

The OVM takes this approach a step further, realizing that if all of these claims of bad behavior can be expressed in the same way, we can use the same justice system (L1 dispute contract) for all cases (L2 implementations).

State Channels PoC Overview

What it is

An example that has the minimum functionality necessary to demonstrate that full-blown State Channels are possible on the OVM. Code that optimistically evolves L1 state through mutual agreement in L2, completely agnostic of L1, in a way that is verifiable and disputable by participants, thereby completely inheriting L1 security. One of the many scaling ideas that can be implemented on top of the generic OVM L2 client.

What it is not

Special-purpose code that only does State Channels. A production-ready demo of State Channels, free of bugs and small cryptoeconomic holes. Something that actually interacts with L1 (we will discuss here how this would happen and demonstrate how that part works in a later post).

High-level OVM Workflow

Enter L2: Lock funds (and/or state) into a dispute contract on L1, referencing the [L1] adjudication contract. In this case, we will reference a State Channel contract containing logic that defines what constitutes a valid exitable state. The State Channel contract, and presumably all OVM adjudication contracts, will depend on the dispute contract for standard exit request and exit dispute processing.

Use L2: Interact efficiently in L2, completely inheriting L1 security.

Exchange light-speed updates of mutually-agreed-upon state in L2, following the rules specified in the L1 State Channel contract without actually interacting with it.

Exit L2: Exit with a valid claim for which there are no valid challenges during the challenge period. For State Channels, this claim will be that the state being exited is valid, as defined by the L1 State Channel contract. In using the L2 State Channel, the parties necessarily collect proof capable of disputing invalid exit claims.

Deep-dive into L2 with State Channels

L1 Adjudication Contract

If we’re going to use L1 as judge and jury, we need to define a clear set of laws that it will use to make rulings on claims. This will include, at a minimum, defining valid exitable states. For State Channels, a valid exitable state can be defined in English as “The most recent mutually-signed state of a channel.”

To make this statement evaluable by a general purpose judge, a valid exit claim for party A to exit State Channel C, and update its L1 state to match the state of L2, contains two assertions:

Message M with State S, for Channel C has been signed by all channel participants. There exists no Message M’ for Channel C such that M’ has a higher nonce than M and M’ is signed by all channel participants.

If such a claim is made, 2 possible scenarios can result:

The claim is validated by the dispute period lapsing without a valid challenge. The claim is invalidated during the dispute period if either of the following happen:

a) The L1 contract evaluates message M and determines that it is not signed by all participants of channel C.

b) The L1 contract receives, from any party, a message M’ that disproves the claim that such a message doesn’t.

The logic to evaluate such claims must be present in the L1 contract. While this will not be demonstrated in this post, hopefully the reader is sufficiently convinced that Smart Contracts can be built to associate addresses with a channel ID, determine if specific addresses have signed a specific message, compare the nonce property of different messages, take action after a specified time delay, etc.

L2 Interaction

Alice and Bob have deposited funds into the L1 contract, successfully initiating a State Channel. Now what? The OVM leaves this part entirely up to them.

Messages

If Alice and Bob want to evolve the state of their initial deposits in L2, they’ll need to sign and exchange messages that fit the format that the L1 contract can verify and make sure not to break any of the rules set out in the L1 contract. Let’s say this is the message format (as described in our repo here and here):

{

"channelId": "1234567890", // L1 ID

"nonce": 1, // The first message

"data": {

"addressBalance": {

"0x0a": 100, // Alice's initial balance

"0x0b": 100 // Bob's initial balance

}

}

}

If the messages exchanged do not follow this format, the L1 contract will not be able to interpret them, and they’ll be useless.

Sending, Signing, and Countersigning

Let’s say Alice is paying Bob 5 from her balance for some good or service. She can do this by simply signing and sending him the following message (logic here in our repo):

{

"channelId": "1234567890",

"nonce": 2, // Most recent message

"data": {

"addressBalance": {

"0x0a": 95, // Alice -5

"0x0b": 105 // Bob +5

}

}

}

If Bob accepts the message as valid, he will countersign this message and send it back to Alice (let’s ignore the scenario in which he doesn’t countersign a valid message for now because that is a solved problem in State Channels).

If the message is not valid, Bob will not sign it, and the previous state will continue to be the valid state.

They can continue to evolve the state by signing and exchanging messages in this fashion, increasing the nonce for every new message.

Message Storage

Just as the OVM doesn’t dictate how messages are exchanged, it doesn’t dictate what Alice and Bob need to store, but they know what information the L1 contract needs to process an exit and what they need to present to dispute any invalid exits that arise. As such, they’ll need to store the most recent countersigned message as well as any pending (meaning not countersigned) messages that either party has signed and sent.

If Alice or Bob want to exit, they will submit to the L1 contract the most recent countersigned message as the state they would like to exit. If either party tries to exit a previous state’s message, the other party will submit the most recent countersigned message as proof that the other message is invalid.

An example datastore for State Channel messages is here within our repo (note that it extends the functionality in our base MessageDB).

Exiting and Challenging Exits

Each exit and exit challenge will not only need the data that the L1 adjudication contract needs to evaluate it but it will also need to be presented in a way that the general L1 dispute contract can understand.

👷Here’s where we really learn how the OVM functions. Fasten your seatbelt.👷

Let’s say that Alice wants to exit the State Channel after the above message with nonce 2. Here’s the structure of the State Channel Exit Claim that she would send to the L1 adjudication contract. It won’t make much sense right now but will become much clearer as we walk through it below:

{

"decider": AndDecider,

"input": {

"left": {

"decider": SignedByDecider,

"input": {

"publicKey": "0x0b", // Bob's Address

"message": { // Most recent message

"channelId": "1234567890",

"nonce": 2,

"data": {

"addressBalance": {

"0x0a": 95,

"0x0b": 105

}

}

}

}

},

"leftWitness": {

"signature": "0xbbbbbbbbbbbbbbb" // Bob's msg signature

},

"right": {

"decider": ForAllSuchThatDecider,

"input": {

"quantifier": SignedByQuantifier,

"quantifierParameters": {

"publicKey": "0x0a", // Alice's address

"channelID": "1234567890" // Our Channel

}

"propertyFactory": (message) => {

return {

"decider": MessageNonceLessThanDecider,

"input": {

"messageWithNonce": deserializeBuffer(

signedMessage,

deserializeMessage,

stateChannelMessageDeserializer

),

"lessThanThis": 3,

},

}

}

}

},

"rightWitness": undefined

}

}

I read this far so you could hit me with this gibberish?

I can explain…

First, let’s go back to our State Channel exit claim definition above:

1. Message M with State S, for Channel C has been signed by all channel participants. 2. There exists no Message M’ for Channel C such that M’ has a higher nonce than M and M’ is signed by all channel participants.

If we disregard the funky format for a moment, we can see that Alice’s claim sort of follows this claim structure.

The first part lists something called a SignedByDecider , and while we don’t know what that means yet, we see it is listed right by Bob’s address, our most recent message, and Bob’s signature of our most recent message. We can see that this has all of the info necessary to prove that Bob signed the most recent state.

The second part is more intense, but we see a ForAllSuchThatDecider that we don’t fully understand, a SignedByQuantifier that has Alice’s address and our Channel ID parameters, and a MessageNonceLessThanDecider right by lessThanThis: 3 . If we just read parts of that in order, we see ForAll... SignedBy... Alice’s address and our Channel ID, MessageNonceLessThan... 3. It’s a stretch, but maybe this is claiming that all messages Alice has signed for this channel have a nonce less than 3?

Lastly, these two parts are nested inside an AndDecider block. It has a left part and a right part, so maybe it means left AND right?

The VM in OVM means Virtual Machine

The OVM is a virtual machine, and virtual machines need to execute instructions to be useful. In order to do that, the OVM has to define a base interface for an instruction. Since the OVM is based entirely on claims and disputes of those claims, its instructions must all follow the form “decide whether or not input N is valid according to rule set S.”

In order to do this, the OVM has the concept of a Property . Properties consist of a Decider , which maps directly to a simple L1 smart contract capable of making very specific true/false decisions, and input into the decider to be evaluated. For instance, the AndDecider decides true if and only if its input.left property evaluates to true AND its input.right property evaluates to true .

Deciders may also require a witness , which is proof associated with the input . For example, the SignedByDecider decides if a message has been signed by a public key. Its input (the message and the publicKey ) defines what is being decided, and its witness (the signature ) is the proof used to make the decision.

The last piece of the puzzle is Quantifiers (other simple L1 smart contracts). Quantifiers are able to list all applicable information, given a specific set of constraints. For example, the SignedByQuantifier is able to list all messages signed by a given public key.

Above, you can see that the ForAllSuchThatDecider uses the SignedByQuantifier to get all messages signed by Alice for the channel in question. The ForAllSuchThatDecider also takes a property factory function that it calls, passing in each result from its quantifier, to get all of the properties that it will evaluate*. As you might imagine, the ForAllSuchThatDecider decides true if all of the properties it is evaluating evaluate to true .

Putting it all together

Alice’s exit claim demonstrates how these granular properties can be nested together to represent complex statements. We can also see that, just like with other VMs, assembling different permutations of a small set of granular instructions (deciders), we should be able to represent every claim possible.

Please have a look at our StateChannelClient and associated tests that demonstrate L2 message exchange, exit claim generation, disputing exit claims, and more!

*Note: PropertyFactory functions work for this example, but they will not ultimately be used in the claim that is passed to the L1 contract because they cannot be generically represented. This is not a blocker, as it is easily solved by simply passing a Decider , and optionally a transformer to transform Quantifier results, to the ForAllSuchThatDecider to enumerate the Properties itself, but we’re looking into the best approach to use.

Extrapolating

Now we know how the OVM enables State Channels. I won’t yammer on about how the OVM supports other scaling solutions, leaving it as an exercise to the reader how L1 adjudication contracts and exit claims might be structured for these other implementations. One thing I will touch on, however, is how this approach provides other valuable benefits such as security, compatibility, and chain agnosticism.

In standardizing the L1 dispute contract that actually holds all L2 funds, the OVM will offer excellent security by providing a reusable, extensible, battle-tested contract analogous to the ERC-20 standard. This standardization, and the universal nature of OVM operations, will offer compatibility between otherwise disparate scaling solutions as a byproduct, allowing better wallet integration and UX. Lastly, despite the fact that we’re targeting Ethereum for initial implementation, I haven’t mentioned a specific L1 chain for a reason — the OVM should work on any L1 that supports smart contracts. Taking this a step further, with the OVM, the same scaling solution can be implemented on incompatible L1 chains and share almost all L2 code.

This is just the beginning; the future is bright for the OVM! Thank you for reading, and we look forward to sharing more progress along the way.