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:
- Rust feature flags and conditional compilation: The concept that Cargo features are additive and propagate through
[features]sections inCargo.toml. The reader must understand thatcuda-suprasealis not a built-in Rust attribute but a user-defined feature that gates#[cfg(feature = "cuda-supraseal")]blocks. - 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. - The cuzk Phase 2 architecture: Understanding that the pipeline splits CPU synthesis from GPU proving requires knowing what
SuprasealParameters,synthesize_circuits_batch, andprove_from_assignmentsare — the bellperson fork's exposed API that makes the pipeline possible. - 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:
- The exact feature flag mapping for the storage-proofs crate family at version 19.0.1:
storage-proofs-corehascuda-supraseal, whilestorage-proofs-porep,storage-proofs-post, andstorage-proofs-updateonly havecuda. This is a concrete, actionable finding. - The correct dependency specification for
cuzk-core'sCargo.toml:storage-proofs-porepshould getfeatures = ["cuda"], notfeatures = ["cuda-supraseal"]. Similarly forpostandupdate. - 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. - Confirmation that the
filecoin-proofscrate'scuda-suprasealfeature acts as a translation layer, mappingcuda-suprasealtocudafor the sub-crates while keepingcuda-suprasealforstorage-proofs-coreandbellperson. 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:
- Observe the error:
cargo checkfails becausestorage-proofs-porepdoesn't havecuda-supraseal. - Form a hypothesis: Maybe the feature name is different across crates.
- Design a test: Iterate over all relevant crates and extract their feature declarations.
- Execute the test: Run a bash loop that greps each crate's
Cargo.toml. - Analyze the results: Compare the feature sections and identify the asymmetry.
- Draw the conclusion: The sub-crates use
cuda, notcuda-supraseal, and the top-levelfilecoin-proofscrate 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 addingcfggates. It doesn't guess at alternative feature names. It goes straight to the source of truth — the actualCargo.tomlfiles 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:
- If
cuzk-coredoesn't enablecuda-suprasealonstorage-proofs-core, the SRS parameter loading code won't compile. - If
cuzk-coreincorrectly requestscuda-suprasealfromstorage-proofs-porep, the build fails (as we saw). - If
cuzk-coreforgetscudaonstorage-proofs-porep, the GPU proving path for PoRep won't be available. The correct configuration —cuda-suprasealonstorage-proofs-coreandbellperson,cudaon the sub-crates — mirrors exactly whatfilecoin-proofsdoes internally. The assistant is essentially replicating the feature propagation that the top-level crate previously handled, now at thecuzk-corelevel.
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.