Proof of Gameplay

Update: August 5, 2024 The mentioned issues had been “somewhat” fixed by the concerned team. The game is still not on-chain.

Disclaimer: This piece is meant to be a fun and spicy callout on the current situation and does not mean any harm, the devs in Stackr team are actually coinbase fanboys and would infact love to work with them on fixing the issues highlighted here to onboard users to crypto “properly”.

A few weeks ago, Coinbase (Base) announced a partnership with Atari, the gaming giant, bringing arcade classics like Asteroids and Breakout “onchain” as part of their ongoing “Onchain Summer” campaign. Much like everyone else, we found this move quite based, paid the minting fees, and proceeded to play Asteroids. Few games in, one of our engineers got bored and started wondering the obvious question: “where’s the onchain?”.

Well...trust me bro?
Well...trust me bro?

This blog post serves as a case study, uncovering the lack of “onchain” in the onchain arcade, how we were able to spoof a high score, and how this led us to build an Asteroids-clone as a Micro-Rollup (live at Comets). The aim here is not only to be spicy but also propose a better approach. So, we’d be introducing the concept of Proof of Gameplay and, get this - how we actually put the game on the fkn chain with Stackr.

Proof of Off-chain: Spoofing a High Score on Base’s On-chain Arcade

The psyops of the on-chain gaming world is that many games actually run entirely off-chain, only submitting final scores to the blockchain. This approach, while efficient, has a significant flaw: there’s no way to ascertain if a player genuinely played the game to achieve their high score or if they merely spoofed an inflated score. This lack of verification undermines the game's integrity, making it impossible to distinguish between genuine skill and fraudulent activity.

Base’s On-chain Arcade suffers with the exact same flaw. When a game session ends, the browser simply submits the high score to Stack.so’s point system to record it (via another API hop). The issue is worsened by the fact that since it’s the browser submitting the score directly, the system’s API key is left exposed. So, a little bit of reverse engineering (fancy term for opening the “Network” tab) and we were able to recreate the API request in Postman:

Postman window showcasing the spoofed API request
Postman window showcasing the spoofed API request

This means anyone can submit a custom high score for any address without any authentication. As the obvious next step, we reported this issue to the Base team. It took them a long time (over 2 weeks) to work on the issue.

So, in light of transparency and to simply make the summer more entertaining, we made a webpage - [redacted] - where anyone can go and get their desired high score! After all, Web3 is all about giving control back in the hands of users, amirite?

A webpage which makes this request for your address and desired high score
A webpage which makes this request for your address and desired high score

As of August 5, 2024, this method does not work anymore. The teams changed the API format but the fix is still (it seems at the time of publshing this article) on the frontend side of things with a JWT token session. The gameplay is still not recorded or has any link with final scores being submitted. If the frontend wants, it can send whatever score to the point system. (infact even legit games are not getting submitted on the leaderboard, a bug perhaps?)

How can we do better?

Our team having discovered this issue combined with our general curiosity to explore interesting problems, we thought how can we do better, decided to put on our builder hats and got to work.

Proof of Gameplay 🥁

Let's revisit the basics by examining how traditional games function. In a typical client-server game setup, the client (player's device) and the game server collaborate to deliver a seamless experience. The client is responsible for rendering the game world and user interface, capturing player inputs, and sending them to the server. Both the server and clients run their own game loops where the server is authoritative, meaning it resolves conflicts and discrepancies in the game state. Since the server stores and validates all the player’s inputs and actions in real-time, manipulation of the game state, such as forging scores, is prevented.

General implementation of a client-server setup in traditional online games
General implementation of a client-server setup in traditional online games

In contrast, the underlying issue with the On-chain Arcade game is that the server relies solely on the score submitted by the client, which could easily be spoofed.

To address this issue, we need Proof of Gameplay.

The core idea behind Proof of Gameplay is to ensure that every recorded score is backed by verifiable evidence of actual gameplay. For example, similar to traditional game designs, a record of all the moves made by the player can serve as a proof for the server (or anyone) to be able to verify the submitted score by replaying the sequence of moves.

