The Feature Gate: How Bellperson Dispatches Between Native and Supraseal-C2 Proving

Introduction

In the middle of a deep-dive investigation into Filecoin's SUPRASEAL_C2 Groth16 proof generation pipeline, a single message crystallizes one of the most important architectural decisions in the entire system. Message 51 in this coding session is deceptively brief — a simple sed command that prints the first 30 lines of a Rust source file. But what those lines reveal is the compile-time dispatch mechanism that determines whether Filecoin's proof generation runs on a general-purpose GPU pipeline or on the specialized, high-performance SUPRASEAL_C2 CUDA kernel stack. This message is the moment where the investigation shifts from exploring parameter files and resource requirements to understanding the fundamental architectural fork at the heart of the proving system.

The Message

The assistant executes:

sed -n '1,30p' /home/theuser/.cargo/registry/src/index.crates.io-6f17d22bba15001f/bellperson-0.26.0/src/groth16/prover/mod.rs 2>&1

And the output reveals:

#[cfg(not(feature = "cuda-supraseal"))]
mod native;
#[cfg(feature = "cuda-supraseal")]
mod supraseal;

// ... imports ...

#[cfg(not(feature = "cuda-supraseal"))]
use self::native as prover;
#[cfg(feature = "cuda-supraseal")]
use self::supraseal as prover;

Why This Message Was Written

To understand the motivation behind this message, we must trace the investigation that led to it. The preceding messages (28–50) show the assistant systematically exploring the Filecoin proof parameter landscape. It began by locating parameters.json files in the Go module cache ([msg 28]), then filtering for WindowPoSt, WinningPoSt, and SnapDeals parameters ([msg 30]). It discovered that SnapDeals parameters exist for 32GiB and 64GiB sectors ([msg 32]), and that WindowPoSt and WinningPoSt parameters use a different naming scheme (proof-of-spacetime-fallback) ([msg 35]). It examined actual file sizes on disk, finding a 626 MiB .params file for one SnapDeals variant ([msg 36]).

The investigation then pivoted to resource requirements, grepping for TypeDetails, Ram, and Gpu in Curio task definitions ([msg 37]). This revealed that SnapDeals proving tasks allocate 50 GiB of RAM ([msg 38]), WindowPoSt compute tasks allocate 25 GiB ([msg 38]), and WinningPoSt tasks allocate 1 GiB ([msg 37]). These numbers hinted at dramatically different computational profiles for different proof types.

But the critical question remained: how does the system actually choose between running proofs on the GPU and running them through the SUPRASEAL_C2 pipeline? The assistant began tracing through bellperson's source code ([msg 39]), locating the groth16 prover module ([msg 41]), and attempting to read the dispatch logic. After encountering permission issues with direct file access ([msg 44]), it resorted to copying files to the workspace ([msg 45]), only to be blocked by the external directory rule ([msg 46]). It then used grep to search for key terms like supraseal, create_proof, and cuda ([msg 46]), finding a crucial reference in filecoin-ffi/rust/src/util/api.rs ([msg 47]) that showed feature-gated API dispatch.

Message 51 is the culmination of this search. The assistant finally reads the bellperson prover mod.rs file directly via sed, and the answer is immediately clear: the dispatch is a compile-time feature flag, not a runtime decision.## The Reasoning and Motivation

The assistant's motivation at this point is clear: it needs to understand how the proving system decides between two fundamentally different execution paths. The preceding investigation had established that there are multiple proof types (SnapDeals, WindowPoSt, WinningPoSt, PoRep) with vastly different memory footprints — from 1 GiB for WinningPoSt to 50 GiB for SnapDeals proving. It had also established that the SUPRASEAL_C2 pipeline exists as a specialized CUDA implementation for Groth16 proofs. But the connection between these two facts — how a given proof request ends up in one pipeline versus another — remained opaque.

The key insight from message 51 is that the dispatch is not a runtime decision at all. There is no configuration file, no environment variable, no dynamic check that says "if GPU is available, use supraseal; otherwise, use native." Instead, the Rust compiler makes the choice at compile time through Cargo feature flags. When cuda-supraseal is enabled, the supraseal module is compiled in and aliased as prover. When it is not enabled, the native module (which itself may use CUDA or OpenCL for certain operations) is used instead.

