Skip to content

ZK

The contract runtime exposes a narrow zk module for Groth16/BN254 proof verification and protocol helpers used by the maintained shielded contracts. Proof generation, witnesses, wallet sync, and proving keys remain off-chain.

Verification API

python
zk.is_available()
zk.has_verifying_key(vk_id)
zk.get_vk_info(vk_id)
zk.verify_groth16(vk_id, proof_hex, public_inputs)
zk.verify_groth16_bn254(vk_hex, proof_hex, public_inputs)

verify_groth16 is the preferred contract API. It loads an active verifying key from zk_registry and caches its prepared form by (vk_id, vk_hash).

verify_groth16_bn254 is the lower-level raw-key form. It sends the full key through the call and therefore has a larger payload and metering cost.

Both verification functions return True or False for well-formed inputs. Malformed encodings raise an assertion error.

Input Encoding

  • verifying keys and proofs are 0x-prefixed compressed canonical bytes
  • every public input is a 0x-prefixed, exactly 32-byte, big-endian BN254 field element
  • shortened field encodings are rejected

Limits:

InputMaximum
verifying-key hex8,192 characters
proof hex4,096 characters
public inputs32
verifying-key ID128 characters

Registry Binding

Contracts that keep a long-lived verifier binding should store and recheck:

  • vk_id
  • vk_hash
  • any required circuit family, statement version, tree depth, and input/output bounds from zk.get_vk_info(vk_id)

This prevents an unexpected registry update from silently changing proof semantics.

python
@export
def verify_join(vk_id: str, expected_hash: str, proof: str, inputs: list):
    info = zk.get_vk_info(vk_id)
    assert info is not None and info["active"], "Unknown verifying key"
    assert info["vk_hash"] == expected_hash, "Verifying key changed"
    return zk.verify_groth16(vk_id, proof, inputs)

Shielded Protocol Helpers

The module also exposes helpers for shielded tree appends, public-input construction, nullifier digests, command bindings, execution tags, and output payload hashes. These functions are part of the maintained shielded-note and shielded-command protocols; application contracts should use those higher-level contracts instead of designing a new protocol from the helpers alone.

Node Requirement

A chain with the zk runtime feature requires the native verifier on every validator. xian-abci fails closed when the required backend is unavailable. zk.is_available() is useful for tests and tooling, not for treating verifier availability as optional during consensus.

Security Requirements

  • bind every public input to the intended contract state and action
  • derive post-state from canonical on-chain state, not caller-provided metadata
  • keep witnesses and proof generation outside the validator runtime
  • use registry governance and two-step ownership transfer
  • never use deterministic development setup material for value-bearing networks
  • require an accepted multi-party ceremony and checksum-pinned artifacts for mainnet proving keys

Groth16 setup integrity and zk_registry control are both trust assumptions. A compromised setup or malicious registry authority can invalidate proof soundness.

Metering

Verification is metered by a fixed base, public-input count, and payload size. Registry-backed verification has a lower per-byte rate and supports prepared-key reuse. See Chi Cost Table for current constants and use readonly simulation for a complete transaction estimate.