proposed implementation of proof of gameplay as a micro-rollup
proposed implementation of proof of gameplay as a micro-rollup

Implementations of Proof of Gameplay can preserve the fairness and competitiveness of games and also reinforce trust in the on-chain gaming ecosystem.

Proof of gameplay for Turn-based/shared world games

This is just one of the approaches which is possible with this construct where the user starts a session, records all the moves and sends the proof of gameplay to the micro-rollup for verification. This works in this particular category of games but might not be the best one as it requires a large list of moves that gets signed on in a single packet. The packet size can grow by a lot which may not be practical to sign as a message.

Moreover, in case there are multiple players involved, for example, turn based games or shared world games, the moves from each player would be dependent on each other. In this case, each move must be a standalone user action to the micro-rollup which will be sequenced and executed on the world state.

Proof of Gameplay for single-player games vs multi-player games
Proof of Gameplay for single-player games vs multi-player games

Micro-rollups, thanks to their insanely fast block times can support both modes. We will discuss this in a later blog post ;)

Off-chain Game + Proof of Gameplay = On-chain Game

As noted above, in the traditional setup, full control lies with the game server, and ultimately, the game developer, who could arbitrarily change the game logic or state, potentially cheating their users. This is where the importance of “on-chain games” comes into play. By implementing all or parts of the game logic on-chain as (immutable) smart contracts, games can become more transparent and permanent.

However, this is much easier said than done. Game logic can be extremely complex to write in EVM, and game servers are among the most demanding systems, while blockchains are terrible for compute-heavy tasks. Fortunately, we have this technology called rollups which supercharge blockchains with just that - perform a lot of heavy computation off-chain and submit a small proof of correctness on-chain. However, even most rollups today are general-purpose blockchains themselves and inherit the same limitations.

The quintessential thing to note here is that the game’s state machine needs to duplicated on both client and server side to enforce the rules. At the same time, an off-chain server requires trust while an on-chain server is impractical. But what if we had verifiable off-chain servers — only submit a Proof of Gameplay on-chain without requiring the game logic to be on-chain?

Micro-rollups, with their verifiable and fast off-chain compute, are perfectly suited for this problem.

Proof of Gameplay ∈ Verifiable Compute ∈ Micro-Rollup

Execution cost is minimal because these micro-rollups are super optimized for the game and hosted as web2 servers, storage settlement still happens on-chain
Execution cost is minimal because these micro-rollups are super optimized for the game and hosted as web2 servers, storage settlement still happens on-chain
  1. Micro-rollups are simply backend services

    Micro-rollups, being indistinguishable from backend services, offer the same developer experience as traditional backend frameworks. Combined with the fact that the Stackr SDK is written in TypeScript, this enables code reuse between the frontend and backend. This is a big unlock for browser-based games, as developers don’t need to duplicate the game logic.

  2. Micro-rollups enable specialized minimal runtimes

    Micro-rollups lets you selectively expose only necessary functionality through deterministic and rigid State Transition Functions (STFs) tailored to the state of the rollup. This allows for highly optimised minimal runtimes dedicated to the game logic offering extremely fast execution.

  3. Micro-rollups enable verifiable off-chain compute

    Micro-rollups require no overhead in bootstrapping a network since they leverage Stackr’s verification layer (aka Vulcan) for correctness. Once the state machines are deployed, the STF logic cannot be mutated. This enables the users to have an assurance that the provider has not arbitrarily changed the rules of the system.

The following sections cover the gameplay experience of the game we built and also the developer experience of building it, detailing various engineering tidbits.

Comets: Actually “Onchain” Asteroids-clone with Stackr

because comets have a streak which makes it’s path “predictable” ;)

Demo gameplay GIF of the Comets game
Demo gameplay GIF of the Comets game

After a bunch of research (and meme-ing), it was now time to bring this to life. We started by carefully listing down the core properties and features our game should offer:

  1. The game should incorporate Proof of Gameplay without sacrificing UX. That is, the game should provide the same web2-like experience familiar to users.

  2. The Proof of Gameplay will guarantee Verifiable Replays for each game session. That is, the server (read: micro-rollup) will verify the Proof of Gameplay from the client, keeping it in check.

  3. The ultimate source of truth should be the underlying blockchain. That is, the server will also relay the proof to be verified on the parent chain.

