The Architecture of Choice: How Bellperson Selects Its Proving Backend in Filecoin's Groth16 Pipeline
Introduction
In the complex machinery of Filecoin's proof generation system, few decisions carry as much weight as the selection of the proving backend. The Groth16 proof pipeline that underpins Filecoin's Proof-of-Replication (PoRep) and Proof-of-Spacetime (PoSt) mechanisms must navigate a treacherous landscape of GPU availability, feature flags, and dependency trees. At message index 48 in this investigation, the assistant executes a targeted grep command against bellperson's Cargo.toml to answer a single, decisive question: how does the system decide between supraseal-c2 and standard GPU proving? This message, though brief in its surface form, represents the culmination of a long investigative arc and the pivot point where architectural understanding crystallizes.
Context and Motivation: Why This Question Matters
The message arrives after an extensive exploration spanning dozens of prior interactions. The assistant has been tracing the full call chain of the SUPRASEAL_C2 Groth16 proof generation pipeline, from Curio's Go task layer through Rust FFI into C++/CUDA kernels. The investigation has already uncovered critical details: the parameter files stored in /var/tmp/filecoin-proof-parameters/, the resource allocations in task definitions (25 GiB for WindowPoSt, 50 GiB for SnapDeals), and the structure of the parameters.json that maps proof types to their circuit sizes.
But a crucial gap remains. The assistant has seen references to supraseal-c2 in the dependency tree and has observed that bellperson's prover module contains three distinct paths — a mod.rs orchestrator, a native.rs CPU implementation, and a supraseal.rs GPU-accelerated path. What it does not yet know is how these paths are selected at compile time versus runtime. The grep in message 48 is designed to bridge that gap by examining the feature flags that control conditional compilation.
The motivation is architectural: understanding the feature flag system is essential to understanding the entire proof generation pipeline's deployment characteristics. If cuda-supraseal is a compile-time feature that gates the entire GPU proving path, then the system's behavior is determined at build time, not at runtime. This has profound implications for how Curio (the Filecoin storage mining software) is packaged, deployed, and configured across heterogeneous hardware environments.
The Message Itself: A Targeted Query
The assistant writes:
Now let me check what features bellperson has - specifically around supraseal and cuda:
>
[bash] grep -n "feature\|supraseal\|cuda\|opencl\|gpu\|BELLMAN_NO_GPU" /home/theuser/.cargo/registry/src/index.crates.io-6f17d22bba15001f/bellperson-0.26.0/Cargo.toml 2>&1 | head -40
The command is a textbook example of targeted source code archaeology. Rather than reading the entire Cargo.toml (which could span hundreds of lines), the assistant constructs a regex that captures the essential vocabulary of the feature system: feature, supraseal, cuda, opencl, gpu, and the environment variable BELLMAN_NO_GPU. The head -40 limits output to the most relevant lines, preventing noise from overwhelming the signal.
The output reveals the feature flag architecture:
31:required-features = ["groth16"]
36:required-features = ["groth16"]
49:features = ["__private_bench"]
60:[dependencies.ec-gpu]
63:[dependencies.ec-gpu-gen]
84:default-features = false
94:features = ["derive"]
99:[dependencies.supraseal-c2]
123:features = [
127:default-features = false
146:features = ["__private_bench"]
148:[build-dependencies.ec-gpu-gen]
154:[features]
156:cuda = [
157: "ec-gpu-gen/cuda",
159: "blstrs/gpu",
161:cuda-supraseal = ["supraseal-c2"]
164:opencl = [
165: "ec...
This output, though truncated, tells a rich story. The cuda feature (lines 156-159) activates ec-gpu-gen/cuda and blstrs/gpu — these are the standard GPU acceleration paths using NVIDIA's CUDA platform. The cuda-supraseal feature (line 161) is a distinct flag that activates the supraseal-c2 dependency — a completely different proving backend that uses the SUPRASEAL_C2 library. The opencl feature (line 164) provides an alternative GPU path for non-NVIDIA hardware.
Decisions and Assumptions
The assistant makes several implicit decisions in this message. First, it chooses to examine the Rust crate's Cargo.toml rather than the Go-side configuration or the runtime initialization code. This is a deliberate methodological choice: the feature flag system is the source of truth for compile-time decisions, and understanding it first provides the foundation for understanding runtime behavior.
Second, the assistant assumes that the bellperson crate's feature flags are the primary mechanism for backend selection. This assumption is well-founded given the Rust ecosystem's convention of using Cargo features for conditional compilation, but it is not the complete picture. As later investigation would reveal, the actual dispatch logic in mod.rs uses runtime checks as well — the create_proof function evaluates GPU availability at runtime and falls back to CPU if no GPU is found. The feature flags determine which code paths are compiled, but runtime conditions determine which path is actually executed.
Third, the assistant assumes that the supraseal-c2 dependency (line 99) is the exclusive GPU proving path. In reality, bellperson supports three proving modes: native CPU (pure Rust), GPU-accelerated via ec-gpu-gen (CUDA/OpenCL for NTT and MSM operations), and the full supraseal-c2 pipeline (which offloads the entire Groth16 proof to a C++/CUDA implementation). The cuda and opencl features enable GPU acceleration of individual operations within the Rust prover, while cuda-supraseal replaces the entire prover with the SUPRASEAL_C2 backend. This distinction is critical for understanding the system's memory profile and performance characteristics.
Input Knowledge Required
To fully understand this message, the reader needs several pieces of contextual knowledge. First, the structure of the Filecoin proof system: that Groth16 proofs are generated for PoRep (proving storage of a sector), WindowPoSt (proving ongoing storage over time), and SnapDeals (proving updated sector data). Each proof type has different circuit sizes and parameter files.
Second, the architecture of bellperson: that it is a Rust library implementing Groth16 proving, with multiple backend options. The crate lives at bellperson-0.26.0 in the Cargo registry, and its prover module is organized into mod.rs (orchestration), native.rs (CPU), and supraseal.rs (GPU via SUPRASEAL_C2).
Third, the role of Cargo feature flags in Rust: that features like cuda, opencl, and cuda-supraseal are compile-time flags that conditionally include dependencies and code paths. The default-features = false directive (lines 84, 127) means that consumers must explicitly opt into GPU support.
Fourth, the prior investigation context: that the assistant has already traced parameter file sizes (626 MiB for some SnapDeals params), resource allocations (25 GiB RAM for WindowPoSt, 50 GiB for SnapDeals), and the FFI boundary in filecoin-ffi/rust/src/util/api.rs where #[cfg(any(feature = "opencl", feature = "cuda", feature = "cuda-supraseal"))] gates the GPU initialization path.
Output Knowledge Created
This message produces several concrete pieces of knowledge. The most immediate is the feature flag structure of bellperson 0.26.0: the existence of distinct cuda, cuda-supraseal, and opencl features, each activating different dependency chains. The cuda feature activates ec-gpu-gen/cuda and blstrs/gpu — these are the lower-level GPU acceleration libraries for elliptic curve operations. The cuda-supraseal feature activates the supraseal-c2 dependency, which is a completely separate proving backend.
The message also reveals that default-features = false is set, meaning that a standard build of bellperson will not include any GPU support unless explicitly requested. This has implications for deployment: a naive cargo build without --features cuda or --features cuda-supraseal will produce a CPU-only binary, regardless of the GPU hardware available.
The truncated output hints at the opencl feature structure (line 164-165), which provides an alternative GPU path for AMD or other non-NVIDIA hardware. This is important for understanding the system's portability across different GPU vendors.
The Thinking Process Visible in the Message
The message reveals a clear investigative methodology. The assistant has been following a systematic trace: starting from the Go task layer (tasks/window/compute_task.go, tasks/snap/task_prove.go), moving to the FFI boundary (filecoin-ffi/rust/src/util/api.rs), and now diving into the Rust crate that performs the actual proof generation. The grep command is the natural next step after discovering the #[cfg(...)] attributes in api.rs (message 46) that conditionally compile GPU initialization code.
The assistant's thinking is visible in the transition from message 46 to message 48. In message 46, the assistant notes: "The external directory rule blocks me. Let me use grep to search these files from the workspace" — a workaround for the constraint that prevents reading files outside the workspace directory. The grep finds only two matches in api.rs, both showing #[cfg(any(feature = "opencl", feature = "cuda", feature = "cuda-supraseal"))]. This is the trigger: the assistant realizes that the feature flags are the key to understanding the backend selection, and immediately pivots to examining the Cargo.toml where those features are defined.
The head -40 is also a thinking artifact. The assistant knows that the Cargo.toml could be long and that most lines are irrelevant (version numbers, author metadata, etc.). By limiting output to 40 lines and using a targeted regex, the assistant demonstrates an understanding of where the signal is concentrated: the [features] section typically appears near the end of a Cargo.toml, and the dependency declarations for supraseal-c2, ec-gpu, and ec-gpu-gen are the critical entries.
Mistakes and Incorrect Assumptions
The message itself contains no explicit mistakes — it is a grep command that executes successfully and returns useful data. However, the implicit assumption that feature flags tell the complete story of backend selection is a limitation. As the investigation would later need to discover, bellperson's mod.rs contains runtime dispatch logic that checks for GPU availability even when the cuda feature is enabled. The create_proof function in mod.rs attempts to initialize a GPU device and falls back to the CPU prover if initialization fails. This means that even a binary compiled with --features cuda may use the CPU prover if no compatible GPU is found at runtime.
Additionally, the assistant's focus on supraseal-c2 as the primary GPU path overlooks the fact that the cuda feature (without supraseal) provides significant GPU acceleration through ec-gpu-gen. The ec-gpu-gen crate accelerates Number Theoretic Transforms (NTT) and Multi-Scalar Multiplication (MSM) on GPU, which are the dominant computational kernels in Groth16 proving. The supraseal-c2 path is a more aggressive offloading strategy that moves the entire proof generation to C++/CUDA, but it is not the only GPU acceleration path.
Broader Significance
This message, while small in isolation, represents a critical juncture in the investigation. It is the moment when the assistant shifts from understanding what the system does (parameter files, resource requirements, task types) to understanding how it makes decisions (feature flags, conditional compilation, backend selection). This is the difference between knowing that a system uses GPU acceleration and knowing the mechanism by which that acceleration is selected and configured.
For the broader investigation into the SUPRASEAL_C2 pipeline and its ~200 GiB peak memory footprint, understanding the feature flag architecture is essential. The cuda-supraseal feature gates the entire SUPRASEAL_C2 backend, which is responsible for the high memory usage. Without this feature enabled, the system falls back to the native Rust prover with optional GPU acceleration via ec-gpu-gen, which has a significantly different memory profile. Any optimization proposal — such as the Sequential Partition Synthesis or Persistent Prover Daemon designs developed later in this investigation — must account for which backend is actually in use.
Conclusion
Message 48 is a model of focused, targeted source code investigation. The assistant identifies a critical gap in understanding — how the proving backend is selected — and executes a single, well-crafted command to fill that gap. The output reveals the feature flag architecture that governs the entire proof generation pipeline, from the cuda and opencl features that accelerate individual operations to the cuda-supraseal feature that enables the full SUPRASEAL_C2 backend. This knowledge becomes the foundation for understanding the system's deployment characteristics, memory usage patterns, and optimization opportunities. In the archaeology of complex software systems, the most valuable discoveries often come from asking the right question at the right time — and this message is a perfect example of that principle in action.