Skip to content

docs: the guest and provider examples in putting-it-together describe APIs that changed #1807

Description

@Toby1009

Updated 2026-08-20 after #1821. That PR rewrote docs/pages/tutorials/write-e3-program.mdx
against Risc0ComputeProof and added the input-root invariants to agent/INVARIANTS.md, which
resolved four of the five items this issue originally listed under "Stale copies". What follows is
what still reproduces, on main at 3c7172c8. #1821 also widened the remaining item and added a
second one, because the guest and the ComputeProvider trait both changed.

The guest commits ComputeJournal: nine 32-byte values, in the order chain ID, Interfold address,
E3 ID, encryption scheme ID, committee public-key hash, ciphertext-output hash, SAFE commitment,
parameter hash, and input root (crates/support/types/src/lib.rs:69-79). The encoding is RISC Zero
serde rather than ABI: one length word plus 32 byte words per value, which gives 132 bytes each and
1,188 in total. Risc0ComputeProof.sol:13-14 mirrors it with FIELD_SIZE = 132 and
FIELD_COUNT = 9.

The guest example commits the wrong thing

docs/pages/putting-it-together.mdx:25-48 shows a guest that reads FHEInputs and commits the
processor's raw output:

let fhe_inputs: FHEInputs = env::read();
let result = fhe_processor(&fhe_inputs);
env::commit(&result);

The guest in the repository reads a ComputeGuestInput from stdin, runs the Secure Process under
the user program's policy, and commits the journal built from the result
(crates/support/methods/guest/src/bin/program.rs:18-28):

let input: ComputeGuestInput = deserialize(&decode_input(&input_slice).unwrap()).unwrap();
let result = input.input.process(fhe_processor, policy()).unwrap();
let journal = ComputeJournal::new(input.domain, result).unwrap();
env::commit(&journal);

A guest copied from the page emits a journal holding the raw ciphertext bytes.
Risc0BfvCiphertextVerifier.verify rebuilds the digest from chain state plus the envelope's input
root and passes it to the RISC Zero verifier (Risc0BfvCiphertextVerifier.sol:38-65), so that
receipt does not verify. No incorrect result is accepted; the cost is a developer who cannot ship.

The page also predates InputPolicy. ComputeInput::process now takes one, and the page's call has
no place to put it.

The provider example on the same page has the same drift

docs/pages/putting-it-together.mdx:57-90 declares:

fn prove(&self, input: &ComputeInput) -> Self::Output

#1821 gave the trait method a second parameter, policy: InputPolicy
(crates/compute-provider/src/ciphertext_output.rs:21), and both implementations in the repository
carry it (crates/support/host/src/lib.rs:69, :403). An impl copied from the page does not
satisfy the trait.

The layout is written out in more places than it is checked

crates/support/host/src/lib.rs:535-581 pins the length and a SHA-256 digest on the Rust side, in
compute_result_journal_matches_crisp_layout. That test passes today, and the digest it pins,
4403934eb9404372…, appears nowhere under packages, examples, or templates. The TypeScript
side has its own encodeVec32 and concatenates the fields independently
(packages/interfold-contracts/test/Risc0BfvCiphertextVerifier.spec.ts:19-23, :56), and no shared
fixture links the two. Each side can change its implementation and its own expectation together and
still pass while disagreeing across the layer boundary.

Happy to open a PR for the page if it is useful.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    documentationImprovements or additions to documentation

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions