---
# System prepended metadata

title: zkWasm Demo Game Mini Documentation

---

-----------------------------
## zkWasm Demo game quickstart. 
-----------------------------


## Introduction:

The advent of Web3 technologies, including blockchain and decentralized applications (dApps), has paved the way for a new era of gaming experiences. One exciting development in this space is the emergence of browser-based Web3 games that leverage the power of zk-SNARKs (Zero-Knowledge Succinct Non-Interactive Arguments of Knowledge) to provide enhanced security and privacy.

Web3 gaming aims to create truly decentralized and trustless gaming environments, where players can interact, own digital assets, and participate in fair and transparent gameplay without relying on centralized game hosts or gaming companies that hold databases of user data and in game assets. By leveraging blockchain technology, game assets and in-game economies can be securely managed and owned by players themselves. This opens up the oportunity for real life markets for in game assets.


## Overview

Below we provide a walk-through for the construction of a simple Web3 demonstration game that is interacted with via a web browser. The users gameplay is constructed into a proof that is verifiable on the blockchain via a proving service - Delphinus Labs zkWasm prover and ascoiated wasm cloud explorer. The procedure is broken down into four main components


1. Writing a demo-game in a source languare (Rust).
2. Compiling the demo-game source to wasm using Makefile and copying the files to a game directory.
3. Building the Web Application to interface the game.



### Web3 Application in Web2 form: 

The Model View Controller (MVC) is used throughout the overall architecture of the Web2 interface build on Web3 services. The MVC model in this case, has the View being the game frontend the user sees and interacts with (web application). The  Controller is responsible for processing user operations and manipulating the data. The View is responsible for representing the changes in the data, i.e., the game play. The reason why we take this approach in architecture is becase we want to separate the game mechanics and the mechanism of provably assuring a particular state from the view of that state. The Model and Controlelr are run in the Web Assembily virtual machine, but the View is not.



