Programmable Privacy and Onchain Compliance using Homomorphic Encryption
A few months back, the crypto team at a16z published the Nakamoto Challenge, a list of the most important problems to solve in blockchain. The fourth one in particular caught our attention: “Compliant Programmable Privacy”, as we have been actively thinking about this for some time. Today, we are proposing a first solution using homomorphic encryption and our fhEVM confidential smart contract protocol (if you’re not familiar with the fhEVM, you can read our articles about confidential ERC20 token and blind auctions).
The fhEVM is a regular EVM with some precompiles that enables computing on encrypted states using our TFHE-rs homomorphic encryption library. From the perspective of the developer, there is no cryptography involved: they simply write Solidity code using the encrypted data types we provide (euint32, ebool, etc). One of the big advantages of the fhEVM vs other privacy solutions is that all the data and computation happens onchain. This means you can have the same level of composability and data availability as regular, plaintext contracts.
This property is key to building programmable privacy, as all the access control logic can be defined in the contract itself. There is nothing that needs to be hardcoded into the protocol, and nothing the user has to do offchain to be compliant. The application can enforce compliance directly, with just a few lines of Solidity code!
In this article, we will show how to build a compliant ERC20 token, using onchain DIDs. The source code for this tutorial can be found in the examples folder of the fhEVM repository.
Identity abstraction via onchain, confidential DIDs
A Decentralized Identifier (DID) is a unique digital identity that is issued by an entity such as a government, a registrar, a company or the user itself. This DID can be tied to a cryptographic key that proves the user owns the DID, such as an EVM wallet. But it can also store a whole host of attributes, such as the user’s age, nationality, social security number etc. These attributes in turn can then be used to prove that you satisfy some condition (called an “attestation”), such as being over 18 or not being a Narnia citizen.
Most DIDs are implemented client side, and use zero-knowledge proofs to generate attestations. While this is fine in many cases, it quickly becomes complicated when you have multiple users involved in a transaction, when you have to apply complex rules to the DID, or when you need to keep a common set of rules for everyone to follow. It’s essentially the same tradeoff as in edge vs cloud applications.
Having a centralized DID registry however would solve these issues, as you could then simply ask the registry to check that everyone is compliant. It would also make it simpler to keep track of regulations, as you would only need to implement it in a single place. A blockchain would be a perfect infrastructure for this, as it would enable composability between DIDs and applications that require compliance, as well as composability between regulations themselves.
Problem: everyone would see the identity of everyone!
Fortunately we have a solution: homomorphic encryption, and more specifically the fhEVM! Thanks to the ability to have composability on encrypted state, we can host the user DIDs directly onchain in encrypted form, and have compliant applications verify attributes using a simple contract call. The ability to manage an identity via a smart contract, which we call “Identity Abstraction”, is akin to how one can manage funds via smart contract with Account Abstraction.
This tutorial has 3 parts:
- Identity Abstraction is done via a registry contract that is responsible for managing identities and attestations. Here we assume the DIDs are official government IDs. The registry itself is managed by a central authority (e.g. the AFNIC) who can create registrars (e.g. KYC companies such as Onfido, Jumio, etc..) who then in turn can create user DIDs. The user then goes through their registrar to manage and update their DIDs.
- Regulation is defined in a contract that encodes a set of rules for token transfers between individuals, based on information contained in their DIDs. It basically enforces regulation at the contract level rather than the user level.
- Compliant Confidential Transfers are implemented in a compliant ERC20 contract that uses the regulation contract to enforce compliance in token transfers, without any changes to the ERC20 API itself. In this example we use a confidential ERC20 contract, where balances and amounts are hidden, but it would work just as well with a regular, plaintext ERC20 token.
The identity registry contract
The IdentityRegistry contract is a registry of user DIDs that are issued by registrars and include a set of encrypted identifiers, such as their nationality, age, social security number etc. These identifiers are stored as encrypted 32 bit values (euint32).
The contract also handles permissions, such as:
- Enabling the contract owner (e.g. AFNIC) to add, remove or update registrars.
- Enabling registrars to add, remove or update the user DIDs they created.
- Allowing users to grant smart contracts access to specific attributes of their DIDs. It’s important to note here that the user is responsible for not giving access to malicious contracts, just like they are responsible for not letting malicious contracts spend their tokens.
As a first step, let’s implement the logic for creating and managing DIDs:
Now the next step is to implement the logic for identifiers and access control.
An identifier is simply a string (e.g. “date of birth”) and an encrypted 32 bit value. It can be created or updated only by the registrar. A user can’t create their own identifiers, as we want them to be certified by the registrar.
Since identifiers are encrypted however, the user needs to give permission to a contract to access specific values, which we will handle through a simple access control mechanism similar to how you can allow a contract to spend your ERC20 tokens.
We can now wrap up our identity registry contract by adding the necessary getters, with some conditions and error handling.
The regulation contract
The next step is to create our regulation contract.
When implementing a set of rules for transfers between two individuals, it's important to recognize that these rules may evolve over time. Having a single smart contract defining all the regulation for a given context such as money transfer means that ERC20 contracts don’t have to keep track of regulation themselves. Governments can simply update this contract, and it will automatically propagate to all tokens that implemented it.
At the core, the regulation contract is just a set of conditions that are matched against encrypted identity attributes. To avoid misuse, users won't directly grant access to the regulation contract, but rather grant access to the ERC20 token contract, who then performs a delegate call to the regulation contract. This approach ensures that only the ERC20 contract, which the user trusts, can access their information. Keep in mind that both the sender and the receiver have to have granted permission to the ERC20 contract before a transfer can happen between them.
In this example, we will implement some basic rules:
- Transfers within a country are unlimited, but transfers to a foreign country is capped at 10,000 tokens.
- A blacklisted user can’t transfer or receive tokens.
- A user cannot transfer tokens to a blacklisted country.
Rather than failing the transaction, which could reveal sensitive information, we will simply set the transfer amount to 0 if one of the conditions is not met. This uses a homomorphic ternary operator called a cmux: [.c-inline-code]value = TFHE.cmux(encryptedCondition, valueIfTrue, valueIfFalse);[.c-inline-code]
The compliant confidential ERC20 contract
Now that we have an identity registry and a regulation contract, we can finally create our compliant, privacy preserving token contract. This contract will be called CompliantERC20 and have the following key features:
- The user balance and transfer amount are encrypted.
- Compliance is enforced in transfers by calling the regulation contract.
- Visibility of certain balances can be granted to whitelisted addresses (eg regulators)
The regulation contract is invoked via a simple call. This implies that users must provide access to the ERC20 contract before initiating any transfer; otherwise, the transfer will be reverted.
Finally, we can now create our ERC20 contract:
Similarly to how users grant permissions to DeFi protocols to spend their tokens, they will need to grant permission to the contract to access identifiers needed by the regulation contract. This is done via a call to Identity.grantAccess(contractAddress, identifiers), which can be retrieved by calling the ERC20.identifiers() view method. This list comes directly from ERC20Rules contract to allow update of properties.
Compliance and privacy can coexist!
Hopefully this tutorial has shown you that compliance isn’t a difficult thing to build if the rights tools are available. While we initially built the fhEVM to enable privacy in blockchain, we quickly realized that this technology could be used for identity management and thus programmable compliance.
The proposed design here is far from perfect, but we believe it can easily be improved and launched as a real world use case, so that compliance no longer has to be synonymous with surveillance!
Additional links
- Star Zama's fhEVM Github repository to endorse our work.
- Review Zama's fhEVM documentation.
- Get support on our community channels.
- Help advance the FHE space with the Zama Bounty Program: Create an on-chain game that keeps private states hidden using Zama's fhEVM programmable privacy (15,000€ in prize).