Mapping the API Frontier: How a Visibility Audit Shaped the SUPRASEAL_C2 Optimization Pipeline
Introduction
In the world of cryptographic proof systems, memory is the silent bottleneck. The SUPRASEAL_C2 Groth16 proof generation pipeline for Filecoin's Proof-of-Replication (PoRep) consumes approximately 200 GiB of peak memory — a staggering footprint that constrains deployment options, increases cloud rental costs, and limits throughput. A deep-dive investigation into this pipeline had already mapped the full call chain from Curio's Go task layer through Rust FFI into C++/CUDA kernels, identified nine structural bottlenecks, and proposed three ambitious optimization proposals: Sequential Partition Synthesis, Persistent Prover Daemon, and Cross-Sector Batching. But before any of these proposals could be implemented, a fundamental question had to be answered: what parts of the existing Rust codebase are actually accessible to a new CUDA integration layer?
This article synthesizes the work of a critical sub-investigation within that larger effort — a systematic audit of Rust API visibility across four crates that would determine the architectural boundaries within which the optimization proposals must operate. The investigation, spanning multiple rounds of file discovery, source reading, and re-export chain tracing, produced a definitive visibility map that directly shaped the feasibility and design of each optimization proposal.
The Strategic Context: Why Visibility Matters
The broader investigation had established that Filecoin's proof pipeline operates through a layered architecture. At the top, Curio's Go task layer orchestrates proof generation jobs. These flow through Rust FFI into the bellperson crate (a Groth16 proving system), which in turn calls into supraseal-c2 — the C++/CUDA layer that performs the actual GPU-accelerated computation. The peak memory problem stems from two main sources: the pipeline runs 10 partition circuits in parallel, each consuming ~16 GiB, while the Structured Reference String (SRS) occupies another ~48 GiB in pinned GPU memory.
The three optimization proposals aimed to address this:
- Sequential Partition Synthesis — stream partitions one-at-a-time through the GPU instead of holding all 10 in memory simultaneously, reducing peak from ~200 GiB to ~64–103 GiB.
- Persistent Prover Daemon — keep the proving process alive across multiple proofs to eliminate the ~60-second per-proof SRS loading overhead.
- Cross-Sector Batching — use the freed memory headroom to batch multiple sectors' circuits into single GPU invocations, projecting 2–3× throughput per GPU. But each proposal required direct access to specific Rust APIs. Could a new CUDA integration layer — codenamed "cuzk" — call
seal_commit_phase2_circuit_proofsdirectly? Could it constructStackedCircuitinstances with custom parameters? Could it load the SRS from cache without going through the existing high-level API? These questions were not academic; they would determine whether the proposals could be implemented as external tools or would require forking and modifying upstream crates.
The Investigation: A Multi-Round Visibility Audit
The visibility audit unfolded across seven messages in a dedicated subagent session, each round building on the previous one with methodical precision.
Round 1: The Questionnaire (Message 0)
The investigation began with a precise, multi-part query from the user (the parent session agent) to the subagent. The user wanted exact visibility modifiers — pub, pub(crate), or private — for ten specific items across four source files spanning three crates:
storage-proofs-core/src/compound_proof.rs: TheCompoundProoftrait, its methodscircuit_proofs()andcircuit(), the constantMAX_GROTH16_BATCH_SIZE, andSetupParams.storage-proofs-porep/src/stacked/circuit/proof.rs: TheStackedCompoundandStackedCircuittypes, and whether they can be constructed from outside the crate.filecoin-proofs/src/api/seal.rs: Theseal_commit_phase2_circuit_proofsfunction, its intermediate types, andget_stacked_params.filecoin-proofs/src/caches.rs: Theget_stacked_paramsfunction andGROTH_PARAM_MEMORY_CACHE. The user's framing — "key types and functions that cuzk would need to call directly" — revealed the strategic intent. This was not a casual code review; it was a reconnaissance mission to determine the integration surface for a new system.
Round 2: File Discovery (Messages 1–2)
The assistant's first attempt to locate the files used the glob tool, but it apparently failed to produce results — likely because the tool was restricted to the workspace directory, while the crate sources lived under ~/.cargo/registry/src/. Recognizing this limitation, the assistant pivoted to bash with a clear explanation: "Let me use bash to find the files since they're in an external directory" ([msg 2]).
This decision point — abandoning a specialized tool for a general-purpose one — exemplifies the adaptive problem-solving that characterizes effective code archaeology. The bash approach used ls with wildcard paths and 2>/dev/null error suppression, producing clean, resolved absolute paths for all four files. The output confirmed that all files existed under the same crate registry hash (1949cf8c6b5b557f), validating the assumption that the crates were downloaded from a consistent registry snapshot.
Round 3: Reading the Primary Sources (Message 3)
With file paths confirmed, the assistant read all four source files in parallel ([msg 3]). This parallel strategy was a deliberate optimization: since none of the reads depended on each other, issuing them simultaneously minimized round trips within the synchronous round-based execution model.
However, the initial reads revealed an important gap. Reading the source files alone was insufficient to determine external visibility. In Rust, a type declared pub in a submodule is not automatically accessible from outside the crate — it must be re-exported through every intermediate module in the hierarchy. The assistant recognized this and pivoted to trace the re-export chain.
Round 4: Tracing the Module Hierarchy (Messages 4–6)
The assistant systematically read three module declaration files to trace the visibility path from definition site to crate boundary:
circuit/mod.rs([msg 5]): Revealed thatStackedCircuitandStackedCompoundare explicitly re-exported viapub use proof::{StackedCircuit, StackedCompound};, while other submodules (column,column_proof,hash,params) remain private.stacked/mod.rs([msg 5]): Showedpub use circuit::*;— a wildcard re-export that propagates the circuit module's public items upward. Notably, thevanillamodule is declaredpub(crate), making it inaccessible from outside the crate.api/mod.rs([msg 6]): Confirmedpub use seal::*;, which combined withlib.rs'spub use api::*;(verified in message 5) ensuresseal_commit_phase2_circuit_proofsreaches the crate root as a fully public function. This multi-level traversal demonstrated a sophisticated understanding of Rust's visibility model. The assistant was not just reading visibility modifiers — it was tracing the path that each item must travel to reach external consumers.
Round 5: Synthesis and Findings (Message 7)
The culmination of the investigation was a comprehensive visibility map that answered every question with precision. The key findings were:
CompoundProof trait: Public. Its methods circuit_proofs() and circuit() are implicitly public (Rust trait methods cannot be private). This means any code that can name the trait can call these methods — a crucial enabler for the Sequential Partition Synthesis proposal, which needs to invoke circuit_proofs() with different partition configurations.
MAX_GROTH16_BATCH_SIZE: Private, with value 10. This constant controls how many proofs can be batched in a single Groth16 batch proof. Its privacy means external code cannot read or modify it. For the Cross-Sector Batching proposal, this is a hard constraint: larger batch sizes would require modifying the storage-proofs-core crate itself.
StackedCircuit and StackedCompound: Public and re-exported to the crate root. However, the assistant went beyond the user's explicit question and examined field-level visibility. All fields of StackedCircuit are private, meaning the struct cannot be constructed using literal syntax from outside the crate. Instead, three construction pathways exist: via CompoundProof::circuit(), via CompoundProof::blank_circuit(), or via the pub fn synthesize(...) associated function. This distinction between "the type is public" and "you can construct an instance" is critical for integration work.
seal_commit_phase2_circuit_proofs: Fully public, re-exported through api/mod.rs and lib.rs to the crate root. This function is the main entry point for Phase 2 proof generation, and its public visibility means cuzk can call it directly without forking the filecoin-proofs crate.
get_stacked_params: pub(crate). This is arguably the most consequential finding. The function that loads Groth16 parameters (including the ~48 GiB SRS) from cache is not accessible from outside the filecoin-proofs crate. This directly impacts the Persistent Prover Daemon proposal: if the daemon cannot call get_stacked_params, it cannot keep the SRS loaded across invocations without either forking the crate or implementing its own parameter loading logic.
GROTH_PARAM_MEMORY_CACHE: Module-private. The cache itself is not directly accessible, further constraining external optimization attempts.
Implications for the Optimization Proposals
The visibility audit did not merely produce a report — it directly shaped the feasibility and design of each optimization proposal.
Sequential Partition Synthesis
This proposal requires calling StackedCompound::circuit_proofs() with different partition configurations. The audit confirmed that circuit_proofs() is accessible as a trait method on the public CompoundProof trait, and StackedCompound is public. However, the private partitions field means the partition count must be set through the trait's type parameter or constructor, not by directly modifying the struct. This is feasible but constrains the implementation approach — the partition count must be known at compile time or set through a controlled construction pathway.
Persistent Prover Daemon
This proposal faces the most significant obstacle from the visibility audit. The pub(crate) visibility of get_stacked_params means an external process cannot load the SRS through the existing caching layer. The daemon would need to either:
- Be implemented as a library within the
filecoin-proofscrate (giving it access topub(crate)items) - Load parameters through the existing public API (which may have overhead and may not expose the raw SRS)
- Implement its own parameter loading from disk, duplicating logic This finding effectively forces a design decision about where the daemon lives in the codebase. The visibility audit transformed an abstract optimization idea into a concrete architectural constraint.
Cross-Sector Batching
The discovery that MAX_GROTH16_BATCH_SIZE = 10 is a private constant directly constrains this proposal. If the proposal requires larger batches (e.g., to batch multiple sectors' circuits), the constant would need to be changed in the storage-proofs-core crate itself — an operation that requires forking or patching the dependency. Alternatively, batching could be done at a higher level (multiple calls to circuit_proofs()), but this would not achieve the same efficiency gains as a single larger batch proof.
Beyond Visibility: Micro-Optimization Analysis
The visibility audit was not the end of the investigation. A final round of analysis examined the computational hotpaths at the instruction level, providing the data needed to optimize within the constraints the audit had revealed.
CPU Synthesis Hotpaths
The CPU-side circuit synthesis was analyzed at the instruction level, revealing several hotpaths:
- The
enforce()loop in the Rank-1 Constraint System (R1CS) builder, which processes constraints one at a time. This loop is the primary bottleneck in circuit synthesis, with each iteration performing field arithmetic and constraint bookkeeping. - SHA-256 bit manipulation for the PoRep circuit's Merkle proof verification. The SHA-256 operations involve extensive bit-level manipulation that is inherently slow in field arithmetic.
- Fr (field element) addition in the constraint system, where elements of the BLS12-381 scalar field are added, multiplied, and reduced. These hotpaths are CPU-bound and memory-light, making them good candidates for the Sequential Partition Synthesis approach — each partition's circuit can be synthesized independently on CPU, then streamed to GPU for proving.
GPU NTT/MSM Characteristics
On the GPU side, the analysis examined the Number Theoretic Transform (NTT) and Multi-Scalar Multiplication (MSM) operations that dominate the Groth16 proving time:
- NTT is memory-bandwidth-bound, performing O(n log n) field operations on large arrays. The 10 parallel partitions mean 10× the NTT memory footprint.
- MSM is compute-bound, requiring O(n) elliptic curve point additions and scalar multiplications. The MSM for the SRS (48 GiB of curve points) dominates GPU time.
- GPU kernel occupancy analysis showed that the NTT kernels achieve high occupancy (limited by register pressure), while MSM kernels are limited by L1/L2 cache bandwidth when accessing the SRS.
Host-to-Device Transfer Patterns
The analysis characterized the H-to-D (host-to-device) transfer patterns, revealing that the circuit witness data (the a/b/c vectors from the R1CS) is transferred from CPU to GPU for each partition. These vectors are large (~1.6 GiB per partition for the PoRep circuit), contributing significantly to the ~200 GiB peak. The analysis considered recomputing a/b/c vectors on-the-fly on the GPU rather than materializing them in host memory — a technique that would trade compute for memory, potentially reducing peak memory by ~16 GiB (one partition's worth of witness data) at the cost of increased GPU compute time.
The Broader Shift: From Optimization to Architecture
The visibility audit and micro-optimization analysis together represent a shift in the investigation's focus — from optimizing individual proof generation operations toward architecting a continuous, memory-efficient proving pipeline optimized for heterogeneous cloud rental markets where RAM cost dominates.
The key insight is that the ~200 GiB peak memory is not a single problem but a cascade of architectural decisions: 10 parallel partitions, each with its own circuit synthesis and witness materialization, combined with a 48 GiB SRS that is loaded from disk for every proof. The optimization proposals address these decisions at the architectural level:
- Sequential Partition Synthesis changes the parallelism model from spatial (all partitions at once) to temporal (one partition at a time).
- Persistent Prover Daemon changes the process model from ephemeral (one proof per process) to persistent (many proofs per process).
- Cross-Sector Batching changes the workload model from single-sector to multi-sector, amortizing fixed costs across more work. The visibility audit revealed which of these changes can be implemented as external tools and which require modifications to the upstream crates. The
pub(crate)visibility ofget_stacked_paramsmeans the Persistent Prover Daemon cannot be a standalone tool — it must be integrated into thefilecoin-proofscrate or fork it. The privateMAX_GROTH16_BATCH_SIZEmeans Cross-Sector Batching requires upstream changes. The public visibility ofCompoundProofandseal_commit_phase2_circuit_proofsmeans Sequential Partition Synthesis can proceed as an external tool.
Conclusion
The visibility audit of the SUPRASEAL_C2 Groth16 proof generation pipeline was far more than a technical exercise in reading Rust source files. It was a strategic reconnaissance mission that mapped the API boundaries within which all optimization work must operate. By tracing the re-export chains from definition site to crate boundary, the investigation produced a definitive map of what is accessible, what is hidden, and what the boundaries mean for the three optimization proposals.
The findings were consequential: get_stacked_params is pub(crate), forcing a design decision about the Persistent Prover Daemon's architecture. MAX_GROTH16_BATCH_SIZE is private, constraining Cross-Sector Batching to upstream modifications. CompoundProof and seal_commit_phase2_circuit_proofs are fully public, enabling Sequential Partition Synthesis as an external tool. The micro-optimization analysis then provided the instruction-level data needed to optimize within these constraints.
In the end, the investigation transformed the optimization problem from "how do we reduce memory?" to "how do we architect a continuous proving pipeline within the existing API boundaries?" — a shift that reflects the maturity of the analysis and the practical constraints of working with real-world cryptographic codebases. The visibility audit was the foundation upon which all subsequent architectural decisions would be built.## References
[1] "Mapping the API Surface: A Visibility Audit in the Filecoin Proof Pipeline" — Article examining the initial questionnaire message (msg 0) and its strategic purpose in the visibility audit.
[2] "The Art of the Preliminary: How a Single Message Orchestrates Discovery in OpenCode Sessions" — Article analyzing the assistant's first response (msg 1) and the decision to use glob for file discovery.
[3] "Reading the Source: A Data-Gathering Pivot in the Filecoin Proof Pipeline Investigation" — Article covering the parallel file read operation (msg 3) and its role in the investigation.
[4] "Tracing the Visibility Chain: How One Message Mapped Rust's Module Re-Export Topology for Filecoin Proofs" — Article examining the mod.rs reads (msg 5) and the re-export chain tracing.
[5] "Navigating Rust's Visibility Maze: A Deep Dive into Filecoin Proof Pipeline API Boundaries" — Article synthesizing the complete visibility findings (msg 7) and their implications.
[6] "Tracing Visibility Through Rust's Module Hierarchy: A Subagent's Investigation into Filecoin Proof Types" — Article analyzing the module hierarchy verification step (msg 4).
[7] "Navigating the Crate Jungle: How a Simple ls Unlocked the Filecoin Proof Pipeline's API Surface" — Article covering the bash-based file discovery pivot (msg 2).
[8] "Tracing the Re-export Chain: A Subagent's Systematic Visibility Investigation" — Article examining the api/mod.rs read (msg 6) and its role in completing the re-export chain.