This repository provides a fully standalone mock environment for working with Proof-of-Data (POD) and General Purpose Circuits (GPC) (Documentation). It includes the necessary tools and scripts to create PODs, manage GPC circuit parameters, compile circuits, generate proofs, and verify proofs off-chain. On-chain verification capabilities are planned for future integration.
The environment includes pre-generated mock POD data, allowing developers to experiment with the proof generation and verification workflow without needing external data sources initially. Circuit discovery (find-circuit) operates entirely on locally compiled artifacts.
This guide outlines the steps required to set up the Pod Flow development environment.
- Node.js: Version 18 is required (as specified in the root
package.json). We recommend using nvm (Node Version Manager) to manage Node.js versions. - pnpm: Version 8 or 9 is required. Install it globally via
npm install -g pnpmif you don't have it. (https://pnpm.io/installation) - Rust & Cargo: Required for compiling the Circom compiler. See the "Install Circom" section below if you don't have them installed.
- Git: For cloning the repository.
- Memory: While not a strict requirement for all operations, GPC circuit compilation (
compile-circuit) and proof generation (gen-proof) can be memory-intensive. The scripts are configured to request up to 32GB of RAM (16GB each for circuit compilation and proof generation) (--max-old-space-size=32768). Ensure your system has sufficient available memory for these steps, especially when working with complex proofs.
1. Clone the Repository
git clone https://github.com/projectawakening/pod-flow # Replace with your repo URL if different
cd pod-flow 2. Install Dependencies
This project uses pnpm workspaces. Running pnpm install from the root directory will automatically:
- Install all dependencies listed in the root
package.json. - Install dependencies for each individual package located in the
packages/*directories (e.g.,@pod-flow/client,@pod-flow/gpc, etc.). - Link local packages together, so packages can import each other correctly (e.g.,
@pod-flow/examplescan import from@pod-flow/gpc). - Install
snarkjs, which is needed for Zero-Knowledge proof operations, as a root dev dependency.
pnpm install3. Install Circom (Rust Version)
This project requires the Rust version of the Circom compiler (v2.x.x or higher) for compiling Zero-Knowledge circuits.
-
(a) Install Rust and Cargo (if needed): Circom v2 is built with Rust. If you don't have Rust installed, the recommended way is via
rustup:# Install rustup (follow on-screen prompts) curl --proto '=https' --tlsv1.2 https://sh.rustup.rs -sSf | sh # Once installed, ensure cargo is in your PATH. # You might need to restart your terminal or run: source "$HOME/.cargo/env" # Verify installation rustc --version cargo --version
(See the official Rust installation guide for more details: https://www.rust-lang.org/tools/install)
-
(b) Clone and Build Circom: Clone the official Circom repository and build the release version:
# Clone the repository (can be outside your project) git clone https://github.com/iden3/circom.git # Navigate into the cloned directory cd circom # Build the release binary (this may take a few minutes) cargo build --release
-
(c) Install Circom Binary: Install the compiled binary into your user's Cargo bin directory:
# Make sure you are still inside the cloned 'circom' directory cargo install --path circom -
(d) Verify Installation and PATH: Ensure your system uses the correct Rust version (
2.x.x).# Clear the shell's command cache hash -r # Check the version circom --version
- Expected Output:
circom compiler 2.x.x(e.g.,2.2.2). - If you see
0.5.x: An older version is being found first in your PATH. Runwhich circomto find it. Either fix your PATH environment variable (in~/.zshrc,~/.bash_profile, etc.) to prioritize$HOME/.cargo/bin, or carefully remove the older version. Runhash -randcircom --versionagain after fixing.
- Expected Output:
4. Fetch Powers of Tau File
Zero-Knowledge proofs require a "Powers of Tau" (.ptau) file generated from a trusted setup ceremony. This file contains universal parameters needed for circuit compilation. Since these files are large, we fetch it using a script instead of committing it to Git.
This project is configured via packages/gpc/gpc-compile-config.json to use pot22 (supporting up to 2^22 constraints), which is suitable for the expected complexity of large GPC circuits.
Run the following command from the root of the project:
pnpm setup:gpc-ptauThis will execute the script in packages/gpc/scripts/fetch-ptau.ts, which reads the configuration and downloads the necessary file (e.g., powersOfTau28_hez_final_22.ptau) to the directory specified in circomkit.json (default: packages/gpc/ptau/).
-
Note: This is a large file (~4.5GB), so the download may take some time depending on your network connection. The script will show progress. If the file already exists, the script will skip the download. You can customize smaller version if you would like but we have opted to accomodate very large proofs.
-
You can find a complete list of other available
.ptaufiles here: https://github.com/iden3/snarkjs#7-powers-of-tau
5. Generate Authority Key (Optional)
The project uses an EDDSA authority key and public key (packages/contracts/.env) which can be fetched and used via helper functions in packages/pods/utils/fsUtils.ts.
IMPORTANT: to facilitate this we have left the .env files exposed to the github repo, be very careful with this behaviour. NEVER store or commit production keys to these files!
If you wish to generate your own authority key pair (e.g., for testing signing with a different identity), you can run the following script from the root directory (after deleting the existing keys from the .env file):
node scripts/generateAuthorityKey.js- IMPORTANT: If you generate a new set of keys, the pre-existing mock POD data in
packages/*/pod-data/(which was signed with the original key) will no longer verify successfully if you try to validate their signatures against the new public key. You would need to re-generate the mock PODs using the scripts in@pod-flow/podsor@pod-flow/examplesand sign them with your new key. Use the default key if you want to work with the provided mock data without modification.
This project is a monorepo managed by pnpm. It consists of the following packages:
- Description: Contains various example implementations demonstrating how to use the Pod Flow GPC system. Each sub-directory typically focuses on a specific use case, providing necessary scripts, proof configurations, and parameter generation.
- Key Examples:
location-bounding: Demonstrates proving proximity based on PODs representing ship and object locations, along with a distance assertion POD.inventory-verification: Demonstrates proving ownership of a specific item type from a pre-defined set, using inventory and item type PODs.
- Run Mock Flows: Each example typically provides a
run-mockscript in theexamplespackage'spackage.json(e.g.,location:run-mock,inventory:run-mock) that orchestrates the necessary steps (parameter generation, proof generation) for that example.# Example: Run the full location bounding mock flow pnpm run location:run-mock # Example: Run the full inventory verification mock flow pnpm run inventory:run-mock
For example, the location command will:
- Execute
location:gen-params(script defined inpackages/examples/package.json) to create/updatepackages/examples/location-bounding/pod-data/location_proof_pods.json. - Execute
location:gen-proof(script defined inpackages/examples/package.json), passing the default paths./location-bounding/proof-configs/locationProofConfig.tsand./location-bounding/pod-data/location__porrof_pods.json(relative to theexamplespackage) as arguments. - The
location:gen-proofscript (packages/examples/location-bounding/scripts/generateLocationProof.ts) then orchestrates the full GPC workflow:-
Generates requirements (
gen-proof-requirements). -
Generates structured proof inputs (
gen-proof-inputs). -
Finds the best-fit existing circuit (
find-circuit). -
Optionally compiles a new circuit if needed (
compile-circuit). -
Optionally adds the new circuit to the known set (
add-circuit-params). -
Generates the final proof (
gen-proof). -
Copies the generated proof to
packages/examples/location-bounding/proof-output/. -
Verifies the generated proof (
verify-proof).
-
This provides a streamlined way to test the location proof use case from end-to-end.
- Description: A boilerplate React front-end template designed for building User Interfaces (UI/UX) for MUD (mud.dev) decentralized applications. (Functionality TBD)
- Technology: React, Vite, MUD client libraries (
@latticexyz/...). - Key Scripts:
dev(starts dev server),build(builds for production).
- Description: Contains the on-chain smart contracts, primarily integrating with the MUD framework (EVE Frontier World V2 contracts coming soon). This package will eventually hold the Solidity verifier contract generated from our ZK circuits.
- Technology: Solidity, MUD (
@latticexyz/world,@latticexyz/store), Foundry (for testing/deployment viaforgeandmud). - Key Scripts:
build(compiles contracts),deploy:local(deploys to local node),test(runs Forge tests).
- Description: Manages Proof-of-Data (POD) generation utilities and contains shared mock data. It includes scripts to fetch real-world game data (currently targeting the "Stillness" environment data via ESI-like APIs) and convert it into signed PODs.
- POD Data Format: Shared mock POD data (e.g.,
item_type_pods.json) is typically stored as a JSON object where keys are the POD'scontentIdand values are the fullJSONPODobjects (including entries and signature). - Technology: Node.js, TypeScript,
@pcd/podlibrary (GitHub),dotenv(for API keys if fetching data). - Key Scripts: Various
fetch-*andgenerate-*-podsscripts.initialize-game-dataandupdate-game-dataorchestrate fetching.generate-all-podscreates PODs from fetched data. - Note: Re-fetching and generating new mock data, especially for all solar systems (~24,000) and their assemblies, can be very time-consuming. Using the provided mock data (located in
packages/*/pod-data/) is recommended for initial development and it is not necessary to run the data fetching scripts to use the core GPC proof generation workflow.
When converting raw game data into PODs, certain numeric values require special handling to be compatible with POD int types (which have int64 limits) and to enable numeric comparisons within GPC circuits:
-
Location Coordinates (x, y, z):
- Problem: Raw location coordinates can be very large integers (represented as strings in the API) that exceed the
int64range supported by the PODinttype. Storing them as strings prevents numeric comparisons (bounds checks, inequalities) in GPC circuits. - Solution: A 2-limb representation is used. Each coordinate (x, y, or z) is split into a
_highand_lowpart, each stored as a separateintentry in the POD (e.g.,location_x_high,location_x_low). This effectively allows storing values up to 2^126 while remaining compatible with theint64circuit inputs. Helper functions for converting betweenBigIntand this 2-limb format exist inpackages/pods/utils/podBigInt.ts. - Benefit: Enables range checks and numeric comparisons on location coordinates within GPC proofs.
- Problem: Raw location coordinates can be very large integers (represented as strings in the API) that exceed the
-
Radius, Mass, and Volume:
- Problem: These values can appear as integers or floating-point numbers in the source data. PODs do not have a native float type, and storing them as a mix of
intandstringprevents consistent numeric operations. - Solution: Fixed-point arithmetic is used. All these values are multiplied by a fixed factor (currently 10^5, based on observing a maximum of 5 decimal places in the source data) and stored as integers (
inttype) in the POD. Helper functions for this conversion are available inpackages/pods/utils/podFixedPoint.ts. - Benefit: Allows all radius, mass, and volume values to be treated numerically within GPC circuits, enabling bounds checks and comparisons.
- Problem: These values can appear as integers or floating-point numbers in the source data. PODs do not have a native float type, and storing them as a mix of
Essentially, these adjustments ensure that critical numeric data from the game can be represented and utilized effectively within the constraints and capabilities of the POD and GPC ecosystem.
- Description: Handles the General Purpose Circuits (GPC) workflow (GitHub). This includes defining circuit parameters, managing circuit artifacts (like
.r1cs,.wasm,.zkey), providing example proof configurations, and scripts for compiling circuits and generating inputs/proofs (located inpackages/gpc/scripts/). It's base family relies on the underlying circuit implementations from@pcd/gpcircuits(GitHub). - Technology: Node.js, TypeScript,
@pcd/gpc,@pcd/gpcircuits,circom(external compiler),snarkjs. - Key Scripts:
gen-proof-requirements,find-circuit,compile-circuit,add-circuit-params,gen-proof-inputs,gen-proof,verify-proof,setup:fetch-ptau.
The core workflow for generating a proof for a specific use case involves several steps, primarily orchestrated by scripts within the @pod-flow/gpc package. Here's a typical sequence when running steps manually (commands assume execution from the workspace root):
Intended Logic: The general approach for handling circuits is:
- Calculate the specific requirements for a given proof config and input set (
gen-proof-requirements). - Search the existing, locally compiled circuits for the smallest one that meets these requirements (
find-circuit). - If a suitable circuit is found, use its artifacts for proof generation.
- If no suitable circuit is found:
- Compile a new one based on the exact requirements (
compile-circuit). - Add its parameters to the known set (
add-circuit-params) to make it discoverable. - Use the new artifacts for proof generation.
- Compile a new one based on the exact requirements (
Manual Steps:
-
Parameter Generation (
packages/examples/.../scripts/generate*Params.ts):- Purpose: Each example provides a script (e.g.,
generateLocationParams.ts,generateInventoryParams.ts) responsible for creating the necessary input PODs and structuring them into a standardizedparams.jsonfile required by the GPC workflow. params.jsonStructure:pods(object): Contains the actual signedJSONPODdata for all PODs needed by the proof, keyed by theircontentId.podConfigMapping(object): Maps the logical config keys used within the correspondingGPCProofConfigfile (e.g., 'object', 'ship', 'inventory', 'keyItem1') to the actualcontentIdof the POD within thepodsobject. This allows GPC scripts to link the configuration constraints to the correct data.membershipLists(object, optional): Contains named lists ofPODValues orPODValue[][](tuples) used for membership/non-membership constraints (isMemberOf,isNotMemberOf).owner/watermark(object/PODValue, optional): May contain identity information or a unique value, potentially includingPODValues.
- Serialization: These parameter generation scripts must serialize
PODValueobjects (especially those containingBigInt) into a JSON-compatible format before writing theparams.jsonfile using helpers likepodValueToJSON. Conversely, GPC scripts reading this file must deserialize them back usingpodValueFromJSON. - Example:
# Run parameter generation for the location example pnpm run location:gen-params # Output: packages/examples/location-bounding/pod-data/location_proof_params.json # Run parameter generation for the inventory example pnpm run inventory:gen-params # Output: packages/examples/inventory-verification/pod-data/inventory_proof_params.json
- Purpose: Each example provides a script (e.g.,
-
- Purpose: Takes the high-level
params.jsonfile generated in the previous step and the correspondingGPCProofConfigfile, verifies their consistency, deserializes the PODs and other values, and outputs a structured_gpc_inputs.jsonfile containing livePODobjects ready for the proving engine. - How it works: Reads the config (
.ts) and the parameters file (params.json). Uses thepodConfigMappingto find and deserialize the correct PODs from thepodsobject. DeserializesmembershipLists,watermark, etc. usingpodValueFromJSON. Verifies signatures and basic config/input consistency. - Output: Creates a
_<configName>_gpc_inputs.jsonfile (e.g.,locationProofConfig_gpc_inputs.json) in thepackages/gpc/proof-inputs/directory. - Example:
pnpm run gen-proof-inputs ./packages/examples/location-bounding/proof-configs/locationProofConfig.ts ./packages/examples/location-bounding/pod-data/location_proof_params.json
- Purpose: Takes the high-level
-
- Purpose: Calculates the minimum circuit parameters required based on a
GPCProofConfig(.tsfile defining constraints) and the structured_gpc_inputs.jsonfile generated previously. - How it works: Analyzes the constraints in the config and the structure/content of the input PODs (from the
_gpc_inputs.json) to determine the necessarymaxObjects,maxEntries,merkleMaxDepth, etc. It performs detailed checks based on the@pcd/gpclibrary's logic. - Output: Generates a
_<configName>_requirements.jsonfile (e.g.,locationProofConfig_requirements.json) in thepackages/gpc/proof-requirements/directory. - Example:
pnpm run gen-proof-requirements ./packages/examples/location-bounding/proof-configs/locationProofConfig.ts ./packages/gpc/proof-inputs/locationProofConfig_gpc_inputs.json
- Purpose: Calculates the minimum circuit parameters required based on a
-
- Purpose: Checks if a pre-compiled circuit artifact exists locally that meets or exceeds the parameters specified in a
_requirements.jsonfile. It aims to find the smallest suitable circuit to minimize proving time. - How it works: Compares the input requirements against the parameter sets listed in
packages/gpc/src/circuitParameterSets.tsand checks for the existence of corresponding artifacts (.wasm,-pkey.zkey,-vkey.json) in thepackages/gpc/artifacts/directory. - Output: Prints the
circuitIdof the best matching circuit (e.g.,3o-26e-...) or indicates that compilation is needed (COMPILE_NEEDED <configName>). - Example:
pnpm run find-circuit ./packages/gpc/proof-requirements/locationProofConfig_requirements.json
- Purpose: Checks if a pre-compiled circuit artifact exists locally that meets or exceeds the parameters specified in a
-
compile-circuit(Optional - iffind-circuitindicates needed):- Purpose: Compiles a new circuit based on a
_requirements.jsonfile and performs the necessary setup (snarkjs) to generate the proving and verification keys. - How it works: Reads the requirements, loads configuration from
gpc-compile-config.json, generates a canonical circuit name (ID), creates a temporary wrapper.circomfile instantiating the baseproto-pod-gpc.circomtemplate with the specific parameters, compiles this wrapper using a directcircomcommand, performs the Groth16 Phase 2 trusted setup using thesnarkjsprogrammatic API and the downloaded.ptaufile, and finally moves the final artifacts (.wasm,-pkey.zkey,-vkey.json, and.r1cs) to the mainpackages/gpc/artifacts/directory. Cleans up temporary build files. - Example:
pnpm run compile-circuit ./packages/gpc/proof-requirements/locationProofConfig_requirements.json
- Security Note: The
snarkjs zkey contributestep uses dummy random entropy. This means the setup is not cryptographically secure for production use. It's sufficient for testing and development, but a real deployment would require a proper multi-party computation (MPC) ceremony for the Phase 2 setup of each specific circuit.
- Purpose: Compiles a new circuit based on a
-
add-circuit-params(Optional - after compiling a new circuit):- Purpose: Registers the parameters of a newly compiled circuit into the
packages/gpc/src/circuitParameterSets.tsfile. This step is crucial if you want the newly compiled circuit to be discoverable byfind-circuitand usable by subsequentgen-proofcalls. - How it works: Reads the requirements file, derives the circuit ID, checks that the final artifacts exist in the
artifactsdirectory, and then adds/updates the entry in thesupportedParameterSetsarray incircuitParameterSets.ts. - Example:
pnpm run add-circuit-params ./packages/gpc/proof-requirements/locationProofConfig_requirements.json
- Purpose: Registers the parameters of a newly compiled circuit into the
-
- Purpose: Generates the actual Zero-Knowledge proof based on a config and the structured inputs (
_gpc_inputs.json). - How it works: Takes the proof config (
.ts) and the GPC inputs (_gpc_inputs.json), loads the requirements (_requirements.json), derives the circuit description and identifier, performs pre-computation (gpcPreProve), callssnarkjs groth16 fullProveusing the circuit.wasmand-pkey.zkeyartifacts, performs post-computation (gpcPostProve), and saves the resulting proof data (proofobject,boundConfig,revealedClaims) to a JSON file. - Output: Creates a
_<configName>_<circuitId>_proof.jsonfile (e.g.,locationProofConfig_3o-26e-..._proof.json) in thepackages/gpc/proofs/directory. - Example:
pnpm run gen-proof ./packages/examples/location-bounding/proof-configs/locationProofConfig.ts ./packages/gpc/proof-inputs/locationProofConfig_gpc_inputs.json
- Purpose: Generates the actual Zero-Knowledge proof based on a config and the structured inputs (
-
- Purpose: Verifies a generated proof against the corresponding verification key.
- How it works: Loads the proof file (
_proof.json), extracts the proof object, bound config, and revealed claims. It uses thecircuitIdentifierfrom the bound config to find the matching circuit parameters incircuitParameterSets.ts, locates the corresponding verification key (-vkey.json) in theartifactsdirectory, and calls theProtoPODGPC.verifyfunction (which usessnarkjs groth16 verifyinternally). - Output: Prints whether the proof is VALID or INVALID.
- Example:
pnpm run verify-proof ./packages/gpc/proofs/locationProofConfig_3o-26e-6md-2nv-1ei-0x0l-0x0t-0ov3-0ov4_proof.json
Important Disclaimers:
- Mock Environment: This repository provides a standalone development and testing environment. It is designed for mocking use cases and iterating quickly.
- No Trusted Setup: The circuit compilation process (
compile-circuit) includes a dummy Phase 2 contribution. This means the generated proving keys (-pkey.zkey) and verification keys (-vkey.json) are not secure for production environments where trust is required. A real deployment would necessitate a proper multi-party computation ceremony for each specific circuit compiled. - Path Handling: Scripts generally interpret file path arguments relative to the Current Working Directory (
process.cwd()) from whichpnpm runis executed (typically the workspace root). Ensure you provide paths correctly based on this convention.