This is a design choice with significant implications. It means that a single binary cannot dynamically fall back between the two proving strategies. If you compile with cuda-supraseal, you get the SUPRASEAL_C2 pipeline regardless of whether a compatible GPU is actually present at runtime. If you compile without it, you get the native GPU pipeline (which uses ec-gpu-gen's CUDA or OpenCL backends) regardless of whether SUPRASEAL_C2 would be more efficient. The two proving strategies are mutually exclusive at the binary level.

Assumptions Made

The assistant makes several implicit assumptions in this message. First, it assumes that the feature flag mechanism is the only dispatch mechanism — that there is no additional runtime indirection within the supraseal or native modules themselves. This assumption is reasonable given the clean #[cfg] gates, but it leaves open the possibility that within the supraseal module, there might be further runtime fallbacks to native code for operations that SUPRASEAL_C2 does not support.

Second, the assistant assumes that the cuda-supraseal feature is the relevant one for understanding the current system's behavior. The Cargo.toml excerpt from message 48 confirms this: cuda-supraseal = ["supraseal-c2"]. But there is also a cuda feature and an opencl feature, which enable GPU acceleration in the native pipeline. The assistant's focus on cuda-supraseal is correct for the SUPRASEAL_C2 investigation, but the full picture includes the possibility that the native pipeline with cuda or opencl features is also a GPU-accelerated path.

Third, the assistant assumes that the mod.rs file it is reading is the definitive dispatch point. The create_proof_batch_priority_inner function referenced at line 253 of the same file is where the actual work happens, and it delegates to prover::create_proof_batch_priority_inner. The assistant has not yet read the supraseal.rs or native.rs implementations to verify that they both implement this function with the same signature. This is a reasonable assumption given Rust's module system, but it is an assumption nonetheless.## Input Knowledge Required

To fully understand message 51, the reader needs several layers of context. First, they need to understand the Rust feature flag system — that #[cfg(feature = "cuda-supraseal")] is a compile-time conditional compilation directive, not a runtime check. Without this knowledge, the significance of the mod native vs mod supraseal gating is lost.

Second, they need to understand the Filecoin proof architecture: that Groth16 proofs are generated by bellperson, a Rust library that implements the Bellman proving system with GPU acceleration. The reader must know that SUPRASEAL_C2 is a specialized CUDA implementation that replaces large portions of the proving pipeline with custom CUDA kernels, and that it was developed to address the memory and performance limitations of the native GPU path.

Third, the reader needs the context of the preceding investigation: the parameter file exploration, the resource requirement discovery, and the search through bellperson's source tree. Message 51 is the payoff of a search that began with parameters.json files and traced through Go task definitions, Rust FFI layers, and Cargo feature declarations. Without this context, the message appears to be a trivial file read; with it, it is the key architectural insight.

Output Knowledge Created

This message creates several pieces of knowledge that fundamentally shape the rest of the investigation. First and foremost, it establishes that the SUPRASEAL_C2 pipeline is a compile-time alternative to the native prover, not a runtime optimization layered on top of it. This means that any analysis of memory usage, performance characteristics, or GPU utilization must be done separately for each compilation configuration.

Second, it reveals the module structure of bellperson's prover. The mod.rs file acts as a dispatch facade: it conditionally includes either native or supraseal as a submodule, then re-exports it under the common name prover. This pattern means that the rest of the groth16 module (the create_proof functions, the parameter handling, the proof serialization) can be written against a uniform prover interface, while the actual implementation is swapped at compile time.

Third, it creates a roadmap for the next phase of investigation. Now that the assistant knows the dispatch is at mod.rs, the natural next steps are to read supraseal.rs to understand what the SUPRASEAL_C2 pipeline actually does, and native.rs to understand the baseline it replaces. The assistant has already copied these files to the workspace (message 45), and subsequent messages will read them to understand the full proving pipeline.

The Thinking Process

The thinking process visible in this message is one of systematic elimination. The assistant has been working through a chain of reasoning:

  1. "I need to find how proof generation works." → Explores parameters.json for proof types and sizes.
  2. "I need to find the resource requirements." → Greps Curio task definitions for RAM and GPU allocations.
  3. "I need to find how bellperson dispatches to GPU." → Traces through bellperson's source tree.
  4. "I need to find the actual dispatch code." → Reads mod.rs via sed. Each step builds on the previous one, and each step narrows the search space. The sed command in message 51 is not a random exploration — it is the targeted reading of a specific file that the assistant has identified as the likely dispatch point. The assistant's comment "This is the key finding" confirms that it recognizes the significance of what it has found. The thinking also shows an awareness of the investigation's broader goals. The root session context describes this as a "deep-dive investigation into SUPRASEAL_C2 Groth16 proof generation pipeline for Filecoin PoRep, mapping the full call chain from Curio to CUDA kernels." Message 51 is a critical node in that call chain — it is the point where the abstract "proof generation" concept forks into two concrete implementation paths.

Potential Misconceptions

One subtle point that could be misunderstood from this message is the relationship between the cuda feature and the cuda-supraseal feature. The cuda feature enables GPU acceleration in the native prover via ec-gpu-gen's CUDA backend. The cuda-supraseal feature enables the entirely separate SUPRASEAL_C2 pipeline. These are not the same thing, and they are not additive. The #[cfg] gates in mod.rs make them mutually exclusive at the module level, though the Cargo.toml shows they are independent features that could theoretically be combined (the cuda-supraseal feature does not imply the cuda feature, and vice versa).

Another potential misconception is that this dispatch mechanism is specific to Filecoin or Curio. In fact, it is a feature of bellperson itself, which is a general-purpose Groth16 proving library used by multiple projects in the Filecoin ecosystem and beyond. The cuda-supraseal feature is a bellperson feature, not a Curio or lotus feature. This means that any project using bellperson with the cuda-supraseal feature enabled will get the SUPRASEAL_C2 pipeline, regardless of whether it is running in a Curio context.

Conclusion

Message 51 is a turning point in the investigation. It transforms the search from a broad exploration of parameters and resource requirements into a focused analysis of two competing proving pipelines. The compile-time dispatch mechanism it reveals has profound implications for how the system is deployed, configured, and optimized. It means that the choice between native GPU proving and SUPRASEAL_C2 proving is baked into the binary at build time, with no runtime flexibility. This architectural decision shapes everything that follows: the memory analysis, the performance characterization, and ultimately the optimization proposals that the investigation will produce.