Approach 1: State Channels

The first approach to build Valhello will be with state channels. How will the state channel approach help Valhello grow? What are State channels and how do they work? Are they a viable scaling solution? Let's see.

Header image via BBC

The following content is partially extracted from this post.

State Channels

If you've been in Ethereum long enough, you probably remember the time CryptoKitties slowed the whole network down because of pending transactions. Around that time, the idea of payment channels started to take more concrete shape across the various blockchain projects.

Payment Channels

A payment channel works like this: two parties deposit a stake into a multi-signature wallet, and then exchange messages about the state of that wallet until they're ready to close the wallet and each withdraw their share. So Alice and Bob may start out with A:1eth / B:0eth, and end with A:0.3eth / B:0.7eth. Between those two states (the opening and closing state), there may have been a thousand transactions - but all of them were free and instant because they were only communicated between the two parties. Once Alice and Bob are ready to broadcast their wallet states to the world, they send a transaction to the blockchain which contains the closing state, and to the outside world it looks like Alice just straight up sent Bob 0.7 eth. This is a payment channel because it channels payments, and we explained it with an illustrated example here - highly recommended you read this.

The most notable examples of payment channels are Bitcoin's Lightning Network (which cannot work and is already driving users away) and Ethereum's Raiden Network which is just Eth's version of Lightning (Raiden is the god of lightning in Mortal Kombat).

Personally, I'm a much bigger fan of state channels.

State Channels

State channels are like payment channels, but for state. Technically, wallet balance is also state - the state refers to the current data of the blockchain, such that the balances of everyone are known. But while payment channels focus on tokens in particular (native like BTC or ETH or implementations like ERC20), state channels abstract that even further and consider not only token balances state, but also all other data points state.

So instead of exchanging messages about changes in wallet balance with another party, you exchange messages about data.

Example of a payment channel:

  • Alice and Bob open a payment channel with 0.5 eth each (<- transaction happens)
  • Alice sends Bob 0.1 eth
  • Alice sends Bob 0.1 eth
  • Alice sends Bob 0.1 eth
  • Bob refunds 0.25 eth
  • Alice and Bob close a payment channel with Alice having 0.45 and Bob having 0.55 eth. (<- transaction happens)

Example of a state channel:

  • Alice buys a crypto kitty "AngryCat" on the open market for 0.5 eth (transaction outside state channel, on something like Opensea)
  • Bob buys a crypto kitty "BloatyCat" on the open market for 0.2 eth (transaction outside state channel, on something like Opensea)
  • Alice opens a channel with CryptoKitties, committing her AngryCat as a deposit along with 1 eth (<- tx happens)
  • Bob opens a channel with CryptoKitties, committing his BloatyCat as a deposit along with 0.5 eth (<- tx happens)
  • Alice puts AngryCat to breed for the breed price of 0.2 eth
  • Bob notices a breedable kitty AngryCat, and decides to accept the offer
  • Bob sends 0.2 eth to CryptoKitties for breeding BloatyCat with AngryCat
  • CryptoKitties sends 0.2 eth to Alice and creates a new cat - AngryFartCat - assigning it to Bob. This turns out to be a rare cat because angry farts are special.
  • Bob decides to put AngryFartCat up for breeding for the breed price of 0.5 eth.
  • A third user, Charlie, notices this cat and decides he wants in on the action. He has a cat from before: CombatCat, so he opens a channel with CryptoKitties and 0.5 eth (<- tx happens)
  • Charlie breeds CombatCat with AngryFartCat, sending 0.5 eth to CK. CK mints a new cat: NerveGasCat, and assigns it to Charlie, sending 0.5 eth to Bob.
  • Bob decides he's had enough of the game for now and closes the channel. He ends up with a rare AngryFartCat and 0.8 eth. Bob profited. (<- tx happens)
  • Charlie puts NerveGasCat up for breeding for 1 eth.
  • Alice would like to breed AngryCat with it. 1 eth out of her 1.2 eth is sent to CK. CK mints SaiyanCat and assigns it to Alice, sending her 1 eth to Charlie.
  • Alice and Charlie have now had enough, and exit the game by closing their channels (<- tx happens)
  • Alice ends up with 0.2 eth but she also has SaiyanCat. She puts it up for sale on Opensea.io for 5 eth. If she sells it, she's turned a nice profit from the starting 1 eth.
  • Charlie ends up with 1 eth (a profit of 0.5 eth) and NerveGasCat. He's happy with the gain, but still puts it up for sale on Opensea.io for 5 eth.

As you can see, the gameplay itself can get pretty complex - especially as we introduce more players - but the number of transactions when players are in the game (i.e. have state channels open) remains the same. We still max out at 2 transactions per player - one to open the channel, and one to close. Granted, if Alice wanted to breed for 1 eth and didn't have enough eth in her balance, she would have to either earn it in the game by breeding / selling / etc. or she would have had to top up her account. This would have required another transaction (a top-up is actually a simultaneous open / close channel transaction).

Now if we apply this to a truly multi-player game with consequences and more interactivity, we can stretch out imagination a bit and see how it might apply to a MUD. Read on below.

To find out more about state channels, see:

https://medium.com/l4-media/making-sense-of-ethereums-layer-2-scaling-solutions-state-channels-plasma-and-truebit-22cb40dcc2f4

Implementation

Account Creation

To create an account, a user interacts with the game's smart contract by sending in 0.15 eth. 0.05 eth used for funding further development (better than an ICO ¯\_(ツ)_/¯), while 0.1 eth is locked up as that player's deposit. The created character thus enters Asgard (the lobby area), where everything begins.

World Shards are State Channels

Each world zone is a state channel, including Asgard. So entering Asgard adds a player to the Asgard state channel. Whenever a player exits a shard, they get their deposit back. Whenever a player enters a shard, they need to put the deposit down again. The deposit is always 0.1 eth, and the initial 0.05 eth character creation fee is only paid once per character, but every time for a new character. This deposit is a guarantee of good behavior, and is refunded once the player character exits ("Summon a Valkyrie") unless the exit was an ejection triggered by another player (i.e. an invalid transaction was detected and not rectified in a given time frame).

During interaction in a shard, it is assumed all players are making only valid moves. If, however, a player makes invalid moves IRT the game state, others who detected these moves can trigger a reporting procedure which will send a transaction to the public blockchain for the initiation of the dispute resolution process. In other words, you can rat someone out and unless they start behaving they get kicked out of the universe and their deposit is split in half: half is burned, and half is given to the reporter and those confirming the report.

Players transition between worlds (zones) through the Bifrost portal by exiting a previous channel, if in any, and entering a new channel. Entering and exiting a zone means sending a transaction, but keeps the game worlds isolated and small, allowing for addition of zones, tweaks to rules, user-contributed zones, and more.

Rest and exit

As long as players remain in the game, others can initiate combat with them, steal from them, interact with them. If the player remains inactive (no response given) for more than 24 hours, their state channel automatically closes. A player can close their channel manually, too, to exit the game and pause their character (the Summon a Valkyrie operation). This has the added effect of withdrawing the player character to a user's wallet, which lets them trade the character on an NFT marketplace (the player characters are NFTs).

UX

All this talk about transactions makes for a poor user experience. The average user does not want to think about transactions, gas costs, confirmation times, etc. The way we get around this is as follows:

  • the game will use Nimbus as a back end, which can handle peer to peer connections and key management. In effect, the game client is a wallet which can auto-sign transactions or, for the more paranoid, request a password for every transaction (depending on whether or not a keystore file was encrypted). This means that the user starts playing by downloading the game client (weird to call it that since there's no server, but okay), seeding it with a bit of eth, and the client takes over, occasionally asking for a password.
  • each interaction with a shard is happening on a state channel, which makes it possible to interact with other players and the world through commands like LOOK at @swaderor PUNCH @swader or GIVE #mjolnir to @swader. These interactions are signed messages aggregating on the clients running that particular shard of the universe, of which there will always be at least 2: the software running over in Bitfalls HQ, and the player's computer.

Therefore, think of the game client like a stripped down version of Mist which has the ability to execute custom pre-defined transactions. The client itself will contain all the game logic.

State growth

To achieve a feeling of real time interaction, the clients that communicate are constantly exchanging messages that basically say "no action taken". This happens roughly once per second (throttled, so powerful computers don't become too fast players). This allows a player to issue a command at any time, and it'll seem real time. This also means the state of the channel grows rapidly and constantly.

Because it's in everyone's best interest to keep the game client light-weight, the plan is to occasionally purge the shard data though how often and how exactly is still up in the air and will be decided when we see how fast the clients are filling up hard drives.

What next?

Wait for the code or help us write it. The Valhello org on Github is the one to keep an eye on, or get in touch on Twitter.