With this in place, we could design the end-to-end user story of a player’s interactions with the game, clearly demarcating what needs to be off-chain vs on-chain.

Gameplay Experience: Off-chain Game meets Proof of Gameplay

Breaking this down:

0. The player visits the webpage — comets.stf.xyz — and connects their wallet. They can also choose to create an embedded wallet.

1. The player presses ENTER to start the game. At this point, their wallet pops-up, requesting them to sign an action and submit it to the micro-rollup.

2. The micro-rollup executes the player’s action, records a new game in the state, and returns a pseudorandomly generated game ID.

3. The game world is now rendered, and the player experiences traditional web2-style gameplay with no wallet pop-ups or on-chain transactions during the gameplay. The client keeps track of each move made by the player at each tick of the game loop.

4. At the end of the game, the player is prompted with another request to sign an action to submit their score and Proof of Gameplay (their list of moves) to the micro-rollup for validation and storage.

5. The micro-rollup executes the player’s action and re-runs the game loop, replaying all the moves. If the simulated score matches the submitted score, it is recorded in the state. Otherwise, if the scores don’t match, the action’s execution fails with no state update.

6. Finally, the player is notified of success or error with their updated standing in the leaderboard.

Try out the game here -

The entire game codebase can be found here

Developer Experience: Off-chain Game Server meets Micro-Rollup

E2E user story of playing the Comets game
E2E user story of playing the Comets game

Now that we have talked about the UI/UX of the game, let’s delve deep into the DevEx of building this game’s server-side logic as a micro-rollup.

Note: Before proceeding further, if you are not familiar with how Stackr Micro-Rollups work, please refer to Quick Primer on Micro-Rollups.

When developing a micro-rollup, it's crucial to conceptualize your logic in terms of a state machine. This involves carefully considering the state of the micro-rollup - that is, the data it will hold - and the actions that will dictate the behavior of the state transition function, which in turn operates on this state.

The Comets state machine
The Comets state machine

With the above in mind, we start by designing the state of the micro-rollup.

The Design

1. Game sessions are stored off-chain inside a state machine

2. Player sends actions which trigger a state transition function inside the state machine

3. Player can send action to start or end a game.

4. After each set epoch a block is generated which contains state root and action inputs

5. The block is sent to Vulcan network for verification

6. If the block conforms the rules of the state machine it is approved

7. The block data is split between Ethereum and DA for settlement.

Game settlement mechanism
Game settlement mechanism

The Code

The Stackr SDK, written in TypeScript, lets you inject the application logic directly into the blockchain. The developer only needs to define their state and state transition functions without worrying about any of the protocol heavy lifting of building a blockchain. Let’s see how:

Defining the base state

Our micro-rollup state only needs to keep track of game sessions with basic information, such as the player's address, the score, and whether the game has ended.

1. So as a starting point, we defined the rollup state as types in TypeScript.

The Stackr SDK adopts a schema-first design paradigm familiar to application developers. It allows complete freedom in terms of state design, allowing developers to simply think in terms of individual entities and how to compose them together to form the full application state.

You may be wondering how we are able to get away with only storing the score and not the list of moves in our rollup state — this is covered in the next section.

Adding handlers for state update

After we have defined our rollup’s state, we need to define State Transition Functions (STFs) that update the state.

2. We defined two functions, startGame and endGame to manage a game’s lifecycle.

By having only two actions for each game and performing validation in a single step at the end, the on-chain footprint is kept minimal, making this approach cost-effective.

Start Game

Breaking down the startGame state transition,

  • The player can submit an action to start a game.

  • The STF deterministically generates a new game ID to uniquely identify a game session, records it in the state and emits the same in execution logs.

