How a real-world moment becomes a cryptographic fact
zkbounty replaces human judgment with a deterministic pipeline. Here is exactly what happens between a hunter pressing record and an on-chain payout.
From a real-world moment to an on-chain payout
Five stages turn a physical action into a cryptographic fact. The hunter never exposes who they are - only that the criteria were met.
Capture
The hunter records the action through the SDK. Raw camera sensor data, IMU, and barometric altitude are read directly from the device - never a gallery upload.
sdk.capture()Attest
The secure enclave signs the raw metadata with a hardware-bound key. GPS is locked across multiple constellations and the current Solana slot hash is folded in.
ed25519_enclaveStrip
Every personal identifier - device IDs, faces, exact identity - is removed before anything leaves the device. Only the facts the bounty needs survive.
redact(pii)Prove
A succinct zero-knowledge proof is generated: it asserts the attestations matched the bounty parameters - coordinate, slot window, device class - revealing nothing else.
physical_action_v2Settle
The on-chain verifier program checks the proof against the bounty in a single transaction. Match → escrow releases. No match → funds stay locked. No human in the loop.
verify_and_pay()Four signatures, four classes of fraud eliminated
Each attestation closes a specific attack. A proof must carry every layer the bounty requires - partial proofs never settle.
Secure Enclave
A hardware-bound key inside the device's secure enclave signs the raw capture metadata. The private key is generated in, and never leaves, the chip.
Defeats
Emulators, rooted devices, and replayed captures - none can produce a valid enclave signature.
GPS Lock
A multi-constellation GNSS fix is taken at capture time and reduced to a coordinate, locked against the bounty's geofence with a haversine distance check.
Defeats
Mock-location apps and after-the-fact tagging - the lock is part of the signed payload.
Sensor Fusion
Camera and IMU signatures (rolling-shutter timing, lens noise, motion) are folded into the proof as a provenance fingerprint of genuine capture.
Defeats
Screen recordings, re-encodes, stock footage, and AI-generated media that lack a real sensor fingerprint.
Slot Binding
The most recent Solana slot hash is mixed into the attestation, binding the capture to a point in time that did not exist before it happened.
Defeats
Backdating and pre-recording - a proof cannot reference a slot hash from the future or reuse an old one outside the window.
One verifier program. Every proof. No exceptions.
A bounty's criteria compile to an on-chain Solana program. When a proof arrives, the program checks it against the bounty in a single transaction - geofence, slot window, device class, attestation set - and releases escrow only on a complete match.
- Immutable once deployed - issuers can't move the goalposts
- Deterministic - the same proof always yields the same result
- Public - anyone can audit the exact settlement logic
verify_proof()release_escrow()Groth16 · BN254pub fn verify_proof(ctx: Context<Verify>, p: Proof) -> Result<()> {
let b = &ctx.accounts.bounty;
// 1. zk proof attests the public inputs honestly
require!(groth16_verify(&b.vk, &p.proof, &p.inputs), Err::BadProof);
// 2. coordinate inside the geofence
require!(haversine(p.inputs.coord, b.center) <= b.radius_m, Err::Geo);
// 3. capture slot inside the window
require!(b.window.contains(p.inputs.slot), Err::SlotWindow);
// 4. all required attestations present
require!(p.inputs.attest_mask & b.required == b.required, Err::Attest);
// match -> release escrow to the hunter
release_escrow(ctx, b.reward)?;
emit!(ProofVerified { slot: p.inputs.slot, hunter: p.inputs.payee });
Ok(())
}The chain learns the fact - never the person
Personal identifiers are stripped on the device, before any proof is generated. The zero-knowledge proof asserts only that the bounty criteria were satisfied. Faces, device IDs, exact identity, and raw media never touch the network.
What gets published on-chain is a boolean and a payout address - nothing that could deanonymize the hunter or expose bystanders.
// runs on-device, pre-proof
const redacted = sdk.redact(capture, {
faces: "blur+drop", // bystander privacy
deviceId: "drop", // no device fingerprint
exif: "drop", // no embedded identity
keep: ["coord", "slot", "sensorSig"], // only what the bounty needs
});
const proof = await sdk.prove(redacted, bounty.criteria);
// proof.publicInputs -> { coordInGeofence: true, slotInWindow: true, ... }
// nothing else leaves the device