Chapter 7 / 15
FHE Basics
encrypted<T>, fhe_add, fhe_mul — computing on encrypted state.
Covenant's FHE type system lets you write contracts where state is never
decrypted on-chain. The encrypted<T> type annotates a field
or variable as ciphertext. Operations (fhe_add, fhe_mul)
compile to STATICCALL instructions targeting chain precompile addresses
— the actual FHE implementation lives in the chain's precompile layer, not in
Covenant. Covenant is fully scheme-agnostic (TFHE, BGV, CKKS, …).
private_counter.cov
record PrivateCounter {
count: encrypted;
owner: address;
// Increment without revealing the current count
action increment() only(self.owner) {
// fhe_add compiles to STATICCALL → precompile 0x0300
self.count = fhe_add(self.count, encrypted(1u64));
}
action add(n: encrypted) only(self.owner) {
self.count = fhe_add(self.count, n);
}
// Reveal the decrypted value — only owner can call
view read_count() -> u64 only(self.owner) {
return decrypt(self.count);
}
} encrypted_voting.cov — private ballot tally
record PrivateBallot {
yes_votes: encrypted;
no_votes: encrypted;
end_time: time;
owner: address;
voted: map(address => bool);
error AlreadyVoted();
error VotingClosed();
// Vote without revealing direction
action vote(choice: encrypted)
when(block.timestamp < self.end_time)
{
if self.voted[msg.sender] {
revert_with AlreadyVoted();
}
self.voted[msg.sender] = true;
// Homomorphic addition — tally stays encrypted
self.yes_votes = fhe_add(self.yes_votes, choice);
self.no_votes = fhe_add(
self.no_votes,
fhe_sub(encrypted(1u64), choice)
);
}
// Owner tallies after vote ends — decrypts locally
view tally() -> (u64, u64)
only(self.owner)
when(block.timestamp >= self.end_time)
{
return (decrypt(self.yes_votes), decrypt(self.no_votes));
}
} Annotations
encrypted<T> | is a first-class type. You cannot accidentally pass it to a function expecting T — the compiler rejects the type mismatch. |
fhe_add / fhe_mul | compile to STATICCALL targeting addresses 0x0300–0x0308. The chain's precompile determines the underlying FHE scheme. |
encrypted(v) | wraps a plaintext value as a client-side ciphertext before sending to the contract. |
decrypt(c) | calls the decryption precompile. Only the keyholder can obtain the plaintext — on chains with threshold decryption, this requires a quorum. |
| Scheme agnosticism | Covenant emits the same bytecode regardless of whether the chain uses TFHE, BGV, or CKKS. See LICENSE_CLARIFICATION.md. |
Key takeaways
encrypted<T>fields are stored as ciphertext on-chain — observers see only the encryption, not the value.- FHE arithmetic is exact for integers (TFHE) and approximate for fixed-point (CKKS) — choose based on chain support.
- Covenant never imports any FHE library — the chain's precompile layer handles all crypto.