The Missing Header: Tracing Enum Values Across Four Language Boundaries

In the middle of implementing Phase 1 of the cuzk proving engine — a distributed GPU proving daemon for Filecoin — the assistant issued a seemingly simple grep command that would determine the correctness of an entire subsystem. The message, appearing at index 312 in the conversation, reads:

Now let me check what the C header has for these numeric values: [grep] REGISTERED_PO_ST_PROOF_STACKED_DRG_WINNING32|REGISTERED_PO_ST_PROOF_STACKED_DRG_WINDOW32|REGISTERED_UPDATE_PROOF_STACKED_DRG32 No files found

This two-line message — a grep invocation followed by its empty result — captures a critical moment in a much larger investigation. The assistant was attempting to trace how proof-type enum values propagate from Curio's Go orchestration layer, through CGO bindings, into a C header, and ultimately into the Rust filecoin-proofs-api that performs the actual Groth16 proof generation. The grep's failure to find results is not an error; it is a signal that redirects the investigation to the correct source of truth.

The Context: Wiring Up Four Proof Types

The broader effort was Phase 1 of the cuzk project, which aimed to transform a PoRep-only proving daemon into a universal proving engine supporting all four Filecoin proof types: PoRep (already implemented in Phase 0), WinningPoSt, WindowPoSt, and SnapDeals (formerly called EmptySectorUpdate). Each proof type requires calling a different function in the filecoin-proofs-api Rust crate, and each function takes a registered_proof enum value that identifies the proof variant (sector size, proof version, etc.).

The challenge is that these enum values originate in Curio's Go codebase, traverse a CGO FFI bridge into a C shared library, and then pass into Rust code. The gRPC API that cuzk exposes to Curio sends these values as plain integers. For the cuzk engine to dispatch proof requests correctly, it must map each integer to the right Rust enum variant. A single off-by-one error in this mapping would cause the wrong proving function to be called, producing invalid proofs or runtime panics.

Why This Message Was Written

The assistant had already accomplished substantial research by this point. It had read every source file in the cuzk workspace ([msg 286]), examined the filecoin-proofs-api function signatures ([msg 287]), studied Curio's Go FFI layer to understand vanilla proof serialization formats ([msg 288]), and traced the Go enum values through the CGO constant definitions ([msg 311]). The missing piece was the authoritative mapping from integer values to enum variants.

The grep in message 312 was targeting the C header file (filcrypto.h) that sits between Go's CGO layer and the Rust FFI library. The assistant hypothesized that the C header would contain #define constants like REGISTERED_PO_ST_PROOF_STACKED_DRG_WINNING32 with explicit numeric values. Finding these would provide a single source of truth that could be cross-referenced against both the Go abi enum and the Rust filecoin-proofs-api enum.

The grep pattern was carefully constructed: it used the C-style macro naming convention (REGISTERED_PO_ST_PROOF_...) with underscores, matching the pattern seen in the CGO const.go file where Go constants like RegisteredPoStProofStackedDrgWinning32GiBV1 were assigned via C. prefix references to C symbols. The pipe-separated alternatives covered WinningPoSt, WindowPoSt, and the Update (SnapDeals) proof types — all three new proof types being implemented in Phase 1.

The Empty Result: A Redirect, Not a Dead End

The grep returned "No files found." This is the most significant aspect of the message. The C header did not contain the expected #define constants. This negative result forced the assistant to pivot its investigation strategy.

The absence of C macro definitions is itself informative. It reveals that the enum value mapping is not centralized in a C header but is instead defined independently in each language layer, connected only by the #[repr(i32)] attribute in the Rust FFI code. The Go side has its own abi.RegisteredPoStProof enum with implicit integer values (0, 1, 2, ...). The Rust FFI side has its own RegisteredPoStProof enum with #[repr(i32)] and explicit discriminant values. These two enums must agree on the integer values, but there is no shared C header enforcing the correspondence — only convention and testing (as seen in the proofs_test.go file that asserts equality between Go and CGO values).

This is a brittle design pattern. If the Rust FFI enum were ever reordered or had a variant inserted, the integer values would shift silently, breaking the Go-to-Rust mapping without any compile-time error. The grep's empty result thus reveals an important architectural property of the system: the enum mapping relies on positional agreement rather than symbolic cross-referencing.

Assumptions and Their Consequences

The assistant made several assumptions in crafting this grep. First, it assumed the C header would use a specific naming convention (REGISTERED_PO_ST_PROOF_...). This was a reasonable inference from the CGO const.go file, which references C. symbols. However, the actual C header may use a different naming scheme, or the constants may be generated by bindgen rather than hand-written, producing less human-readable names.

Second, the assistant assumed the C header was the authoritative source for numeric values. In reality, the Rust FFI enum with #[repr(i32)] is the definitive source, as it is the enum that the filecoin-proofs-api actually consumes. The C header is merely an intermediate translation layer.

Third, the assistant assumed the grep would find matches in the filcrypto.h header file. The search scope was the current directory tree (/home/theuser/curio/extern/filecoin-ffi/), but the actual C header might be located in a different path or generated at build time rather than stored in the repository.

These assumptions were not mistakes — they were productive hypotheses that guided the investigation. The empty result eliminated one possible source of truth and pointed toward the correct one: the Rust FFI enum definition, which the assistant would read in the very next messages ([msg 313], [msg 314], [msg 315]).

Input Knowledge Required

To understand this message, one must know several things:

  1. The cuzk architecture: A distributed proving daemon that accepts proof requests via gRPC and dispatches them to GPU workers. The proof type is identified by an integer in the protobuf message.
  2. The Filecoin proof type hierarchy: There are four proof families (PoRep, WinningPoSt, WindowPoSt, SnapDeals), each with multiple variants for different sector sizes (2KiB, 8MiB, 512MiB, 32GiB, 64GiB) and protocol versions (V1, V1_1, V1_2).
  3. The multi-language FFI stack: Curio (Go) → CGO bindings → C shared library (filcrypto.h) → Rust FFI (filecoin-ffi) → filecoin-proofs-apifilecoin-proofs (bellperson-based Groth16). Enum values must pass correctly through all layers.
  4. The grep tool and regex syntax: The pipe character for alternation and the use of REGISTERED_PO_ST_PROOF as a prefix pattern matching C macro conventions.
  5. The #[repr(i32)] attribute in Rust: This specifies that a Rust enum should use C-compatible i32 representation, making its discriminant values directly accessible from C code.

Output Knowledge Created

This message produced one piece of knowledge: the C header does not contain the expected enum constant definitions. This negative result is valuable because it:

The Thinking Process Visible in the Message

The assistant's reasoning is visible in the structure of the grep itself. The three alternatives in the pipe-separated pattern reveal a systematic mind: the assistant is thinking about all three new proof types simultaneously (WinningPoSt, WindowPoSt, Update/SnapDeals) and searching for them in a single pass. This is efficient and thorough.

The naming convention used in the grep — REGISTERED_PO_ST_PROOF_STACKED_DRG_WINNING32 — shows the assistant is extrapolating from the Go naming pattern (RegisteredPoStProof_StackedDrgWinning32GiBV1) to a hypothetical C macro name. The transformation involves:

Conclusion

Message 312 is a small grep command with an empty result, but it represents a critical investigative step in a complex multi-language integration effort. It demonstrates the research-driven development methodology that characterizes the entire cuzk project: before writing any code, the assistant exhaustively traces data flows across all system boundaries, verifying assumptions at each layer. The empty grep result is not a failure — it is a discovery that redirects the investigation toward the correct source of truth, ultimately enabling the correct implementation of proof type dispatching for all four Filecoin proof families.