Off-Chain Event AggregatioN
When building blockchain applications (dApps), we often face the challenge of taking as many things as possible off-chain — but while retaining the benefits of the underlying trustless consensus at the same time.
But why do we want to do this? Well, by design a decentralized consensus means that every validator/miner in the network has to compute the result of all transactions. This makes it expensive to use on-chain transactions for every action in your dApp. On top of this, most actions in a dApp do not actually require changing a global ledger.
The most popular ways for off-chain scaling are state channels and sidechains (such as Plasma), but we felt that the particular case of AdEx did not fit in any of those scaling solutions. This is why we came up with a solution that felt more natural to us; later on, we discovered that it could be useful for more projects.
We call this scaling mechanism OCEAN: Off-chain Event AggregatioN. It’s a very simple way to scale dApps: certain events happen off-chain, and are eventually committed to the chain, therefore providing the underlying security. Sounds a lot like a state channel, right? Wrong.
OCEAN is quite different. A state channel is usually an interaction between two parties only, where those two parties finalize the said interaction themselves. OCEAN, on the other hand, defines validators beforehand. These validators are supposed to observe interactions between any number of parties, and then vote on the the overall result on-chain.
An OCEAN flow goes like this:
- A commitment is written to the blockchain, defining the end goal for the OCEAN channel (for example: 100 click events, signed by different whitelisted public keys), the validators, the validator rewards and the channel timeout.
- The off-chain events transpire and the validators record them.
- Within the timeout, the validators sign their vote, and over 2/3 signatures for the same vote value are required in order to be committed to the chain; the channel is considered finalized, and you can handle the result however you wish on-chain.
- Beyond the timeout, the channel is considered expired and is reverted.
Within AdEx, anything between the beginning and the end of a delivery commitment is tracked off-chain (e.g. clicks, impressions, etc.), and committed on-chain by the validators (usually the publisher, advertiser and an arbiter) at the end. A ‘success’ vote means the publisher will get paid, and a ‘failure’ vote means the funds will be returned back to the advertiser.
You can see a working implementation of OCEAN here: https://github.com/adexnetwork/adex-protocol-eth
You can think of an OCEAN channel as a mini, one-block blockchain with pre-delegated validators. This is especially useful when:
- There is a clear, logical and game-theoretically sound way to select validators;
- There are clear start/end points of your dApp logic that correspond to opening/closing of OCEAN channels, where the result at the end would define important actions (e.g. funds being transferred);
- There are interactions between multiple parties (ones that have never interacted with the blockchain) that need to be resolved or settled relatively quickly.
To make this more clear, here’s an explanation of the AdEx use case:
- The publisher and advertiser have opposing interests, so a logical validator set will be: publisher, advertiser and arbiter.
- When the publisher accepts to deliver an advertising bid, that’s a new OCEAN channel. It gets resolved when the publisher proves that they delivered the ad, and upon resolution the funds would be paid out to them.
- The interactions happen between publishers, advertisers and any number end-users, where the end-users do have an automatically generated cryptographic identity but have no Ether.
OCEAN use cases
While the current implementation is specific to AdEx (validating impressions/clicks of ad bids), we believe that there are many other possible use cases for OCEAN, for example:
- AdEx: real-time bidding
- Social network: polls, voting
- Sidechain: you can implement a BPoS (bonded proof of stake) sidechain where every block is an OCEAN channel, and the final committed vote is a merkle root of the entire chain state; every finalization would save the new validator set on-chain, allowing validators to bond/unbond; since the merkle root is committed on a PoW chain, long range attacks are not feasible.
Is OCEAN trustless?
By itself, OCEAN relies completely on the validators you delegate when an OCEAN channel is opened. So it is as trustless as the method you use to delegate the validators. A good OCEAN implementation should (1) delegate validators who don’t stand to gain from their own malfeasance; (2) incentivize voting by validator rewards; and (3) implement stake and slashing if possible.
In most cases, most of your OCEAN validators would be the most important participants in the interaction themselves, along with third-party mediators.
State channels are designed for interactions between two parties. You can use state channels for multi-party interactions too but that’s usually a network of multiple two-party state channels.
OCEAN channels are designed to handle multiple-party interactions in a much easier way, with much easier on-chain exits. For example, in a multi-party state channel network, each of the channels will have to exit/checkpoint before you could actually transfer a token reward out, while with OCEAN you’d only require finalizing a single OCEAN channel.
It needs to be said that a well-designed network of state channels may never need on-chain settlement. However, as of today, in a real world scenario, your users would actually need their funds to be moved on-chain if they want to spend them.
Furthermore, the trust model is different in that state channels rely on the participants to settle the channel, while OCEAN relies on delegated validators. When you scale that to multiple parties, however, it starts to look similar: you can have all the participants in the interactions be the validators on OCEAN. Should some validators decide to group together and act malicious, if they’re less than 2/3, they risk stalling the OCEAN channel and letting it timeout, therefore possibly getting slashed (depending on the implementation).
There’s much more to be said on the tradeoffs of both approaches, so expect a follow up post on this later this year.
To learn more on state channels, we recommend you check out https://www.learnchannels.org/
Most Plasma designs are sidechains for fund/NFT transfer, while an OCEAN can be used for any arbitrary events/computation.
In a dApp design, a Plasma chain would be expected to always stay open, meanwhile OCEAN channels would get opened/closed all the time.
Plasma delegates a single validator, but does it’s best to allow users to exit, should the validator misbehave. In OCEAN, we trust the validators completely, so delegating the validators in a fair and trustless manner is critical.
Something interesting to mention is that you can implement a sidechain using OCEAN as well, by implementing PoS with slashing. Each block would be a separate OCEAN channel, getting resolved by a vote for the correct state merkle root. The state would contain the new validator set, since it might be changed if someone gets slashed. The next OCEAN channel (for the next block) would only respect votes from the new validator set.
Compared to Plasma, this OCEAN-based sidechain will not have the exit guarantees, instead shifting the trust into the validators, and relying on a BPoS (bonded proof of stake) system to ensure they behave correctly. Such a sidechain will be much more suitable for arbitrary state transitions (smart contracts/ EVM), because the security model relies on ensuring validators behave correctly rather than on exit guarantees, which depend on UTXOs/ clear coin ownership.
We will publish a follow-up on this use case of OCEAN later on.
For more information on Plasma, this post is an excellent start: https://medium.com/tendermint/blockchain-scaling-solutions-cosmos-and-plasma-b5ee09456f80; and, of course, https://www.learnplasma.org/.
When should I use OCEAN?
Generally, the answer to this question depends on the logic of your dApp.
If the interactions are state transitions, are strictly ordered, and often flow between two parties, you probably need state channels.
If the interactions are mostly fund transfers, you probably need a network of payment channels or Plasma.
If, however, interactions have a complex flow and you need to exit on-chain often, we recommend you look into OCEAN.
The OCEAN protocol is very loosely defined, allowing each particular design/implementation to extend it and refine it for the particular purpose.
The basic building blocks are:
- startCommitment(*any args*) -> commitment: starts a commitment with specific goals, validUntil and validators; logic on how to determine those is up to the application;
- finalizeCommitment(commitment, vote, signatures): finalizes a commitment with a vote; signatures is an array of signatures, each index corresponding to the given index of validators; a validator must sign keccak256(commitmentId, vote); whether the validators will be rewarded and what will happen on specific votes is up to the application;
- timeoutCommitment(commitment): called to clean-up after a timed out commitment and perform the revert; this is not needed on systems where you can define your own block start/end behavior, such as Cosmos/Polkadot.