![mvc-light_25p](https://hackmd.io/_uploads/rkzzWidZC.png)



In this regard the game logic itself can be written in a variety of languages that can be compiled down to WebAssembily bytecode. These optiions include:

Javascript, Typescript, Assembily, C++, Rust for the game development language.

After compilation of the approiate game files, the javascript and typescript files are copied to a web application which renders that user gameplay and global state of the game.


## 1. Demo-game source and zkmain():

In this example demo-game we build the game logic and associated files in Rust which employs some very basic game play logic. We make use of [Delphinus Labs Rust-SDK](https://github.com/DelphinusLab/zkWasm-rust.git) for web assembily imputs and ellipic curve functions. The game simply requires the player to guess a number. 

Demo game source files are:
```
./demo-game/
├── Cargo.lock
├── Cargo.toml
├── Makefile
├── README.md
├── src
│   ├── game.rs
│   ├── lib.rs
│   └── zkmain.rs
└── target
```

Note that the entry point of the zkWasm prover is via the `zkmain()` function in the game source code file `zkmain.rs`.

```Rust
#[wasm_bindgen]
pub fn zkmain() {
    ...
}
```

Within this file we import `zlwasm-rust-sdk` from [Delphinus Labs zkWasm rust](https://github.com/DelphinusLab/zkWasm-rust.git) and use Baby JubJub curve to use for signature creation. We import `wasm_input` for user inputs from the app through to wasm and which is fed onto the prover. 

```Rust
use zkwasm_rust_sdk::jubjub::BabyJubjubPoint;
use zkwasm_rust_sdk::jubjub::JubjubSignature;
use zkwasm_rust_sdk::wasm_input;
```

We also import a function from the `game.rs` file that is our gameplay logic and global state modifier.

```Rust
use super::game::step;
```

Within the `zkmain()` function the player input is obtained by the `wasm_input()` function, the command is hashed and also used in a call to the the game play logic `step()`

```Rust
    let commands_len = unsafe {wasm_input(0)};
    for _ in 0..commands_len {
        let command = unsafe {wasm_input(0)};
        hasher.update(command.to_le_bytes());
        step(command);
    }
```

The above is a basic building block of how a player input is received and processed through the game play logic and used to update the global state.


The game logic for the demogame in particular is in file `game.rs`. This is a very simple game that relies on a player using a guess (a number) and if the number guessed is 11 (as a u64), then the  `GLOBAL_STATE` is set to true.

```rust
/// Step function receives a encoded command and changes the global state accordingly
#[wasm_bindgen]
pub fn step(command: u64) {
    let commands = command.to_le_bytes();
    unsafe {
        wasm_dbg(commands[0] as u64);
    };
    if commands[0] == 11 {
        unsafe { GLOBAL_STATE = true }
    } else {
        unsafe { GLOBAL_STATE = false}
    }
}

#[wasm_bindgen]
pub fn get_state() -> bool {
    unsafe { GLOBAL_STATE }
}

#[wasm_bindgen]
pub fn init(seed: u64) {
}
```

With the game source built in Rust, the next step is to compile the game to web assembilty to sue within the browser based App.

## 2. Compilation of the game (from Rust source) to Web Assembily:

Compiling the demo game source files from directory `./demo-game` branch `td`

`https://github.com/ZhenXunGe/trustless-browser-sandbox`

This requires `wasm-pack` a command line tool for building wasm modules and npm packages. If you dont already have it installed use:
```
cargo install wasm-pack

```

### Compilation of game files: 

Compilation of the source game files to the approiate wasm, .js & .ts files can be done via a Makefile. This will copy the output files to the game directory in the App.

in the demo-game director (`./demo-game`) use the Makefile:

``` 
$ cd ./trustless-browser-sandbox/demo-game
$ make build
    Finished release [optimized] target(s) in 7.43s
[INFO]: ⬇️  Installing wasm-bindgen...
[INFO]: Optimizing wasm binaries with `wasm-opt`...
[INFO]: Optional fields missing from Cargo.toml: 'description', 'repository', and 'license'. These are not necessary, but recommended
[INFO]: ✨   Done in 8.28s
[INFO]: 📦   Your wasm pkg is ready to publish at /home/user/Workspace/trustless-browser-sandbox/demo-game/pkg.
cp pkg/gameplay.d.ts ../src/games/demogame/js/gameplay.d.ts
cp pkg/gameplay_bg.wasm ../src/games/demogame/js/gameplay_bg.wasm
cp pkg/gameplay_bg.wasm.d.ts ../src/games/demogame/js/gameplay_bg.wasm.d.ts
cp pkg/gameplay_bg.js ../src/games/demogame/js/gameplay_bg.js
[INFO]: 📦   Your wasm pkg is ready to publish at crates/core/pkg.
```

After compiling the game source, you will see the package files containing the main web assembily `.wasm` and the type definitions and loading script for the browser application.

```
├── pkg
│   ├── gameplay_bg.js
│   ├── gameplay_bg.wasm
│   ├── gameplay_bg.wasm.d.ts
│   ├── gameplay.d.ts
│   ├── gameplay.js
│   └── package.json
```

These files are copied to the target React web App frontend for the particular game in the diretory `./src/games/`. In the case of the demo-game the fiels are copied to: `./src/games/demogame/js/`



## 3. Web App:

The web application serves as the "View" portion of the MVC model. It is a browser based representation of the global state of the gameplay. It is also a user interface to submit the proof task once gameplay has finished.

In the Web app, a typical file structure for a Reat app looks similar to the below:

Root directory of Web App:

```
├── src
│   ├── app
│   ├── App.css
│   ├── App.tsx
│   ├── assets
│   ├── components
│   ├── data
│   ├── fonts
│   ├── games
...
```

The game files from the build (above) are copied to the directory:

```
./src/games/demogame/    .
    ├── controller.tsx
    ├── images
    │   ├── bg.png
    │   ├── ...
    ├── js
    │   ├── config.js
    │   ├── game.js
    │   ├── gameplay_bg.js
    │   ├── gameplay_bg.wasm
    │   ├── gameplay_bg.wasm.d.ts
    │   ├── gameplay.d.ts
    │   └── package.json
    └── style.scss
```

`config.js` simply contains the MD5 sum of the `.wasm` image itself.
`gameplay_bg.d.ts` 
`gameplay_bg.wasm.d.ts` contains function declarations for from the source game.
`gameplay_bg.js` contains function definitons from the source game.

### Initial State and Processing Key

The Processing Key is used in proof submission. Upon each new run of a game, the user is asked to sign a message (the selected wallets account public key) via their connected browser extension wallet.  

The below shows a basic outline of the `demo-gmae` displaying the start screen waiting for the user to hit "start" ans sign the emssage to initialize the game state.

```typescript

return (
    <Container className="mt-5" style={{ display: 'flex', flexDirection: 'column', alignItems: 'center' }}>
      {!l2account && (
        <div
        className="load-game"
        style={{
          display: 'flex',
          justifyContent: 'center',
          alignItems: 'center',
          flexDirection: 'column',
        }}
      >
        <img
          src={stscrn[0]}
          style={{
            width: '794px',
            height: '529px',
          }}
        />
        <button
          className="btn btn-confirm"
          onClick={() => dispatch(loginL2AccountAsync(account!))}
        >
          Start Play
        </button>
      </div>
      )}
      {gameLoaded && state && 
          <div>
          ... gameplay view ...
          </div>
      }
    </Container>
  );
```

### The game Controller

The game controller serves as the main renderer for the game View - this is in file `controller.tsx`. The controller processes commands from user input and renders the state to the Web app.

specific imports for the game are done via 

```typescript
import init, * as gameplay from "./js";
```

with exports from `src/games/demogame/js/gameplay.d.ts` being:
```typescript
export function zkmain(): number;
export function get_state(): number;
export function init(a: number): void;
export function step(a: number): void;
```

The boilterplate for a Controller is as follows, with the `GameController` functions being the main entry point form the App `Main.tsx`
```typescript    
export function GameController() {

      const l2account = useAppSelector(selectL2Account);
      const gameLoaded = useAppSelector(selectGameLoaded);
      const [state, setState]  = useState<string>("false");
      const [currentNumber, setCurrentNumber] = useState(0);

    function initGame(){...};
    function guessNumber(){...};
    function endTurn(){...};
    function otherGameFunction(){...};

    return (
        "take user input and render components for the game view itself."
    )

}
```


In the demogame, the function that forms the interface between the game logic and the controller is `guess_number()`.

```typescript
  function guess_number(guessNumber: number) {
    (init as any)().then(() => {
        gameplay.step(BigInt(guessNumber));
        const globalstate = gameplay.get_state();
        if (globalstate == true) {
            setState(`${currentNumber} Correct! 🎉 `);
            dispatch(appendCommand(BigInt(guessNumber)));
            console.log("end guessing");
            dispatch(setReadyToSubmit(true));
        } else {
            setState(`${currentNumber} not correct - try again!`);
        }
    });
```

The line: `gameplay.step(BigInt(guessNumber));` calls the step function from the game source with the users "guessed" number `guessNumber`, 

Im the game logic the `step()` function is as follows:

```Rust=
#[wasm_bindgen]
pub fn step(command: u64) {
    let commands = command.to_le_bytes();
    unsafe {
        wasm_dbg(commands[0] as u64);
    };
    if commands[0] == 11 {
        unsafe { GLOBAL_STATE = true }
    } else {
        unsafe { GLOBAL_STATE = false}
    }
}
```

This function receives a command which is used within "some logic" of the game itself to modify the global state of the game. When we say "some logic" above, this can be anything that the game is doing, a feature of the gameplay itself. In the above example the `step()` function simply checks is the user command, which us a unsigned 64 bit number, is the "secret" value of 11 or is not, setting the `GLOBAL_STATE` as true or false as a result.

Immediately after this call the state of the game is then returned to the app via `get_state()` 

When the gameplay is finished a proof containg the steps of the gameplay can be sent to the zkWasm proving service.

### readytoSubmit:

As part of the `guess_number()` function in the controller, when the global state equals true, i.e., when the guessed number is correct, the `setReadyToSubmit` action is dispatched to set the readyToSubmit state to true (see src/data/game.ts). This critical part updated the game handler that the game is ready to have a proof submitted.


## 4. Proving Service:

After gameplay has finished the player can submit a proof task to via Delphinus Labs zkWasm prover and a smart contract can be verified on chain.


![Selection_041](https://hackmd.io/_uploads/S1yZifa1A.png)

"Create Proof" asks the user to sign the message containing the Movements, MerkleRoot, PubKey and Signature information.

![Selection_042](https://hackmd.io/_uploads/ry5g_NpyR.png)



## Smart contract & verification

After gameplay the player is rewarded through a smart contract/ verification prcedure. (Proxy.sol) which uses callbacks for the accepted proof. The `verify()` function 
verifies the proof at L240 and call the side effect function L242. The calldata is user data.

```sol    
    /*
     * @dev Data encodes the delta functions with there verification in reverse order
     * data = opcode args; opcode' args'; ....
     */
    function verify(
        bytes calldata tx_data,
        uint256[] calldata proof,
        uint256[] calldata verify_instance,
        uint256[] calldata aux,
        uint256[][] calldata instances,
        RidInfo calldata ridInfo
    ) public {
        require(rid == ridInfo.rid, "Verify: Unexpected Request Id");

        // [0]: old root, [1]: new root, [2]: sha_low, [3]: sha_high

        require(
            tx_data.length == OP_SIZE * ridInfo.batch_size,
            "Verify: Insufficient delta operations"
        );

        uint256 sha_pack = uint256(sha256(tx_data));
        require(
            sha_pack == (instances[0][2] << 128) + instances[0][3],
            "Inconstant: Sha data inconsistant"
        );

        require(
            merkle_root == instances[0][0],
            "Inconstant: Merkle root dismatch"
        );

        verifier.verify(proof, verify_instance, aux, instances);

        uint256 sideEffectCalled = perform_txs(tx_data, ridInfo.batch_size);

        uint256 new_merkle_root = instances[0][1];
        merkle_root = new_merkle_root;
        rid = ridInfo.rid + ridInfo.batch_size;
        emit Ack(ridInfo.rid, sideEffectCalled);
    }
```


#### Versioning:

v0.1: initial doc, 29 March 2024.
v0.2: update processing key, readytosubmit, 05 April 2024.