When reading the FHEVM code, I have a question about the input verification flow:
When fromExternal is called, it internally call FHEVMExecutor.verifyInput function and emits a VerifyInput event. However, this event doesn’t seem to be written to the coprocessor’s off-chain database (via insert_tfhe_event). So how does the system actually verify the legitimacy of user-submitted ciphertexts?
From what I can see, gw-listener monitors the VerifyProofRequest event emitted by the verifyProofRequest function in the Gateway’s InputVerification contract. But here’s my question: who calls this verifyProofRequest contract function? Is it the coprocessor? If so, at what point in the workflow does it get triggered? It clearly can’t be the user calling it themselves.
Intuitively, calling fromExternal should directly trigger the external input verification flow, but the design introduces an additional verifyProofRequest function, which is confusing. I don’t understand why this extra layer is necessary, and it’s also unclear how this mechanism is supposed to be used in practice.
The verifyProofRequest function in the Gateway’s InputVerification contract is called by the user (or frontend app via relayer), not the coprocessor. This function emits the VerifyProofRequest event, which the coprocessors listen to. They then verify the user’s ZK proof, unpack the ciphertexts, and generate signatures (attestation). These signatures are later passed to the smart contract’s fromExternal call, which verifies them onchain.
When calling fromExternal in a contract, the verification flow should in principle be similar to verifyProofRequest. So why does the user still need to go through the gateway and call verifyProofRequest for validation? Wouldn’t calling fromExternal directly be sufficient? I’m still confused about this.
Calling fromExternal in a contract is not sufficient on its own because it only verifies signatures (attestation) that were previously generated by coprocessors.
These signatures are produced only after the user submits a ciphertext and ZK proof to the Gateway via verifyProofRequest. That’s the actual step where the coprocessors validate the ciphertext’s correctness and user ownership.
So, fromExternal is just the final onchain check to ensure the input matches the verified data, not the place where the heavy verification happens.
Do you mean that one must first call verifyProofRequest, then obtain the signature from the VerifyProofResponse event emitted in verifyProofResponse, use that signature to construct the inputProof parameter of fromExternal, and only then call fromExternal?
In other words, user-generated ciphertexts must go through verifyProofRequest before fromExternal can be called — is that correct?
For user-generated ciphertexts, the required flow is: the user first calls verifyProofRequest on the Gateway to have coprocessors verify the ZK proof and produce signatures (attestation), then uses those signatures as the inputProof passed into fromExternal; calling fromExternal directly without going through verifyProofRequest will fail because there is no valid attestation and no materialized ciphertext behind the handle.