Complete Example
This page follows one E3 through the default template. The E3 adds two encrypted numbers. Each step names the file that does the work, the contract function it calls, and the event that marks the result. It is for developers who want to see how the parts of an E3 program connect before they change one. At the end, you know where to look when a step fails.
Before you start
Make a project with interfold init and complete its setup. See Quick Start.
Template paths on this page are relative to the project root. Paths that start with crates/ or
packages/ are in the Interfold repository.
The local scripts run in dev mode (E3_PROGRAM__DEV=true, unless TEMPLATE_REAL_PROOFS=1). The
compute proof is a mock, and MockOpenVmReceiptVerifier accepts it. The rest of the flow is the
same as in production.
The E3 at a glance
E3ProgramRegisteredDeploy
deploy/default.ts deploys the verifiers and MyProgram, and registers the program.
Start the stack
Run pnpm dev:all in the project root. scripts/dev_all.sh calls
scripts/dev_all_concurrently.sh, which starts six processes:
| Process | Command | What it does |
|---|---|---|
FRONTEND | pnpm dev:frontend | Serves the client at http://localhost:3000 |
EVM | anvil | Local chain on port 8545, chain ID 31337, one block each second |
CHAIN | node ./scripts/anvil-automine.mjs | Moves the chain time forward and fulfills local randomness requests |
CIPHER | pnpm dev:ciphernodes | Deploys the contracts and starts five ciphernodes |
SERVER | TEST_MODE=1 pnpm dev:server | Runs the coordination server, server/index.ts, on port 8080 |
PROGRAM | pnpm dev:program | Runs interfold program start, which starts the program server on port 13151 |
Follow one E3
Deploy the contracts
scripts/dev_ciphernodes.sh runs scripts/deploy-local.ts. That script deploys the Interfold
contracts with mocks, and then calls deployTemplate in deploy/default.ts. deployTemplate does
these actions in order:
- Deploys
MockOpenVmReceiptVerifier(withTEMPLATE_UNPROVED_TEST=1on the local chain) or anOpenVmReceiptVerifierfor the guest thatinterfold program compilebuilt. - Deploys
OpenVmBfvCiphertextVerifierwith that receipt verifier and itsimageId. - Calls
setCiphertextVerifierforkeccak256("fhe.rs:BFV"). - Deploys
MyProgramwith the Interfold address, the receipt verifier, and theimageId. - Calls
registerE3Program. The Interfold contract probessupportsInterfaceand emitsE3ProgramRegistered.
deployTemplate writes the addresses to interfold.config.yaml. Then dev_ciphernodes.sh starts
the ciphernodes with interfold nodes up.
Request the E3
In the client, client/src/pages/steps/RequestComputation.tsx builds the request. It uses committee
size 0 (Minimum), parameter set 0, and an input window. The window opens 15 seconds after the
current chain time and lasts 100 seconds. It calls getE3Quote, approveFeeToken, and requestE3
from the SDK.
On-chain, Interfold.request stores the E3 and calls MyProgram.validate. validate stores
keccak256(e3ProgramParams), starts an input tree of depth 20, and returns
keccak256("fhe.rs:BFV"). The Interfold contract takes the fee, asks the registry for a committee,
and emits E3Requested.
Publish the committee key
The ciphernodes select a committee, run DKG, and publish the committee public key. The E3 moves to
KeyPublished. The serialized key arrives in CommitteePublicKeyChunkPublished events.
server/index.ts listens for these events in setupEventListeners. It assembles the key and checks
it against the commitment with validatePublicKeyCommitment. Then scheduleE3 waits until the
chain time reaches inputWindow[1].
Encrypt and submit the inputs
client/src/pages/steps/EnterInputs.tsx encrypts each number with encryptNumber and computes its
SAFE commitment with computeCiphertextCommitment. It calls publishInput in
client/src/utils/input.ts for each input:
const data = encodeAbiParameters(parseAbiParameters('bytes, bytes32'), [
input,
ciphertextCommitment,
])MyProgram.publishInput inserts the commitment as a leaf and emits
InputPublished(e3Id, ciphertext, index).
Run the secure process
When the input window closes, runProgram in server/index.ts reads
paramSetRegistry(e3.paramSet) from the Interfold contract. It reads all InputPublished events
for the E3 from MyProgram. The server skips the E3 when it has fewer than two inputs.
callFheRunner in server/runner.ts posts the job to http://127.0.0.1:13151/run_compute. The
body has the E3 ID, the proof domain, the parameters, the inputs, and the callback URL
http://127.0.0.1:8080.
The dev runner (crates/program/runner/src/dev.rs) builds a ComputeManager with your
fhe_processor from program/src/lib.rs. start(policy()) builds the input tree, runs the
processor, and computes the output hash, commitment, parameter hash, and input root. The runner
encodes a mock proof with the parameter hash and the input root. With real proofs, the OpenVM
service runs the same secure process natively, proves it in guest/, and returns the verified
OpenVM envelope instead.
Publish the output
The program server posts the webhook to the server. handleWebhookRequest in server/index.ts
stores the ciphertext under its Keccak-256 hash in .interfold/data-availability. Then it calls
publishCiphertextOutput through the SDK.
Interfold.publishCiphertextOutput does these checks in order:
- The E3 is in
KeyPublished, the input window is closed, and the compute deadline has not passed. The output is not published yet, and the committee still meets its threshold. MyProgram.verifyDataAvailabilitychecks that the Keccak-256 hash of the raw ciphertext equals the content hash.OpenVmBfvCiphertextVerifier.verifyrebuilds the journal and calls the receipt verifier.MyProgram.verifycompares the parameter hash and the input root, rebuilds the journal, and calls the receipt verifier.
The E3 moves to CiphertextReady. The Interfold contract emits CiphertextOutputReferencePublished
and E3StageChanged.
Decrypt the result
The ciphernodes fetch the output bytes from the server at /availability/objects/<contentHash>.
This is the mock_http data-availability mode in interfold.config.yaml. The committee makes
decryption shares. The aggregator combines T + 1 of them and calls publishPlaintextOutput. The E3
moves to Complete, and the Interfold contract emits PlaintextOutputPublished.
client/src/pages/steps/EncryptSubmit.tsx listens for PlaintextOutputPublished. It decodes the
output with decodePlaintextOutput and shows the sum.
The Interfold contract emits CiphertextOutputReferencePublished when it accepts the output. The
SDK event type CIPHERTEXT_OUTPUT_PUBLISHED listens for CiphertextOutputPublished, which
Interfold.sol declares but does not emit.
Where to change the example
| Change | Files |
|---|---|
| The computation | program/src/lib.rs (fhe_processor) |
| The input leaf or the selection | program/src/lib.rs (policy) and contracts/MyProgram.sol (publishInput), together |
| Input validation | contracts/MyProgram.sol (publishInput) |
| The request parameters | client/src/pages/steps/RequestComputation.tsx |
| How inputs reach the compute provider | server/index.ts (runProgram) and server/runner.ts |
| How the result is shown | client/src/pages/steps/EncryptSubmit.tsx and client/src/pages/steps/Results.tsx |
Compare with CRISP
CRISP uses the same parts with production choices:
| Step | Template | CRISP |
|---|---|---|
| Input submission | One publishInput call with ciphertext and commitment | publishInput with a Noir proof, then finalizeInput with a DA receipt |
| Input leaf | The SAFE commitment | sha256(keccak256(ciphertext) ‖ commitment ‖ slot ‖ parent) mod the BN254 field |
| Input policy | InputPolicy::default() | policy::crisp(), one input for each slot |
| Compute provider | Dev runner with a mock proof | CRISP's OpenVM service and guest, proved on the operator's GPU or CPU |
| Data availability | Local file store and a hash check | AvailVectorXDataAvailabilityVerifier (a mock on a local chain) |
| Output publisher | server/index.ts | server/ (opens in a new tab) in the CRISP repository (/state/add-result webhook) |
Get help
Gnosis Guild (opens in a new tab) is the initial development team of the Interfold. Contact Gnosis Guild with questions about this example.