What may seem like just a couple of lines of general TypeScript code is actually supercharged by the Stackr SDK adding some interesting properties, such as:

  • Verifiable Randomness is added to the game based on the game ID and used as a seed for generating randomness in the game world. It ensures each game session is unique and prevents cheating, such as using the same list of moves from a previous game.

  • Emitting logs allows linking side effects to a particular action’s execution. Since micro-rollups execute actions instantly upon arrival (within milliseconds), these logs can be returned in response to the very same request. This enables a familiar Web2-style request-response flow, facilitating easier implementation on the frontend.

End Game

Breaking down the endGame state transition

  • After a player completes a session on the client, aka “plays the game”, an action is sent to the micro-rollup to end the game, referencing the related gameId. The request also includes the score, and all their recorded keystrokes as a serialized list (gameInputs) according to a custom action schema.

  • The STF locates the specified game within the state and performs basic checks, such as verifying the player's address.

  • The STF then initiates the game world, passing the gameId and applying the player's inputs to it.

  • Finally, if the simulated score matches the submitted score, it is recorded in the state. Otherwise, if the scores don’t match, the action’s execution fails with no state update.

An interesting detail to note here is that the implementation of GameWorld is independent of the STF. It's a common package shared between the frontend and backend. The core idea is that as long as it's deterministic TypeScript/JavaScript code, it can be imported and used within the micro-rollup’s state machine. This is super cool because it unlocks access to npm, the world's largest software registry, making most (if not all) of your favorite existing libraries now available for building decentralized apps.

Implementation of the Game Logic (spoiler: we didn’t write it ourselves)

This superpower of Stackr SDK became really useful for us because we are not game developers. With some diligent scrounging on GitHub, we were able to find a repository with a JavaScript implementation of Asteroids. Fun fact: it only took us a single day of work to have an initial Proof of Concept implementation where we were able to simply port this package inside the micro-rollup.

Overall, we were able to re-purpose the package as-is with only a few modifications to make it work with the Stackr SDK:

1. We modified the GameWorld class to initialize with a game ID and the screen dimensions. The randomness seed is derived from the game ID. This seed, along with the screen dimensions, affects all operations in rendering the game world, such as the position and size of objects like the spaceship, rocks, aliens, etc. Since the game ID is deterministically generated inside the startGame STF, each game session is verifiably unique.

2. At the end of the game session, we need to send the entire list of all recorded keystrokes as a signed payload to the micro-rollup. Generating a signature on the raw payload and then sending it over to the network could be very slow. Therefore, we came up with a basic serialization technique to convert the list of all input objects into a single compressed string. This string represents the list of all inputs with comma-separated base 10 numbers whose binary representation represents bit-strings, where each position of the bit maps to a particular action. For example, a list of inputs that originally looks like [{ isThrust: true}, { isFire: true }, ...] gets serialized into a string like "32,2,..."

3. Some effort was spent in decoupling the game business logic from pixel rendering logic. This was necessary to keep the WASM minimal and allow for clean re-execution. This, as mentioned above, also allowed us to share this code between the client and server.

Off-chain Game + Micro-Rollup = Endless Possibilities

Verifiable Replays

The beauty of this system is that it allows Verifiable Replays of the game without requiring either of the game state or logic to be on-chain.

As mentioned above, micro-rollup’s state root is settled on L1 (typically, Ethereum). It is interesting to note that the developer can chose what part of the state settles on L1 vs what part can go on DA as metadata, thereby, unlocking hybrid security assumptions.

In this case, we extract the games list from the state and settle it’s merklized root on L1, thereby opening up the possibility of direct inclusion proofs of games in the merkle tree.

Without any extra effort on the developer’s part, the Stackr framework ensures that the micro-rollup’s state machine binary (Wasm) and all the actions to it get published to the DA. The hashes of the Wasm and the genesis state are stored in the AppInbox contract.

Etherscan page showcasing the micro-rollup’s batch submission tx on Sepolia testnet
Etherscan page showcasing the micro-rollup’s batch submission tx on Sepolia testnet
Micro-Rollup Batch data posted as blob on Celestia Mocha testnet
Micro-Rollup Batch data posted as blob on Celestia Mocha testnet

