A zero-knowledge proof system for obfuscated location and distance verification in Eve Frontier on the Sui blockchain, using optimized Groth16 circuits and POD (Provable Object Datatype) structures.
This project provides two main capabilities:
Goal: Enable privacy-preserving location and distance attestations on-chain using optimized Groth16 circuits.
- Location Attestation: Prove knowledge of an object's location (coordinates) without revealing the exact coordinates on-chain
- Distance Attestation: Prove the distance between two objects without revealing their individual locations
- Circuit Optimization: Custom Groth16 circuits (
location-attestation,distance-attestation) for efficient on-chain verification- Performance: ~320ms location proof generation, ~250ms distance proof generation
- Merkle Proofs: Poseidon-based Merkle trees for efficient in-circuit and on-chain inclusion proofs
- Ed25519 Signatures: Cryptographic binding of location data to authorized signers
Key Features:
- Deterministic object ID generation using
derived_object::claim - Fixed Objects: Location set once during creation (for deployed assemblies like stations, structures)
- Dynamic Objects: Location can be updated via
set_location(for ships, characters, and other non-deployed objects) - Distance Verification: Can be used independently; once distance is proven for fixed object pairs, it can be reused for subsequent operations
- Unified
ObjectRegistryfor location and distance data storage - Inline Groth16 verification
Goal: General-purpose POD (Provable Object Datatype) and GPC (General Purpose Circuits) integration for off-chain use cases.
- POD Structure: Standard POD format with EDDSA-Poseidon signatures
- Provable Object Datatype (POD): The foundational data structure that enables cryptographic attestation of object properties
- GPC Circuits: General Purpose Circuits for flexible attestation structures
- Context IDs: POD contentIDs used as context identifiers in distance proofs
- Poseidon Merkle Roots: Consistent Poseidon-based hashing across on-chain and off-chain
Location data is designed to be lazily updated through natural game interactions:
- Natural Updates: When objects interact (e.g., assemblies are anchored, ships dock, characters interact with assemblies), the location attestation timestamp and hidden salt are automatically updated
- Brute-Force Protection: Salt randomization prevents brute-force attacks on location data
- Force Updates: Location data can also be force-updated at any time by providing new ZK proofs on-chain
POD attestations are timestamp-based to allow multiple re-use (as long as on-chain logic permits):
- Fixed Objects: Allow any timestamp for interaction as long as it's after object deployment
- Dynamic Objects: More restricted timestamp validation for real-time location updates
- Staleness Enforcement: On-chain logic is responsible for enforcing time data staleness based on use case requirements
Once distance is proven between two fixed objects:
- The distance data is stored on-chain and can be reused indefinitely for subsequent operations (as long as those objects remain deployed in the same location)
- No need to regenerate proofs for the same object pair
- Enables efficient batch operations and game mechanics that rely on persistent distance relationships
The system provides multiple levels of data revelation, giving builders flexibility in information asymmetry and meta gameplay trade-offs:
- Clear text data revealed only to direct observers
- Examples: Survey data, direct in-game interaction data, data about objects a character is piloting/using
- Use Cases:
- Data marketplace for buying and selling location information of valuable objects in space
- Corporate espionage and intelligence gathering
- Player-to-player information trading
- Naturally hidden data: Data is hidden by the merkle root structure and maximum ZK hidden configuration
- Configurable revelation: ZK proofs can be built and configured in various ways to reveal different pieces of data as needed
- General Circuit Capabilities: POD/GPC circuits can be configured for any proof structure
- Merkle root integration: The merkle root construction can also be used to reveal and verify attestation data on-chain
- Strongest cryptographic binding: Distance attestations are cryptographically bound to specific location ZK proofs and their underlying POD attestations, ensuring distance proofs cannot be reused with different location data
- Use Cases:
- Off-chain tools and interactions
- Player-built on-chain integrations
- Privacy-preserving game mechanics
- Selective data revelation based on use case requirements
- Maximum privacy with cryptographic guarantees
- Distance verification without location disclosure
- Trustless proximity-based interactions
- Merkle inclusion proofs allow revealing and verifying specific POD attestation data on-chain
- Builders can independently choose which POD entries to reveal and verify
- Entry Merkle Root Structure: Each POD entry can be verified independently via Merkle inclusion
- Use Cases:
- Selective data revelation for on-chain operations
- Verification of specific attestation properties without revealing others
- Custom game mechanics that require partial data disclosure
- The POD layer acts as an outer wrapper around the ZK proof system
- Enables builders to use generalized circuits to more easily build other ZK configurations around POD attestations
- Supports diverse use cases beyond location and distance
Trade-offs:
- Generality vs Efficiency: Generalized circuits (POD/GPC) provide flexibility but may be less efficient for proof generation compared to custom-optimized circuits
- Hashing Differences: POD uses SHA256 hashing for
bytestypes (while we use Poseidon for our unified format), and POD uses EDDSA-Poseidon signing (efficient for circuits but not supported on-chain in Sui, which is why we use Ed25519 for on-chain verification) - Circuit Optimization: Our custom circuits (
location-attestation,distance-attestation) are optimized for specific use cases, achieving ~320ms and ~250ms proof generation respectively
- The general revealing of timestamp and distance for two objects on-chain is highly generalized
- Can accommodate any kind and multiple interactions between two objects in space in parallel
- Simple information structure enables complex game mechanics
- Entry Merkle Root Structure allows builders to independently verify any POD attestation data on-chain via Merkle inclusion
- Builders have full control over information asymmetry and meta gameplay trade-offs
- Enables on-chain usage vs. data reveal decisions per use case
eve-frontier-proximity-zk-poc/
├── move/world/ # Move smart contracts
│ ├── sources/
│ │ ├── assemblies/ # Object assemblies (FixedObject, DynamicObject)
│ │ ├── attestations/ # Location and distance attestation logic
│ │ ├── primitives/ # Core primitives (location, distance, authority)
│ │ ├── registries/ # ObjectRegistry for deterministic IDs and data storage
│ │ └── crypto/ # Cryptographic primitives (Merkle, Groth16)
│ └── tests/ # Move unit tests
├── src/
│ ├── on-chain/ # On-chain circuit compilation and proof generation
│ │ ├── circuits/ # Custom Groth16 circuits (location, distance)
│ │ ├── proofs/ # Proof generation utilities
│ │ └── tools/ # Rust vkey serializer
│ ├── off-chain/ # Off-chain POD/GCP integration
│ │ ├── circuits/ # GPC circuit compilation
│ │ └── proofs/ # POD/GPC proof generation
│ └── shared/ # Shared utilities
│ ├── pods/ # POD generation utilities
│ ├── merkle/ # Merkle tree generation
│ └── utils/ # Common utilities
├── test/
│ ├── on-chain/
│ │ ├── integration/ # Integration tests (step-by-step flows)
│ │ └── proofs/ # Proof generation tests
│ └── off-chain/ # Off-chain functionality tests
└── outputs/ # Generated files (PODs, proofs, Merkle trees) - excluded from git
├── pods/
├── proofs/
└── merkle/
- Node.js: v20+ (via NVM recommended)
- Rust: Latest stable (for vkey serializer)
- Sui CLI: Latest version
- pnpm: Package manager
-
Install Homebrew (if not already installed):
/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)" -
Install Git, Curl, and Rust:
brew install git curl curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
-
Install NVM and Node.js:
curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.7/install.sh | bash # Restart terminal, then: nvm install 20 nvm use 20 npm install -g pnpm
-
Install Sui CLI:
pnpm sui:install
This script detects macOS architecture (Apple Silicon vs Intel) and installs the appropriate binary.
-
Install Project Dependencies:
pnpm install
Follow similar steps as macOS, but use system package managers (apt-get for Ubuntu/Debian).
pnpm sui:localnet:startNote for macOS: The script automatically detects Apple Silicon and uses Rosetta 2 if needed for x86_64 Sui binaries. It also prefers arm64 binaries from ~/.local/bin/sui or /opt/homebrew/bin/sui.
Customization:
--accounts N: Create N funded accounts (default: 1 for tests)--mnemonic "...": Use custom mnemonic for account generation
The network runs in the background. To stop:
pnpm sui:localnet:stoppnpm move:buildBefore generating test proof data, you need to generate the required cryptographic keys:
pnpm generate-auth-key
pnpm generate-ed25519-keyThese scripts will create a .env file (if it doesn't exist) with:
EDDSA_POSEIDON_AUTHORITY_PRIV_KEYandEDDSA_POSEIDON_AUTHORITY_PUB_KEY(for POD signing)ED25519_PRIVATE_KEYandED25519_PUBLIC_KEY(for on-chain signing)
Note: The .env file is excluded from git via .gitignore. Never commit your private keys.
Before generating test proof data, you need to compile the on-chain circuits. This is required for proof generation.
The Powers of Tau (ptau) files are large binary files used for trusted setup. Download them first:
pnpm circuit:fetch-ptau:on-chainThis downloads ppot_0080_12.ptau to src/shared/ptau/. This file supports up to 2^12 = 4,096 constraints, which is sufficient for the on-chain circuits (location-attestation: ~2,359 constraints, distance-attestation: ~1,010 constraints).
Note: Ptau files are stored in src/shared/ptau/ and are excluded from git via .gitignore. Smaller ptau files (like ppot_0080_12.ptau) are typically tens to hundreds of MB, while larger ones (like ppot_0080_16.ptau) are typically around 70MB.
The on-chain circuit compilation process uses the Rust vkey serializer to create canonical compressed verification keys for Move contracts. The serializer is automatically fetched and built when needed, but you can also set it up manually:
pnpm rust:vkey-serializer:buildThis script will:
- Fetch the
rust-vkey-serializerrepository from the configured git URL (if it doesn't exist) - Build the Rust project in release mode
Configuration: The repository URL can be set via the RUST_VKEY_SERIALIZER_REPO environment variable, or by updating the default in scripts/fetchRustVKeySerializer.ts. The branch can be configured via RUST_VKEY_SERIALIZER_BRANCH (defaults to main).
Note: The serializer will be automatically fetched and built during circuit compilation if it's not already available.
Compile the custom Groth16 circuits (location-attestation, distance-attestation):
pnpm circuit:compile:on-chainThis compiles the circuits and generates:
- WASM files (
.wasm) - Proving keys (
.zkey) - Verification keys (
.vkey.json,.vkey.hex) - R1CS files (
.r1cs)
Output: Artifacts are saved to src/on-chain/circuits/artifacts/
Note: During compilation, the Rust vkey serializer is automatically used to serialize verification keys for Move contracts. The serializer will be fetched and built automatically if needed.
Important: This must be done before running unit tests, as the tests depend on the generated proof data.
pnpm move:test:generate:proofThis generates move/world/tests/test_proof_data.move with valid proof data for unit tests. The script:
- Generates location PODs for deterministic object IDs (
0x111111,0x111112,0x222222) - Creates Merkle trees and multiproofs
- Generates Groth16 ZK proofs
- Formats all data into Move
vector<u8>literals for use in unit tests
Note: This script uses a fixed test registry ID (0x34401905bebdf8c04f3cd5f04f442a39372c8dc321c29edfb4f9cb30b23ab96) for deterministic object ID generation. This ID matches what test_scenario creates in Move unit tests when using test_scenario::begin(@0x1).
pnpm move:testNote: Groth16 verification in Move unit tests is skipped (commented out) because it requires depth 0 (entry point), but unit tests add an extra level of call depth. Full ZK verification is tested in integration tests where Groth16 verification can be called directly from entry functions.
Note: For initial setup, follow the circuit compilation steps in the Quick Start section (Step 4). This section provides additional details and options.
The on-chain circuits are required for test proof generation and on-chain verification. See Quick Start Step 4 for the setup process.
Manual Serialization (if needed):
pnpm serialize:vkeyManually serialize verification keys using the Rust serializer. This is typically done automatically during circuit compilation, but can be run separately if needed.
Note: Off-chain circuit compilation is optional and only needed if you plan to use POD/GPC circuits for off-chain proof generation. For the basic setup and test proof generation, you only need the on-chain circuits.
Prerequisites: Fetch ptau Files for Off-Chain Circuits:
pnpm circuit:fetch-ptau:off-chainThis downloads ppot_0080_16.ptau to src/shared/ptau/. This file supports up to 2^16 = 65,536 constraints, which is required for the default POD/GPC circuit configurations used in this project (circuits have ~35k+ constraints).
Note:
ppot_0080_16.ptauis required for the default off-chain circuit configurations. The location and distance circuits have approximately 35,669 constraints, which exceeds the capacity of smaller ptau files.- Ptau files are stored in
src/shared/ptau/and are excluded from git via.gitignore. Theppot_0080_16.ptaufile is approximately 70MB, while smaller files likeppot_0080_12.ptauare typically tens to hundreds of MB.
- Compile GPC Circuits:
pnpm circuit:compile:off-chainThis compiles the generic POD/GPC circuits for off-chain use.
Output: Artifacts are saved to src/off-chain/circuits/artifacts/
Note: All circuit artifacts (.wasm, .zkey, .vkey.*, .r1cs, .ptau) are excluded from git via .gitignore due to their large size.
Location Attestation Flow:
pnpm test -- locationAttestationStepByStepTests the complete flow:
- Create
DynamicObjectwith location data - Update location via
set_location - Create
FixedObjectwith location data during creation
Distance Attestation Flow:
pnpm test -- distanceAttestationStepByStepTests the complete flow:
- Create two
FixedObjects with location data - Generate distance proof
- Verify distance via mock
inventory::transfer
Note: Integration tests automatically clean up generated output files in afterAll hooks.
POD Generation:
pnpm test -- shared/podsMerkle Tree Generation:
pnpm test -- shared/merkleProof Generation:
pnpm test -- off-chain/proofsObjects use sui::derived_object::claim for deterministic ID generation:
FixedObject: Created withitem_id→ deterministic IDDynamicObject: Created withitem_id→ deterministic ID- Off-chain: Use
deriveObjectIDor custom implementation to compute IDs before creation
The system uses Poseidon hashing consistently across all layers for circuit and on-chain operations:
- Merkle Tree Construction: All leaf hashes and tree nodes use Poseidon
- Circuit Verification: Leaf hash computation and Merkle proof verification use Poseidon
- On-Chain Verification: Uses Sui's native
poseidon_bn254(available on mainnet) - Benefits: ~100x cheaper than SHA256 in circuits (~250 constraints vs ~25,000)
Note on POD Merkleization: POD's native merkleization uses Poseidon for int types but SHA256 for bytes types. We add an extra layer to achieve a unified Poseidon format for both off-chain and on-chain circuits. This is particularly important for circuit optimization, as Poseidon operations are much cheaper in-circuit than SHA256.
Compatibility: All implementations (POD's poseidon-lite, Sui's poseidon_bn254, Circomlib's Poseidon) are 100% compatible using the same BN254 Poseidon parameters.
- Public Inputs: 4 (well within Sui's 8 limit)
merkleRoot- Poseidon Merkle rootcoordinatesHash- Poseidon hash(x, y, z, salt)timestamp- TimestampsignatureAndKeyHash- Poseidon hash(signature || public key)
- Constraints: ~2,359
- Proof Generation: ~320ms
- Verification: Coordinates-only verification in-circuit (for efficiency), full verification available on-chain
- Public Inputs: 8 (at Sui's limit)
merkleRoot- Distance POD Merkle rootlocationMerkleRoot1,locationMerkleRoot2- Location Merkle rootscoordinatesHash1,coordinatesHash2- Coordinate hashes from location proofsdistanceSquaredMeters- Distance squaredtimestamp- Max timestampsignatureAndKeyHash- Poseidon hash(signature || public key)
- Constraints: ~1,010
- Proof Generation: ~250ms
- Distance Calculation: Manhattan distance (|x1-x2| + |y1-y2| + |z1-z2|)²
- On-Chain: Poseidon-based Merkle roots (32-byte big-endian)
- Off-Chain: Consistent Poseidon hashing with BCS encoding
- POD Entry: Stored as
poseidon_merkle_root(string, hex format) - Tree Structure: Depth 3 (supports 8 entries), Poseidon(2 inputs) for parent nodes
- Leaf Hash: Poseidon(BCS_bytes_to_field_elements(BCS_encode(entryName) || BCS_encode(value)))
- POD Signature: EDDSA-Poseidon (signs entire POD) - This is POD/GCP's native signing method, efficient for circuits but not supported on-chain in Sui
- Ed25519 Signature: Raw Ed25519 (signs merkle root bytes, 64 bytes) - Used for on-chain verification because Ed25519 can be efficiently verified on-chain via Sui's native
ed25519_verifyfunction - Public Key: Ed25519 public key (32 bytes)
Why Ed25519 for On-Chain: While POD/GCP uses EDDSA-Poseidon signing natively (which is efficient for circuits), Sui does not support Poseidon-based signature verification on-chain. We use Ed25519 signatures specifically for the merkle root to enable efficient on-chain verification while maintaining cryptographic binding.
Generate the required keys using the provided scripts:
pnpm generate-auth-key # Generates EDDSA_POSEIDON_AUTHORITY_PRIV_KEY and EDDSA_POSEIDON_AUTHORITY_PUB_KEY
pnpm generate-ed25519-key # Generates ED25519_PRIVATE_KEY and ED25519_PUBLIC_KEYThese scripts will create a .env file (if it doesn't exist) with the required keys. Alternatively, you can manually create a .env file:
EDDSA_POSEIDON_AUTHORITY_PRIV_KEY=your_eddsa_poseidon_private_key
EDDSA_POSEIDON_AUTHORITY_PUB_KEY=your_eddsa_poseidon_public_key
ED25519_PRIVATE_KEY=your_ed25519_private_key
ED25519_PUBLIC_KEY=your_ed25519_public_keyNote: The .env file is excluded from git via .gitignore. Never commit your private keys.
Location Attestation POD:
objectId(bytes)solarSystem(int)x_coord,y_coord,z_coord(int)timestamp(int)pod_data_type(string)salt(bytes)poseidon_merkle_root(string)ed25519_signature(bytes)
Distance Attestation POD:
objectId1,objectId2(bytes)objectLocation1,objectLocation2(string, context IDs/contentIDs)distanceSquaredMeters(int)timestamp(int, max_timestamp)pod_data_type(string)poseidon_merkle_root(string)ed25519_signature(bytes)
This is often caused by corrupted Move build artifacts. Clean and rebuild:
pnpm move:clean
pnpm move:buildNote: pnpm move:clean deletes the entire build/ folder and all other Move contract artifacts (.sui/ directories) for all Move projects.
This is expected. Groth16 verification requires depth 0, which unit tests cannot provide. Use integration tests for full ZK verification.
- Check if port 9000 is in use:
lsof -ti :9000 - Kill existing Sui processes:
pkill -9 -f "sui start" - Check Sui binary architecture:
file $(which sui) - For Apple Silicon, ensure arm64 binary is used or Rosetta 2 is available
This is a segfault, often due to memory issues. Increase Node.js memory:
NODE_OPTIONS=--max-old-space-size=8192 pnpm move:test:generate:proofmove:build- Build all Move projectsmove:test- Run Move unit testsmove:publish- Publish Move package to networkmove:test:generate:proof- Generate test proof data
circuit:compile:on-chain- Compile on-chain Groth16 circuitscircuit:compile:off-chain- Compile off-chain GPC circuitscircuit:fetch-ptau:on-chain- Download ptau file for on-chain circuits (ppot_0080_12.ptau)circuit:fetch-ptau:off-chain- Download ptau file for off-chain circuits (ppot_0080_16.ptau)serialize:vkey- Serialize verification keys for Move
rust:vkey-serializer:fetch- Fetch the rust-vkey-serializer from git repositoryrust:vkey-serializer:build- Fetch (if needed) and build the rust-vkey-serializer
sui:install- Install Sui CLIsui:localnet:start- Start local Sui networksui:localnet:stop- Stop local Sui network
test- Run all teststest:watch- Run tests in watch modetest:coverage- Generate test coverage
- Follow the existing code structure
- Keep on-chain and off-chain logic separate
- Use deterministic object IDs for testability
- Clean up generated files in test
afterAllhooks - Document any new circuit parameters or POD structures
MIT