Diagnosing a Cargo Feature Flag Mismatch in the cuzk Phase 2 Pipeline

In the middle of implementing Phase 2 of the cuzk pipelined proving engine — a substantial refactoring that replaces the monolithic PoRep C2 prover with a per-partition synthesis/GPU architecture — the assistant hit a build error that exposed a subtle but critical misunderstanding about how Rust feature flags propagate through the Filecoin proof dependency tree. The message at index 452 captures the exact moment of diagnosis: the assistant realizes that storage-proofs-porep does not have a cuda-supraseal feature, and pivots to inspect the actual feature declarations across the dependency graph.

The Context: Building a Pipelined Prover

To understand why this message matters, we need to appreciate what was being built. The cuzk proving daemon is a custom GPU-accelerated proving engine for Filecoin's storage proofs. Phase 0 and Phase 1 had established a working multi-GPU worker pool that called into filecoin-proofs-api monolithically — each proof job was a single opaque function call that performed both CPU circuit synthesis and GPU NTT+MSM proving inside a single black box. Phase 2 aimed to split this pipeline: perform circuit synthesis on the CPU for one partition while the GPU concurrently proves a previously-synthesized partition, achieving overlap and reducing peak memory from ~136 GiB to ~13.6 GiB for 32 GiB sectors.

The assistant had spent several messages (indices 432–451) laying the groundwork. It had read the Phase 2 design document, studied the existing engine code, traced the full call chain from filecoin-proofs-api down to the bellperson fork, identified the exact parameter filenames on disk, and created the SRS manager module (srs_manager.rs) that would handle direct parameter loading. The next step was wiring the new dependencies into the workspace's Cargo.toml files so that the pipeline module could call into filecoin-proofs, storage-proofs-core, storage-proofs-porep, bellperson, and other crates directly — bypassing the monolithic filecoin-proofs-api wrapper.

The Build Error and Its Root Cause

In message 451, the assistant ran cargo check --workspace --no-default-features to verify the dependency changes compiled. The build failed with a telling error:

error: failed to select a version for `storage-proofs-porep`.
    ... required by package `cuzk-core v0.1.0`
the package `cuzk-core` depends on `storage-proofs-porep`, with features: `cuda-supraseal` but `storage-proofs-porep` does not have these features.

The assistant had assumed a uniform feature naming convention across the storage-proofs ecosystem. When it added storage-proofs-porep (and similarly storage-proofs-post and storage-proofs-update) as direct dependencies of cuzk-core, it specified features = ["cuda-supraseal"] for each, mirroring the pattern used for storage-proofs-core. This was a natural but incorrect assumption: the crate names are parallel, so why wouldn't their feature flags be parallel too?

The Diagnostic Bash One-Liner

The subject message (index 452) shows the assistant's response to this error. Rather than guessing or blindly trying different feature names, the assistant runs a targeted diagnostic:

for pkg in filecoin-proofs storage-proofs-core storage-proofs-porep \
  storage-proofs-post storage-proofs-update; do
  echo "=== $pkg ===" && \
  grep -A 30 '^\[features\]' ~/.cargo/registry/src/index.crates.io-*/${pkg}-19.0.1/Cargo.toml \
    2>/dev/null | head -20
done

This one-liner iterates over every crate in the dependency chain and extracts the [features] section from each crate's Cargo.toml in the local registry cache. The output reveals the truth: storage-proofs-core has a cuda-supraseal feature, but storage-proofs-porep, storage-proofs-post, and storage-proofs-update do not. Instead, those crates have a cuda feature. The filecoin-proofs crate's own cuda-supraseal feature definition confirms the pattern — it enables cuda on the sub-crates, not cuda-supraseal:

cuda-supraseal = [
    "storage-proofs-core/cuda-supraseal",
    "storage-proofs-porep/cuda",
    "storage-proofs-post/cuda",
    "storage-proofs-update/cuda",
    "bellperson/cuda-supraseal",
    ...
]

This is a deliberate design choice by the Filecoin proof library authors. The cuda-supraseal feature is specific to the core proving infrastructure (the SRS parameter handling, the bellperson integration), while the proof-type-specific crates (porep, post, update) only need the plain cuda feature because their GPU code paths are activated through a simpler mechanism. The assistant's mistake was projecting a uniform naming scheme onto a dependency graph that had intentionally different feature granularity at different layers.## The Reasoning Behind the Mistake

Why did the assistant make this assumption in the first place? The answer lies in the nature of the Phase 2 refactoring itself. The entire point of the pipeline architecture was to bypass the monolithic filecoin-proofs-api wrapper and call directly into the lower-level proof crates. In Phase 0 and Phase 1, cuzk-core only depended on filecoin-proofs-api, which internally handled all the feature flag propagation. The assistant had never needed to specify features for storage-proofs-porep directly — those were transitive dependencies managed by the wrapper.

When the assistant designed the new dependency graph, it looked at storage-proofs-core (which does have cuda-supraseal) and assumed the pattern held for all sibling crates. This is a classic pitfall in Rust workspace management: transitive dependencies that were previously "hidden" behind a facade crate suddenly become direct dependencies, and their feature flags must be specified correctly. The assistant's mental model was "these are all storage-proofs crates, they should all support the same features," but the actual library design had a more nuanced layering.

