A Zero-Knowledge Implementation of the Game BattleShip powered by Aztec Network's Noir Domain-Specific Language
This repository demonstrates how Aztec Network's domain-specific language (DSL) Noir can be used to create an adversarial incomplete information game on top of the Ethereum Blockchain. The corresponding guides are intended to showcase how to easily implement Noir proofs into a React.js application.
This repo was created using the nplate Noir template.
Check out the following links to provide feedback or ask questions!
- Nplate Noir starter kit: https://github.com/whitenois3/nplate;
- Gitbook: https://noir-lang.github.io/book/
- Awesome Noir: https://github.com/noir-lang/awesome-noir
- Git repo with links to many useful Noir resources
BattleZips-Noir draws inspiration for circuit and smart contract design from the popular board game Battleship. The game mechanics require that player's ship positions be completely unknown to the other player, which is a unique use case of zero-knowledge cryptography in a blockchain context. Circuit and contract structure in BattleZips-Noir is derived from BattleZips V1 where proofs were written in Circom.
Two circuits comprise the project; a game instantiation proof in the form of Board verification, and a game turn proof in the form of Shot verification.
- hash (public)
- A hash of all ships as a public
Field
input. Pedersen hashing offers the most computationally efficient binding commitments for our purposes.
- A hash of all ships as a public
- ships (private)
- A private
Field
array of length 15. Every three elements in the array should be deserialized into the tuple(x, y, z)
for a ship's placement. X is the x-coordinate of the board tile, y is the y-coordinate, and z is a binary value (0 / 1) determining whether or not the ship is placed in a horizontal (0) or vertical orientation (1). Ships order is deterministically [carrier, battleship, cruiser, submarine, destroyer] -> [5, 4, 3, 3, 2] Note: As Noir does not yet support multidimensional arrays, the length 15 array was chosen to proceed. This will likely be changed to a 2D array of lengths[5][3]
when possible in Noir
- A private
The board circuit establishes whether ships have been placed correctly on a player's board. The board is abstracted from a 10x10
grid where a coordinate on the board can be thought of as an (x, y)
point (where x and y values range from 0..9
. i.e. top left is (0, 0)
and bottom right is (9,9)
.
The proof first checks to see that all ships are in board boundaries by ensuring none of the ship coordinates are greater than 9 or less than 0. It will then check whether or not there are any collisions where ships are placed on the same coordinates. Lastly, it will compute the Pedersen hash of all ship positions internally in the proof and ensure the computed cash equals the public input.
- hash (public)
- A hash of all ships as a public
Field
input. The hashing algorithm of choice for this implementation was Pedersen although Mimc could have also been used with more constraints.
- A hash of all ships as a public
- hit (public)
- Binary value where 0 represents a miss and 1 represents a hit for the provided shot
- ships (private)
- A private
Field
array of length 15. Every three elements in the array represent the(x, y, z)
values of a ship.
- A private
- shot(public)
- A public Field array of length two that represents a shot. Index zero corresponds to the x-coordinate and index one corresponds to the y-coordinate
This circuit will first ensure that the shot coordinates fall within the bounds of the board. Next, it will check that the ship array computes a matching Pedersen hash as the one passed in as public input. Lastly, with the ship array the circuit will determine whether the shot results in a hit and constrain that again the hit declaration public input.
The following steps will help guide you through how you would set up BattleZips-Noir from scratch, and can be used for reference when setting up arbitrary circuits
See guide to install binaries here
See guide to install from source here
git clone [email protected]:BattleZips/BattleZips-Noir.git
yarn
---
npm install
In order to prove and verify proofs we must ensure that our circuits have witness files generated to demonstrate valid input to a proof. Additionally, public inputs must be written to the Verifier.toml file. The witnesses and verifier files can be generated by running:
yarn generate-witnesses
---
npm run generate-witnesses
Once the witnesses and verifier inputs have been generated then it is easy to prove and verify proofs with nargo. To prove all once must do is navigate the root directory of a proof where the Nargo.toml file is and run:
nargo prove <arbitray_file_name>
this will generate an proof file in the form <arbitray_file_name>.proof
. To verify the proof run:
nargo verify <arbitray_file_name>
Note: You can also compile noir proofs using nargo and generate acir files by entering the following command:
nargo compile <arbitray_file_name>
The acir will be written to build/<arbitray_file_name>.acir
In the BattleZips-Noir tests we generate the acir with typescript so the step above is not necessary to have generated acir for testing. Still useful to know though!
- Create accounts on etherscan and infura and get respective API keys.
- Generate a mnemonic, use the derivation path m/44'/60'/0'/0 (same as Metamask) to get the address that will be used as deployer. Fund that account (i.e. using a faucet like goerli faucet)
Rename .env.example to .env and update with the relevant values:
ETHERSCAN_KEY=<an etherscan API key>
INFURA=<an infura API key>
MNEMONIC=<the mnemonic for your account>
Run test for the circuits with the following command:
yarn test-circuit # runs locally
---
yarn test-circuit:goerli # runs on goerli
to test the BattleshipGame contracts with Verifier contracts linked in run:
yarn test-game # runs locally
---
yarn test-game:goerli # runs on goerli
Once the .env file has been updated then deploying contracts is easy. Simply run:
yarn deploy
---
npm run deploy
This will then write the deployed contracts addresses to contracts.json
in the deploy directory. After deploying the script will then try to verify all the contracts on etherscan. If the script is interrupted then it can pick up where it left off on the deployed contracts by running:
yarn verify
---
npm run verify
The Aztec Backend packages required for generating proofs do not currently work perfectly in frontend generated using create-react-app. In order to get things to run we need to pass over the acir and circuit files for a specific circuit as well as port over the corresponding wasm files to the aztec backend packages. The process has been simplified to one command. All that needs to be run in the root directory of the app is:
yarn generate-frontend-files
---
npm run generate-frontend-files
This command will generate an acir and circuit file for each specified proof as well as copy the the @noir-lang
package wasm files out from node_modules
. These files will then get written to a subdirectory of deploy called frontend
. From here they can simply be copied to the public directory of the BattleZips-Noir frontend
BattleZips-Noir is a standalone project from all other releases under the BattleZips IP. Association between Mach 34 and Aztec in BattleZips-Noir confers NO association between Mach 34 and any third parties engaging with the IP through projects like BattleZipsV2.