Blade Games launches ZK game engine: creating trustless games
Author: Blade Research, Delphinus Lab
Trustless Game will lead the next wave of on-chain gaming.
TL;DR:
Blade Games and Delphinus Lab have collaborated to create a trustless zk game engine based on WebAssembly and zkWASM.
Our zk game engine supports low-latency real-time game genres such as tower defense and RPGs, as well as games like idle games, card/autobattler games, and interactive novels. In simple terms, we run the game logic inside zkWASM (a "zk server" that handles computations), and each game's outcome will generate zkSNARK proofs for publication. This game engine also supports languages like C++, Go, and Rust, with C# and Unity support coming soon.
Taking tower defense games as an example, for a typical 6-minute long game with 100 waves of monsters, the total zkSNARK proof generation time is about 3 minutes. This is just a preliminary result, and we are rapidly optimizing the proof generation time. (The ZKP generation time for 1 million instructions is 19 seconds, with each monster wave consisting of 80,000 instructions, totaling 8 million instructions per game, and 8 zkSNARK proofs generated on a cloud computing end, taking about 3 minutes).
In trustless games based on ZKVM, maintenance costs mainly come from ZK proof generation, RPC/calling data access services, and on-chain verification and settlement fees. With the Cancun upgrade (EIP 4844) taking effect on L2, the cost of running trustless games has significantly decreased.
Moreover, by implementing zkSNARK proof recursion and Nebra's proof aggregation service in the future, we can further reduce the costs of ZKP.
Our game partners include:
Dune Factory (@BladeGamesHQ): A wasteland punk style base-building + tower defense strategy game
0xPioneer: A multiplayer online survival simulation game similar to "Don't Starve"
Craftpunk: A space-themed open-world RPG with customizable spaceships and procedurally generated maps
Main Text:
This ZK game engine is jointly developed by Blade Games and Delphinus Lab, and this article is co-authored by both parties to help more web2 game developers and on-chain game developers understand the advantages and development paths of the ZK game engine.
This is a guide to developing trustless Web3 browser games. (Additional content is showcased at https://www.youtube.com/watch?v=dLZbfTWLGNI, with annotations at https://delphinuslab.com/wp-content/uploads/2023/04/zksummit-presentation-zkwasm-game-1.pdf)
As Web3 continues to evolve, on-chain games are back in our sights. They claim to be superior in decentralization, transparency, trustlessness, and community governance. However, on-chain games also inherit the decentralization, security, and scalability dilemmas of blockchain—this means game developers face challenges in managing game content, interaction frequency, decentralization, trustlessness, and community fairness in the narrative of on-chain games.
Therefore, in the last cycle, game developers reached some compromises at the architectural level and introduced a practice widely referred to as the Web2.5 game architecture.
More specifically, Web2.5 is a comprehensive term that blends Web3 and traditional gaming. Web2.5 emphasizes gameplay content because they believe the primary audience for games is still rooted in Web2. At the same time, they add Web3 elements (NFTs, economic models, play-to-earn) to make their games stand out.
A standard Web2.5 game architecture might look like this:
The left side of the diagram shows the game engine's control over the game's state machine and its response to player activities. The right side of the diagram shows certain changes in the game state and reveals the most valuable data on-chain.
The core gameplay mostly runs on a centralized game server, while the most valuable data (NFTs, token rewards, records, etc.) is tracked on the blockchain.
The advantage of this architecture is that the game server can operate in a centralized mode while processing a large number of user transactions, which can be completed in seconds. Additionally, centralized servers can handle complex and ongoing gameplay, which would typically be too costly to manage in a native blockchain.
However, in this architecture, the communication channel between the game engine and the on-chain protocol is often protected by signatures, so it is not truly trustless. Furthermore, game content may be changed without community consensus, which can sometimes harm the vested interests of existing players (e.g., game economy updates, content updates, or even reward system updates).
Moreover, it is difficult to verify whether the data passed to the blockchain is a valid result of gameplay. Servers can exploit their position to treat player behavior differently (e.g., favoring private accounts of game developers).
While Web2 games often excel in content and gameplay, it is acceptable to leave balance and fairness to game providers. However, when Web2 games decide to enter the Web3 ecosystem, they may need to attract more crypto-native players who care about economics, ownership, and capturing value during gameplay. These players not only enjoy the process of achieving rich outcomes through games but also want the results they achieve in the game to have certain significance (e.g., "sustainability") and even appreciation attributes. Relatively, this expectation for a "sustainable" appreciation effect makes players take their choices in the game more seriously—this means that players have higher thinking and decision-making costs in the game, further deepening their expectations for fairness and predictability of game rules.
Ultimately, players will demand some way to control the "Web3 characteristics" of the game—fairness and trustlessness should not be executed by operators/game developers but by a set of written code, thus achieving a more decentralized, fully on-chain game.
Regarding this, a simple measure is to move all the content on the left side of the above diagram to the right side (the blockchain), resulting in an architecture that looks like this:
Clearly, this move would lead to many "degradations" in gameplay:
- Every action by the player must be represented by an on-chain signature to indicate their authorization.
- The scale of the game engine running on the blockchain is limited.
- A significant amount of gas fees must be paid.
- The frequency of player interactions with the game must be reduced to accommodate the blockchain's TPS (Transactions per Second) limitations.
Does this mean we must give up complex yet rich content in pursuit of the "full-chain philosophy"?
Before the advent of zero-knowledge virtual machine technology, the answer might have been "yes." However, given that zero-knowledge virtual machine technology is now widely researched and applied, we have a "third way" that combines fully on-chain games with trustless computation. How is this achieved?
ZKVM, or zero-knowledge virtual machine, is a concept that combines zero-knowledge proofs with virtual machine technology. To understand it, let's break down the following two modules:
- Zero-Knowledge Proofs (ZKP): These are cryptographic methods that allow one party to prove to another that they know a value (e.g., a key) without revealing any information about that value. Zero-knowledge proofs can protect privacy and security in transactions or interactions because they can verify the truth of a statement without sharing the actual data.
- Virtual Machine (VM): A virtual machine is a software emulation of a physical computer. It runs operating systems and applications—just like a real computer, but entirely implemented in software. Virtual machines are widely used in cloud computing and for running multiple operating systems on a single computer.
Combining these two points: the zero-knowledge virtual machine (ZKVM) is primarily a virtual machine that can execute programs or contracts while providing the privacy and security advantages of zero-knowledge proofs. This means we can run the game engine (or game server) inside the zkVM and use the zkVM to generate ZK proofs to prove to the blockchain that the execution results of different state data are enforced by game logic. Therefore, the game server has no way to adjust the data sent to the underlying blockchain.
In light of this, the comprehensive architecture of on-chain games may look like this:
We refer to such trustless on-chain games as: Trustless Game.
2. Factors to Consider When Developing Trustless Games
2.1 Beginner's Guide
Game development is often considered a challenging task because it involves complex technologies, precious creativity, and project management issues. When applying ZKVM technology to Trustless Games, the factors that may be involved are as follows:
Technical Complexity: In Trustless Games, the game logic needs to be separated from the game's visualization, and the logic must be deterministic so that ZKVM can generate a proof. Additionally, since proofs are generated on segments of code that need to be executed, game developers need to break gameplay into executable segments and regularly synchronize execution with on-chain contracts.
Art and Design: Generally speaking, the work of artists and designers in Trustless Games is not different from that in other games, as their work does not belong to the logic that needs to be proven. In Trustless Games, visualization development needs to be based on the global state of the game, and UI/UX is also used as a tool to collect player activities.
Overall Game Experience: Unlike traditional on-chain games, players do not have to sign on-chain actions with every move, which enhances the possibility of creating "frequent interaction games"—those that require or encourage players to interact frequently.
However, due to the limitations of zkVM proof generation time, high-frequency interactions remain unfeasible for Trustless Games running in zkVM, such as RTS (real-time strategy games) and MOBA (e.g., DOTA), where players must continuously operate units and resources and adjust strategies to defeat opponents. Such types of games are challenging to develop based on zkVM.
Conversely, in simulation games and idle/farming games, players need to periodically allocate resources, engage in market activities, or operate characters to achieve growth in the game—these genres are very suitable for Trustless Games.
Additionally, interactive story games and visual novels are also good choices. These games may not require continuous interaction, but they engage players through constantly emerging decision points, shaping the story, and encouraging frequent interactions to see the outcomes of players' choices.
Next, let's talk about monetization and sustainability.
Generally, the content of games evolves over time to attract new players and retain old ones. This makes the logic of gameplay dynamic, which affects the programs running in zkVM—one result of this is that verification contracts may change and need to be updated.
There are two methods to avoid frequent changes that require ZK verification for contracts:
- Changes in Gameplay: Abstract a protocol layer for gameplay and define these dynamic features of the game as rules. In this way, developers can store these rule sets on-chain and submit these rules to an on-chain hash. Meanwhile, when the game engine runs the game logic, it can first check whether the hash of the current rule set matches the commitment and then act accordingly.
- Changes in Settlement: The reward system can act as an independent layer of gameplay itself. We can view all reward algorithms as callbacks for certain events generated in gameplay. By doing this, we can place the reward callbacks on-chain and call these callbacks based on game events.
For example, we have the following game loop:
zkgame {
// Game logic
output(events)
}
The generated events will become instances of the executed proof. Therefore, we can add callbacks to the contract:
function verify(
bytes calldata events_data,
uint256[] calldata proof,
uint256[] calldata verify_instance,
uint256[] calldata aux,
uint256[][] calldata instances,
RidInfo calldata ridInfo
) public {
…
// Check the events_data pins to a hash in the instances
require(instances.eventshash == hash(eventsdata))
// Performs the zk verification
verifier.verify(proof, verify_instance, aux, instances);
// Call the callbacks to handle the rewarding logic for events
uint256 sideEffectCalled = performtxs(eventsdata, ridInfo.batch_size);
…
}
2.2 Where Should Game Logic Run?
Game logic primarily runs in two places: the frontend or the server side.
Placing game logic on the frontend simplifies the architecture of the game compared to a client-server structure. This approach allows the frontend to simulate the game and generate zero-knowledge proofs (zk-proofs) after establishing the execution trajectory. It then uses a local zk-prover or a remote proof service to generate ZK proofs for the zero-knowledge virtual machine (ZKVM). The proof is then uploaded to the underlying blockchain to trigger the settlement contract.
Conversely, placing game logic on the server side means transferring game simulation and user interactions to a more dedicated component (the game server). This can improve the overall gaming experience in the following ways:
- Better synchronized gameplay: Server-side simulation ensures that all players experience the game in as synchronized a manner as possible. For multiplayer games, a consistent game state across all connected clients is crucial for a cohesive and competitive gaming experience.
- Better resource management: The frontend can focus on content rendering and UI/UX, while the server can act as a centralized sequential controller and provider of Merkle tree storage.
For single-player PVE games (or multiplayer PVP games) that do not require historical data and have simpler sequential logic, placing everything on the frontend is a good choice. For complex games like multiplayer SLG (simulation games) or AW (autonomous world) games, server-side gaming performs better.
2.3 Choosing a Game Development Architecture
Since we are combining traditional game development with ZKVM, we need to carefully consider our tool choices.
- Development Language
First, we need to decide whether to use traditional programming languages like C#, Rust, C, C++, Go to develop the game, or to use a language specific to ZKVM.
The backend bytecode of traditional programming languages is usually MIPS, WASM, RISC-V, x86. Since there is not much ZKVM support for these bytecodes, if your program can compile to RISC-V bytecode, you can choose Risc0 as the underlying ZKVM; if it can compile to WASM, you can choose zkWASM as the underlying ZKVM.
WebAssembly (WASM) is a low-level bytecode format that serves as a compilation target for high-level languages (like C, C++, Rust, etc.). It is designed to run code written in these languages on the web at near-native speed. WebAssembly provides a way to run performance-critical code in web browsers without sacrificing the security or speed of web applications. It is a key part of the modern web technology stack, complementing JavaScript by allowing developers to use other languages for web development.
- Game Engine
Once the programming language is chosen, we can select a game engine based on that language. If a language specific to ZKVM is chosen, it may be necessary to create a custom game development framework, as there may not be a mature framework available for that language. If using languages like Rust, C, or TypeScript, there are many frameworks to choose from, among which we recommend Unity and Cocos2D.
- Proof Generation Costs
Typically, proof costs are measured by the proof time for 1 million instructions. Therefore, it depends on the execution trajectory of gameplay (the number of instructions per gameplay interaction), the word length of the backend bytecode, and the proof performance of ZKVM. For simple instruction sets, some ZKVMs can generate proofs for 1 million instructions in a few seconds (Miden: https://0xpolygonmiden.github.io/miden-base/). For complex instruction sets (RISCV 32-bit, WASM 64-bit), ZKVM can generate proofs for 1 million instructions in GPUs in 12 seconds (Risc0 https://www.risczero.com/) to about 30 seconds (zkWASM https://github.com/DelphinusLab/zkWasm).
3. Using MVC (Model-View-Controller) Pattern for On-Chain Games in ZKWASM
3.1 Introduction to MVC (Model-View-Controller) Pattern
The Model-View-Controller (MVC) pattern, while generally associated with web/enterprise application development, can also be applied to game development—though it requires some adjustments. Here’s how the MVC components operate in a game development environment:
Model:
In game development, the model represents the game's data and logic. This includes game states (such as scores, levels, and player statistics), game objects, and the rules governing the game world. The model is responsible for managing the game's data and state, typically unaware of how this data will be presented or displayed.
View:
In game development, the view is responsible for presenting the game state to the player. This involves rendering game graphics, playing sounds, and displaying UI elements such as scores, life bars, and menus. The view observes the model and updates the visual and auditory representations of the game world to show to the player. In many game engines, the view may be encapsulated within the rendering engine and UI system.
Controller:
The controller interprets user input from keyboards, mice, game controllers, or other input devices and translates it into actions within the game. For example, when a player presses a button to make a character jump, the controller processes this input and communicates the action to the model. The controller in the game acts as an intermediary between input devices and game logic.
Applying MVC in game development has the following significant advantages:
- Separation of Concerns: MVC helps organize code and separates game logic from the user interface, making the development process more manageable and the code easier to maintain.
- Flexibility: MVC allows for different views to be created for the same model. This is very useful for providing multiple perspectives or supporting various display modes in games.
- ZK Friendly: By separating game logic from representation, the model is the only part that cannot be executed in a trustless manner. By placing the model in ZKWASM, the core game mechanics will naturally be proven during game execution.
3.2 Setting Up the Game Engine in ZKWASM
Assuming the controller is connected to the model and has a set of model handlers. Next, we can add a command encoding/decoding layer for the controller and handlers, as follows:
struct GlobalState {
… // Define your game state here
}
enum ControllerTag {
A,
B,
C
}
/// The controller in MVC
fn controller_A (c) {
let handler_cmd = encode(A, c);
model.handle(handler_cmd)
}
fn controller_B (c) {
let handler_cmd = encode(B, c);
model.handle(handler_cmd)
}
fn controller_C (c) {
let handler_cmd = encode(C, c);
model.handle(handler_cmd)
}
/// The model in MVC
fn handler(command) {
match command {
A => {},
B => {},
C => {},
}
}
/// View
fn render(global_state: GlobalState) {
// display your game UI based on global state
}
3.3 Generating a Gameplay Proof
We can view part of the gameplay as a series of calls to the handlers. Therefore, the trustless ZK proof of gameplay is the following code:
fn execution(cs: Vec
for command in cs {
global_state = handler(command);
}
}
During gameplay, it is challenging to determine the last command sent by the controller and difficult to fit all command processing into a single ZK proof. Therefore, the best practice is to split commands into multiple parts and prove them separately, then batch them together to generate a single proof for further verification on-chain.
Note: Please note that there are two missing parts in the above method:
- How to ensure state continuity between each execution segment.
- How to handle scenarios where controllers from different game clients interfere with each other during multiplayer sessions.
In the following chapters, we will briefly introduce multiplayer serialization and data accessibility. Since each topic can be explored in depth, we plan to provide more detailed content in separate notes.
4. Serializing User Interaction Behavior in Multiplayer Games
In the context of modular blockchains, a serializer is a component or node responsible for ordering interactions before their final confirmation. The modular blockchain architecture separates different layers of blockchain functionality (such as execution, consensus, and data availability) into distinct components. This approach aims to improve scalability, security, and efficiency by allowing each layer to be optimized independently.
When developing multiplayer games, we also need a component to help order interactions between different users. Therefore, we repurpose the serialization terminology from modular blockchains.
Since games require low latency, it is best to choose a centralized serializer that produces quick sorting results. The game engine can also work closely with the serializer while obtaining sorted transactions.
Single Player Interaction Protocol
Here (see the diagram below), we describe an interaction protocol from the player's perspective. In user transactions, the user describes their input and proves their identity through public inputs and witness inputs, which can have the following layout:
Public Inputs and Witness Inputs:
Interaction Processing Logic:
pub fn zkmain() -> i64 {
let mut hasher = Sha256::new();
// get the command length
let commandslen = unsafe {wasminput(0)};
// processing all commands and
// hash the commands for future signature verification
for _ in 0..commands_len {
let command = unsafe {wasm_input(0)};
hasher.update(command.tolebytes());
step(command);
}
let msghash = hasher.finalize();
let pk = unsafe {BabyJubjubPoint {
x: U256([
wasm_input(0),
wasm_input(0),
wasm_input(0),
wasm_input(0),
]),
y: U256([
wasm_input(0),
wasm_input(0),
wasm_input(0),
wasm_input(0),
]),
}};
zkwasmrustsdk::dbg!("process sig\n");
let sig = unsafe {JubjubSignature {
sig_r: BabyJubjubPoint {
x: U256([
wasm_input(0),
wasm_input(0),
wasm_input(0),
wasm_input(0),
]),
y: U256([
wasm_input(0),
wasm_input(0),
wasm_input(0),
wasm_input(0),
]),
},
sig_s: [
wasm_input(0),
wasm_input(0),
wasm_input(0),
wasm_input(0),
]};
let msghash_u64 = [
u64::frombebytes(msghash[24..32].try_into().unwrap()),
u64::frombebytes(msghash[16..24].try_into().unwrap()),
u64::frombebytes(msghash[8..16].try_into().unwrap()),
u64::frombebytes(msghash[0..8].try_into().unwrap()),
];
sig.verify(&pk, &msghash_u64);
5. Cost Analysis
5.1 Cost Overview
In trustless on-chain games based on ZKVM, the main maintenance costs come from ZK proof generation, fast data RPC services, calling data access services, and on-chain verification and settlement fees. With the Cancun upgrade (EIP 4844) taking effect on L2, the cost of running trustless games has significantly decreased. Additionally, through the future implementation of zkSNARK proof recursion and Nebra's proof aggregation service, we can further reduce the costs of ZKP.
5.2 ZK Proof Generation Costs
First, ZKVM runs applications and generates execution traces. This trace is relatively basic because ZKVM needs to prove:
- The trace enforces the semantics of the bytecode supported by ZKVM.
- Generally, ZKVM itself is stateless. To support the stateful storage of games, it leverages the idea of converting data setting and retrieval into Merkle proofs.
- ZKVM typically divides the execution trace into multiple segments and generates proofs for them separately. These proofs will be batched together to ultimately generate a verifiable single proof.
Assuming the ZKVM we use employs precompiled instructions as system calls (or host APIs in ZKWASM) to handle Merkle proofs, the overall cost of generating ZK proofs is as follows:
Proof Cost = ZKVM Guest Proof Cost + Merkle Proof Cost + Batch Proof Cost (BatchProofCost)
Typically, the first and third terms on the right side of the equation do not introduce additional costs beyond pure ZK proofs. However, the cost of the second term is somewhat nuanced as it requires support from some data storage services.
Let’s review the components of Merkle proofs:
- Merkle Tree Setup
We abstract global data into blocks and hash them individually. These hashes become the leaf nodes of the target tree. Then, pairs of leaf nodes are hashed to form the next level of nodes, and this process is repeated until there is only one hash at the top, known as the Merkle root. The Merkle root is the unique representation of all data blocks in the tree.
- Data Inclusion Proof
This proof consists of a specific data block from the problem, its hash, and a small number of additional hashes from the Merkle tree. These additional hashes are the minimum required for the verifier to independently compute the Merkle root of the data set.
- Verification Proof:
The verifier knows the Merkle root of the data set but does not necessarily know all the data within it. They use the provided data block and additional hashes to reconstruct the hash path to the Merkle root. If the computed Merkle root matches the known Merkle root, the proof is valid, confirming that the data block is indeed part of the data set and has not been tampered with.
To maintain a stateful ZK game, a Merkle data database is needed to track the leaves of the Merkle tree and provide fast data query services. This service is unlikely to be fully hosted on-chain due to the high frequency of data modifications and high access (read/write) demands.
Once a transaction is completed, the calling data and final state (represented by the new Merkle tree root) need to be accessible. This is typically achieved through a DA layer or on-chain txdata storage.
5.3 Data Costs
According to Blade Games' analysis and referencing ZKWASM, Eth Storage, BNB Greenfield DA storage cost calculator (https://dcellar.io/pricing-calculator), and Google Cloud Storage calculator (https://cloud.google.com/products/calculator), we can conclude that running an on-chain game with 5,000 players (assuming they play continuously, which means the actual daily active users would be much higher than 5,000) using the zk co-processor method is expected to cost about $90,000 per month, a cost similar to running a high-defense, anti-DDoS MMORPG game server.
Blade Games' estimation method is as follows: they convert user events published in Unity into binary code and run them in ZKWASM (ZKWASM is a ZKVM developed by Delphinuslab that supports WASM bytecode). Then, ZKWASM generates execution traces and publishes the compressed traces to the DA layer, while simultaneously publishing the trace hash on-chain and verifying immutability with digital signatures.
Based on ETHstorage and zkwasm's test runs, running a 1-second WASM binary will generate 1 million lines of traces, with each line being 40 bytes in size.
We can choose to prove all traces on-chain or only save the traces, proving them if the user chooses to challenge the results.
- ZK Method for Proving All Traces
If we choose to prove all traces, then 1 million bytecodes will take about 25 seconds on a single RTX4090 graphics card, with costs potentially being 0.5 cents (costing 2000M instructions per dollar). In this solution, costs mainly arise from proof capability fees, on-chain verification fees, and DA's calling data storage fees. Using this method to support a multiplayer game with 10 billion traces per hour would incur about $50 per hour (approximately $36,000 per month) in proof costs.
- Fraud Proof Method
Assuming there are 5,000 players, each playing continuously for 2 hours, and each player generates 864 trillion traces per hour. Therefore, the daily storage consumption would be 60 seconds * 60 minutes * 2 hours * 5,000 players * 1 million bytecodes = 36,000,000,000,000 traces. According to ETHstorage and zkwasm's tests, each trace consumes 40 bytes of storage space. Thus, for a game with 5,000 players (average online time of 2 hours), it would consume 1,440TB of storage space daily.
If we compress the traces at a reasonable ratio of 10 times, we would consume 144TB of Google Cloud storage daily, equivalent to 4,320TB per month, with costs estimated at $90,120/month (in the archival storage layer; the standard storage layer allows for higher data access frequency, costing about $185,000/month). Additionally, the monthly cost of storing signature data on BNB Greenfield is 0.0001 BNB per GB, approximately $0.03. In this method, observer nodes can check the consistency of traces and trigger ZK Fraud proofs to report dishonest transactions.
Based on the above discussion, we can conclude: The total cost of running a large-scale game with many players is similar to the cost of running a high-defense anti-DDoS MMORPG game server.
6. Content Upgrades and Modular Protocols
A successful game typically features dynamic game content, requiring regular updates to its engine. This seems to contradict the popular notion of "code is law" within the crypto-native community.
However, this challenge has potential solutions.
By adopting a design pattern that places maintainable rules on the blockchain, we can ensure that upgrades or modifications to gameplay adhere to these fundamental rules. Given the complexity of this topic in the context of game architecture, we use the following diagram for a concise explanation:
7. Conclusion
With the emergence of zkVM and related infrastructure, on-chain game developers can balance complex yet rich content with the "full-chain philosophy." We believe that such "trustless games" will have a broad market prospect and space.
Combining zkVM with the concept of modular execution layers will also make the game's go-to-market strategy (GTM) exceptionally flexible. Imagine game players interacting simultaneously with popular projects like Eigenlayer, zkWASM, Berachain, receiving airdrops while also earning in-game rewards, which will greatly assist in the early cold start of games.
Regarding readers' concerns about whether "development costs are still high": we believe that in trustless games based on ZKVM, with the Cancun upgrade (EIP 4844) taking effect on L2, the application of recursive proofs, proof aggregation, and the optimization of zkVM itself have significantly reduced the costs of running trustless games. If you are interested in developing Trustless Games, please DM us on Twitter (bladegamesHQ), and we will provide corresponding assistance in game development, code, and GTM.