Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion crates/authenticator/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@ include = [
[features]
default = ["embed-zkeys"]
embed-zkeys = ["world-id-proof/embed-zkeys"]
# Enable ProveKit-backed ownership proof generation.
provekit = ["world-id-proof/zk-ownership-prove", "world-id-primitives/provekit"]
# Compress the embedded zkeys to reduce binary size (ARK point compression)
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same nit as in core/Cargo.toml: "world-id-primitives/provekit" is already transitively activated through world-id-proof/zk-ownership-proveprovekitworld-id-primitives/provekit. Harmless, but removable for simplicity.

compress-zkeys = ["embed-zkeys", "world-id-proof/compress-zkeys"]
# Compress the tar archive with zstd
Expand Down Expand Up @@ -63,7 +65,7 @@ hex = { workspace = true }
base64 = { workspace = true }

# Internal
world-id-proof = { workspace = true, features = ["zk-ownership-prove"] }
world-id-proof = { workspace = true }
world-id-registries = { workspace = true }

# Native-only dependencies (not available on wasm32)
Expand Down
10 changes: 7 additions & 3 deletions crates/authenticator/src/prove.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
use secrecy::ExposeSecret;
#[cfg(feature = "provekit")]
use world_id_primitives::OwnershipProof;
use world_id_primitives::{
Credential, FieldElement, OwnershipProof, ProofRequest, ProofResponse, RequestItem,
ResponseItem, SessionId, SessionNullifier, ZeroKnowledgeProof,
Credential, FieldElement, ProofRequest, ProofResponse, RequestItem, ResponseItem, SessionId,
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

/otto - what is this about?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a consequence of how Rust's #[cfg] works with use statements. Since OwnershipProof is now only compiled when the provekit feature is enabled (it's gated in primitives/src/proof.rs), it can't live in the shared use world_id_primitives::{...} block — that block is always compiled, so any item in it must always exist.

The fix is to split OwnershipProof out into its own use statement that can be individually gated:

#[cfg(feature = "provekit")]
use world_id_primitives::OwnershipProof;

Without this, you'd get a compilation error when building without provekit because OwnershipProof wouldn't be exported from the crate.

SessionNullifier, ZeroKnowledgeProof,
};
use world_id_proof::{
AuthenticatorProofInput, FullOprfOutput, OprfEntrypoint, ProofCompression,
Expand All @@ -14,6 +16,7 @@ use crate::{
error::AuthenticatorError,
};
use world_id_primitives::TREE_DEPTH;
#[cfg(feature = "provekit")]
use world_id_proof::{
circuit_inputs::OwnershipProofCircuitInput, ownership_proof::generate_ownership_proof,
};
Expand Down Expand Up @@ -437,6 +440,7 @@ impl Authenticator {
///
/// # Returns
/// The [`OwnershipProof`] containing the ZKP and Merkle root.
#[cfg(feature = "provekit")]
pub async fn prove_credential_sub(
&self,
nonce: FieldElement,
Expand Down Expand Up @@ -473,7 +477,7 @@ impl Authenticator {
}
}

#[cfg(test)]
#[cfg(all(test, feature = "provekit"))]
mod tests {
use crate::{
authenticator::Authenticator,
Expand Down
7 changes: 7 additions & 0 deletions crates/core/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,13 @@ embed-zkeys = ["authenticator", "world-id-proof/embed-zkeys"]
compress-zkeys = ["authenticator", "embed-zkeys", "world-id-proof/compress-zkeys"]
# Compress the tar archive with zstd
zstd-compress-zkeys = ["authenticator", "embed-zkeys", "world-id-proof/zstd-compress-zkeys"]
# Enable ProveKit-backed ownership proofs.
provekit = [
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: world-id-proof/zk-ownership-prove and world-id-primitives/provekit are already implied transitively by world-id-authenticator/provekit (which activates both). Listing them here is redundant—though harmless and arguably self-documenting. Consider simplifying to just ["authenticator", "world-id-authenticator/provekit"] if you prefer minimal feature lists, or keep as-is for explicitness.

"authenticator",
"world-id-proof/zk-ownership-prove",
"world-id-authenticator/provekit",
"world-id-primitives/provekit",
]
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: world-id-primitives/provekit is already transitively activated via world-id-proof/zk-ownership-proveworld-id-proof/provekitworld-id-primitives/provekit. Similarly, world-id-proof/zk-ownership-prove is itself transitively activated through world-id-authenticator/provekit. So the minimal feature definition could be:

provekit = [
    "authenticator",
    "world-id-authenticator/provekit",
]

The explicit listing is fine as defensive redundancy (protects against dependency restructuring), but adds maintenance surface. Up to you whether to keep them — if you do, a brief comment like # explicit for clarity would help future readers.


issuer = [
"dep:world-id-issuer",
Expand Down
4 changes: 3 additions & 1 deletion crates/primitives/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@ repository.workspace = true
[features]
# Enable OpenAPI schema derives for API types.
openapi = ["dep:utoipa"]
# Enable ProveKit-backed proof types.
provekit = ["dep:provekit-common"]

[dependencies]
alloy = { workspace = true, features = ["sol-types", "signer-local"] }
Expand All @@ -36,7 +38,7 @@ serde = { workspace = true }
thiserror = { workspace = true }
url = { workspace = true, features = ["serde"] }
poseidon2 = { workspace = true }
provekit-common = { workspace = true }
provekit-common = { workspace = true, optional = true }
sha3 = { workspace = true, default-features = false }
rand = { workspace = true }
secrecy = { workspace = true }
Expand Down
4 changes: 3 additions & 1 deletion crates/primitives/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,9 @@ pub use session::{SessionFeType, SessionFieldElement, SessionId, SessionNullifie

/// Contains the quintessential zero-knowledge proof type.
pub mod proof;
pub use proof::{OwnershipProof, ZeroKnowledgeProof};
#[cfg(feature = "provekit")]
pub use proof::OwnershipProof;
pub use proof::ZeroKnowledgeProof;

/// Contains types specifically related to relying parties.
pub mod rp;
Expand Down
5 changes: 5 additions & 0 deletions crates/primitives/src/proof.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
use ruint::aliases::U256;
use serde::{Deserialize, Deserializer, Serialize, Serializer, de::Error as _};

#[cfg(feature = "provekit")]
use crate::FieldElement;

/// Encoded World ID Proof.
Expand Down Expand Up @@ -102,6 +103,7 @@ impl From<ZeroKnowledgeProof> for [U256; 5] {
///
/// Contains the ZKP and the Merkle root public input that the verifier
/// doesn't initially provide.
#[cfg(feature = "provekit")]
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct OwnershipProof {
/// The WHIR R1CS proof from ProveKit.
Expand Down Expand Up @@ -180,6 +182,7 @@ mod tests {
assert!(result.unwrap_err().contains("Invalid length"));
}

#[cfg(feature = "provekit")]
#[test]
fn test_ownership_proof_json_roundtrip() {
let whir_proof = provekit_common::WhirR1CSProof {
Expand All @@ -204,6 +207,7 @@ mod tests {
/// This test simulates the data-level check that a verifier would perform:
/// if the merkle root stored inside an [`OwnershipProof`] has been tampered
/// with, the proof object is distinguishable from the original.
#[cfg(feature = "provekit")]
#[test]
fn test_ownership_proof_wrong_merkle_root_is_detected() {
let whir_proof = provekit_common::WhirR1CSProof {
Expand Down Expand Up @@ -235,6 +239,7 @@ mod tests {
/// This test simulates the data-level check that a verifier would perform:
/// if the proof bytes inside an [`OwnershipProof`] have been tampered with,
/// the proof object is distinguishable from the original.
#[cfg(feature = "provekit")]
#[test]
fn test_ownership_proof_tampered_bytes_is_detected() {
let original_bytes = vec![0xde, 0xad, 0xbe, 0xef];
Expand Down
7 changes: 4 additions & 3 deletions crates/proof/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -30,8 +30,9 @@ zstd-compress-zkeys = ["embed-zkeys", "dep:zstd"]
# Experimental: Feature Flags for Specific Circuits
# RATIONALE: We feature flag each circuit and based on the proving/verifying action because each require
# a proving and verifying key that generally is above >1Mb. So selecting circuits optimizes bundle size.
zk-ownership-prove = ["dep:provekit-prover"]
zk-ownership-verify = ["dep:provekit-verifier"]
provekit = ["dep:provekit-common", "world-id-primitives/provekit"]
zk-ownership-prove = ["dep:provekit-prover", "provekit"]
zk-ownership-verify = ["dep:provekit-verifier", "provekit"]

[dependencies]
ark-babyjubjub = { workspace = true }
Expand All @@ -46,7 +47,7 @@ thiserror = { workspace = true }
tracing = { workspace = true }
zstd = { workspace = true, optional = true }
eddsa-babyjubjub = { workspace = true }
provekit-common = { workspace = true }
provekit-common = { workspace = true, optional = true }
provekit-prover = { workspace = true, optional = true }
provekit-verifier = { workspace = true, optional = true }
rand = { workspace = true }
Expand Down
8 changes: 7 additions & 1 deletion crates/proof/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,13 +20,16 @@ pub use oprf_query::{FullOprfOutput, OprfEntrypoint};
pub mod proof;
pub use proof::*;

#[cfg(feature = "provekit")]
use provekit_common::{InputMap, InputValue, NoirElement};

#[cfg(feature = "provekit")]
use world_id_primitives::FieldElement;

#[cfg(any(feature = "zk-ownership-prove", feature = "zk-ownership-verify"))]
#[cfg(feature = "provekit")]
pub mod ownership_proof;
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This widens the gate from any(feature = "zk-ownership-prove", feature = "zk-ownership-verify") to just provekit. Since both zk-ownership-prove and zk-ownership-verify imply provekit, this is semantically equivalent for those features. However, it means someone who enables only provekit (without prove or verify) will get the ownership_proof module compiled — which is effectively empty in that case since its prover and verifier sub-modules are still individually gated.

This is fine and simpler. Just noting the subtle behavior change for awareness. The build.rs correctly still uses the narrower any(zk-ownership-prove, zk-ownership-verify) gate for artifact downloads, which is right since the .pkp/.pkv files are only needed by those sub-features.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Observation: this gate broadened from any(feature = "zk-ownership-prove", feature = "zk-ownership-verify") to just provekit. Semantically equivalent since both prove/verify now imply provekit, but it means enabling provekit alone (without prove or verify) will compile this module as an essentially-empty shell (no public items). Not a bug, just flagging the slight behavioral difference.


#[cfg(feature = "provekit")]
pub use provekit_common::{NoirProof, WhirR1CSProof};

/// Error type for OPRF operations and proof generation.
Expand Down Expand Up @@ -55,14 +58,17 @@ pub enum ProofError {
InternalError(#[from] eyre::Report),
}

#[cfg(feature = "provekit")]
pub trait NoirCircuitInput {
fn into_witness(self) -> Result<InputMap, ProofError>;
}

#[cfg(feature = "provekit")]
pub trait NoirRepresentable {
fn into_noir_value(self) -> InputValue;
}

#[cfg(feature = "provekit")]
impl NoirRepresentable for FieldElement {
fn into_noir_value(self) -> InputValue {
InputValue::Field(NoirElement::from_repr(*self))
Expand Down
Loading