Overview
Let’s take a look at a simple contract that uses FHE to encrypt a counter, and break it down into its components.Complete Contract Example
Breaking Down the Contract
Importing the FHE Library
To start using FHE, we need to import the FHE library. In this example, we’re importing the typeseuint64 and InEuint64 from the FHE library.
euint64 type.
State Variables
Next, we define some state variables for the contract:Constructor Initialization
In the constructor, we initialize thecounter and delta variables.
We encrypt the delta here to avoid calculating the same encrypted value every time we increment or decrement the counter.
Trivial Encryption
Trivial Encryption
We wanted the example contract to be as simple as possible, so readers can plug-and-play it into their preferred environment.There are some privacy improvements that could be made to this contract.
delta and counter variables, we use trivial encryption.Trivial encryption produces a ciphertext from a public value, but this variable, even though represented as a ciphertext handle, is not really confidential because everyone can see what is the plaintext value that went into it.To make it completely private, we need to initialize these variables with an InEuint from the calldata.More about trivial encryption here.Access Control
For every encrypted variable, we need to callFHE.allowThis() to allow the contract to access it.
Allowing access to encrypted variables is an important concept in FHE-enabled contracts.
Without it, the contract could not continue to use this encrypted variable in future transactions.
You can read more about this in the ACL Mechanism page.
Increment and Decrement Functions
In theincrement_counter and decrement_counter functions, we use the FHE.add and FHE.sub functions to increment and decrement the counter, respectively.
And we also call FHE.allowThis() to allow the contract to access the new counter value.
Reset Function
In thereset_counter function, we receive an InEuint64 value, which is a type that represents an encrypted value that can be used to reset the counter.
This value is an encrypted value that we created using Cofhejs (read more about it here).
Decryption Functions
Now, let’s take a look at thedecrypt_counter and get_counter_value functions.
The decrypt_counter function creates a new decrypt request for the counter.
Since we want to allow users to call get_counter_value function at any given time, we need to store the handle in the lastDecryptedCounter variable.
The result will be valid until the next decrypt_counter call.
In the get_counter_value function, we use the FHE.getDecryptResultSafe function to get the decrypted value of the counter.
Since the decryption is asynchronous, we need to check if the result is ready by checking the decrypted boolean.
If the result is not ready, we revert the transaction with an error message.
Privacy Considerations
In this contract, only the owner can request for a decryption. Once requested, everyone can read the counter’s value at any given time. The owner needs to send a transaction to thedecrypt_counter.
What if we want to allow the owner to privately read the value without sending a transaction that calls FHE.decrypt, exposing the counter to everyone?
For that, we need to add a call for FHE.allow(counter, owner) or FHE.allowSender(counter) every time that we change the counter’s value.
This will allow the owner to read the encrypted counter’s value using the get_encrypted_counter_value function and decrypt it using Cofhejs.
Exercise
Next Steps
- Learn how to use Cofhejs to interact with this contract
- Explore Adding FHE to an Existing Contract
- Review ACL Usage Examples for more access control patterns