This approach allows anyone to verify the entire pipeline in a fully trustless manner. Anyone can retrieve the Wasm and the actions (game inputs) from the DA, replay these actions, catch up to the latest state, and verify a Merkle proof of the score against the state roots on L1.

Incentivization using Points or Referral Programs

Base’s Onchain Arcade makes use of Stack.so for loyalty points and leaderboard. However, as mentioned, the client is making a mere insecure API call to record the scores. So, again — how can we do better?

Points System Micro-Rollup can interop with Comets Micro-Rollup
Points System Micro-Rollup can interop with Comets Micro-Rollup

Recently, we published an article, Micro-Rollups for Points Systems, which details a micro-rollup implementation of a loyalty points platform. An exciting feature of Stackr is its built-in cross micro-rollup interoperability. This means we can spin up a points micro-rollup and seamlessly bridge users scores from our Comets micro-rollup. This approach would keep the game logic decoupled from the points system, enabling it to be used across multiple games and campaigns, and extended with its own features. In fact, anyone has the freedom to launch their own micro-rollups that interoperate with these two; an example could be a referral system.

The end result would be a set of modular but integrated systems, avoiding ecosystem silos and fostering permissionless innovation.

Btw, we have our own points system in beta → try it out pointy.stackrlabs.xyz, and stay tuned for more announcements on this front! 👀

Scale using ZK Proofs

Currently, Vulcan employs a pessimistic re-execution model to verify incoming blocks from micro-rollups. This leads to a trust centralization vector on Vulcan. To mitigate this, Stackr is actively exploring the potential of zkWASM-based verification, enabling Vulcan to execute application WASM binaries within a zkVM and generate STARK proofs for final verification on the parent chain.

In the context of Comets micro-rollup, this could be implemented in a way that allows the client to generate a ZK-Proof of Gameplay locally on the user’s device. The underlying layers would then only need to verify this proof, providing greater scalability and cost-efficiency.

How to think about Proof of Gameplay for your next game?

While this blog post specifically focuses on the Asteroids game, the concept and implementation of Proof of Gameplay (PoG) using Stackr demonstrated here can be extended to any single-player or even multi-player turn-based games.

By integrating PoG with features like loyalty points and referral systems, developers can create new opportunities for rewards and incentives, backed by verified gameplay achievements. This can enhance player engagement and trust in the game's fairness.

Moreover, having Proof of Gameplay on-chain opens up new possibilities for integrating in-game assets and currencies onto the blockchain. By backing in-game asset and currency acquisitions with verified gameplay, developers can prevent fraud and maintain the integrity of virtual economies. This approach naturally supports the development of a thriving secondary market where in-game assets and currencies can be freely traded, with their value determined by the free market rather than the game developers. The result is a system that not only incentivizes players by allowing them to capitalize on their in-game achievements but also fosters a dynamic environment where both developers and players can innovate and create new economic opportunities.

What’s next?

1. Coinbase if you are reading this…

We’d love to explore collaboration with Base and be the choice of game development toolkit for builders in the base ecosystem. Together, we can help developers build authentic on-chain games with genuine “Proof of Gameplay” and unlock a new paradigm in game development with actually on-chain verifiable games. Please reach out to someone from our team :D

2. Proof of Gameplay grants

We’d soon be launching a grants program for builders to use the micro-rollup SDK to build and launch games with proof of gameplay mechanics. Game developers would get a toolkit and additional resources alongside core integration APIs to build some cool shit with our SDK.


Come build with us!

At Stackr Labs, we’re building the toolkit and the necessary infrastructure to build app-specific micro-rollups to supercharge and unlock the DevEx around building apps that bridge the gap between Web2 and Web3. If you’re someone who’s looking to build Web3 apps or games, we’d love to talk to you and find ways to collaborate.

Join our Telegram Community to stay updated with the latest news, or reach out to us on Twitter (X).

FYI

Our SDK is live in open beta for anyone to try out!

Subscribe to Stackr Labs
Receive the latest updates directly to your inbox.
Mint this entry as an NFT to add it to your collection.
Verification
This entry has been permanently stored onchain and signed by its creator.