This mistake was also influenced by the assistant's earlier research. In message 435, a task agent had traced the seal_commit_phase2_circuit_proofs function and noted that it called into storage-proofs-porep with cuda-supraseal enabled. What the assistant missed was that the cuda-supraseal feature was being propagated through filecoin-proofs, not requested directly from storage-proofs-porep. The feature resolution happens at the top-level crate, which then re-exports the feature to its dependencies with the correct mapping.

Input Knowledge Required

To fully understand this message, the reader needs familiarity with several layers of the Rust and Filecoin ecosystem:

  1. Rust feature flags and conditional compilation: The concept that Cargo features are additive and propagate through [features] sections in Cargo.toml. The reader must understand that cuda-supraseal is not a built-in Rust attribute but a user-defined feature that gates #[cfg(feature = "cuda-supraseal")] blocks.
  2. The Filecoin proof dependency hierarchy: The relationship between filecoin-proofs-api (the top-level Go-facing API), filecoin-proofs (the Rust proof orchestration layer), and the storage-proofs sub-crates (core, porep, post, update). Each layer has different responsibilities and different feature requirements.
  3. The cuzk Phase 2 architecture: Understanding that the pipeline splits CPU synthesis from GPU proving requires knowing what SuprasealParameters, synthesize_circuits_batch, and prove_from_assignments are — the bellperson fork's exposed API that makes the pipeline possible.
  4. The concept of SRS (Structured Reference String) parameters: The large (~32 GiB) parameter files that must be loaded into GPU memory before proving can begin. The SRS manager being built in the same session manages these files, and the feature flag determines whether the Supraseal C++ backend is available.

Output Knowledge Created

This message produces several valuable pieces of knowledge:

  1. The exact feature flag mapping for the storage-proofs crate family at version 19.0.1: storage-proofs-core has cuda-supraseal, while storage-proofs-porep, storage-proofs-post, and storage-proofs-update only have cuda. This is a concrete, actionable finding.
  2. The correct dependency specification for cuzk-core's Cargo.toml: storage-proofs-porep should get features = ["cuda"], not features = ["cuda-supraseal"]. Similarly for post and update.
  3. A diagnostic technique for investigating feature flag mismatches in complex Rust workspaces: iterating over dependency crates and extracting their [features] sections from the local registry cache. This is a reusable debugging pattern.
  4. Confirmation that the filecoin-proofs crate's cuda-supraseal feature acts as a translation layer, mapping cuda-supraseal to cuda for the sub-crates while keeping cuda-supraseal for storage-proofs-core and bellperson. This confirms the architectural layering.

The Thinking Process Revealed

The assistant's reasoning in this message is a textbook example of systematic debugging. The sequence is:

  1. Observe the error: cargo check fails because storage-proofs-porep doesn't have cuda-supraseal.
  2. Form a hypothesis: Maybe the feature name is different across crates.
  3. Design a test: Iterate over all relevant crates and extract their feature declarations.
  4. Execute the test: Run a bash loop that greps each crate's Cargo.toml.
  5. Analyze the results: Compare the feature sections and identify the asymmetry.
  6. Draw the conclusion: The sub-crates use cuda, not cuda-supraseal, and the top-level filecoin-proofs crate maps between them. What's notable is what the assistant doesn't do: it doesn't try to patch the error by removing features or adding cfg gates. It doesn't guess at alternative feature names. It goes straight to the source of truth — the actual Cargo.toml files in the registry cache — and reads the feature definitions verbatim. This is a mature debugging approach that prioritizes evidence over speculation.

Broader Implications for the Phase 2 Implementation

This feature flag diagnosis, while seemingly a minor detour, has significant implications for the Phase 2 pipeline. The SRS manager that was just created in message 447 uses SuprasealParameters from bellperson, which is gated behind #[cfg(feature = "cuda-supraseal")]. The pipeline module that will be built next needs to call synthesize_circuits_batch and prove_from_assignments, which are also behind this feature gate. Getting the feature flags right at the cuzk-core level is essential because:

Conclusion

Message 452 captures a moment of diagnostic clarity in the midst of a complex refactoring. A build error that could have been frustrating — "why doesn't this crate have the feature I expect?" — becomes an opportunity to understand the true architecture of the dependency graph. The assistant's response is measured, systematic, and evidence-driven. It doesn't panic, doesn't guess, and doesn't over-correct. It simply reads the source material and adjusts its mental model accordingly.

This kind of debugging is characteristic of work at the intersection of multiple complex systems: a Rust workspace with intricate feature flag propagation, a GPU proving pipeline with conditional compilation for CUDA backends, and a Filecoin proof ecosystem with layered crate dependencies. The message is small — a single bash command and its output — but it represents the kind of precise, informed decision-making that separates a working implementation from a broken one. Without this diagnosis, the Phase 2 pipeline would have remained in a broken state, and the assistant would have wasted time on incorrect assumptions about the dependency graph.