-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
bf8a4a1
commit a811acb
Showing
11 changed files
with
269 additions
and
36 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1 +1,57 @@ | ||
# Architecture | ||
|
||
The following diagram shows an fhEVM-native blockchain with 4 validators. | ||
|
||
```mermaid | ||
graph LR; | ||
Validator1{{Validator1 Node}} | ||
Validator1-- execution API ---Executor1 | ||
Executor1(Executor1) | ||
Validator2{{Validator2 Node}} | ||
Validator2-- execution API ---Executor2 | ||
Executor2(Executor2) | ||
Validator3{{Validator3 Node}} | ||
Validator3-- execution API ---Executor3 | ||
Executor3(Executor3) | ||
Validator4{{Validator4 Node}} | ||
Validator4-- execution API ---Executor4 | ||
Executor4(Executor4) | ||
FullNode{{Full Node}} | ||
FullNode-- execution API ---ExecutorFull | ||
ExecutorFull(Executor F) | ||
dApp[dApp] | ||
fhevmjs[fhevmjs] | ||
dApp-- uses ---fhevmjs | ||
fhevmjs-- HTTP ---Gateway | ||
fhevmjs-- RPC ---FullNode | ||
Gateway(((Gateway))) | ||
Gateway-- TKMS txns, events ---TKMS | ||
Gateway-- RPC/WebSocket ---FullNode | ||
TKMS[[TKMS]] | ||
``` | ||
|
||
_Note:_ For brevity, we don't show P2P connections between validators and the full node in the diagram. | ||
|
||
Each validator has two components: | ||
* the validator node software that executes blocks and connects to other validators over the blockchain's P2P network | ||
* the Executor that is responsible for the actual FHE computation | ||
|
||
The Executor exposes an API that the validator node uses to send FHE computation requests. | ||
|
||
A full node is similar to validators in the sense that it executes all blocks. The difference is that the full node doesn't have stake in the network and, therefore, cannot propose blocks. The full node has all the blockchain data locally. It can be used by the Gateway over RPC or WebSocket endpoints, allowing the Gateway to fetch storage proofs, fetch ciphertexts, listen for events on the fhEVM blockchain, etc. | ||
|
||
The Gateway is a client from the TKMS' perspective and sends decryption/reencryption transactions, listens for "decryption ready" events, etc. | ||
|
||
A dApp uses the **fhevmjs** library to interact with the fhEVM. Some examples are: | ||
* connect over HTTP to the Gateway for reencryptions | ||
* encrypt and decrypt data from the blockchain | ||
* send transactions via a full node | ||
* get the FHE public key from a full node | ||
|
||
The TKMS is used to manage secret FHE key material and securely execute decryptions, reencryptions, key generation, etc. The TKMS is itself a blockchain. See [TKMS](../tkms/architecture.md). |
This file was deleted.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,37 +1,39 @@ | ||
# Contracts | ||
|
||
The fhEVM runs totally on chain symbolically. Essentially, inputs to FHE operations are symbolic values (also called handles) that refer to ciphertexts. We check constraints on these handles, but ignore their actual values. | ||
The fhEVM employs symbolic execution - essentially, inputs to FHE operations are symbolic values (also called handles) that refer to ciphertexts. We check constraints on these handles, but ignore their actual values. | ||
|
||
On the coprocessor, we actually execute the FHE operations on the ciphertexts the handles refer to. If a new ciphertext is generated in the coprocessor as a result of an FHE operation, it is inserted into the blockchain under a handle that is deterministically generated on both the blockchain and the coprocessor. | ||
On the Executor, we actually execute the FHE operations on the ciphertexts the handles refer to. If a new ciphertext is generated in the Executor as a result of an FHE operation, it is inserted into the blockchain (into the ciphertext storage contract, see [Storage](storage.md)) under a handle that is deterministically generated in the TFHEExecutor contract. | ||
|
||
## Coprocessor contract | ||
## TFHEExecutor Contract | ||
|
||
Symbolic execution on the blockchain is implemented via the [Coprocessor](https://github.com/zama-ai/fhevm/blob/main/lib/TFHEExecutor.sol) contract. One of the main responsibilites of the Coprocessor contract is to deterministically generate ciphertext handles. For this, we hash the FHE operation requested and the inputs to produce the result handle H: | ||
Symbolic execution on the blockchain is implemented via the [TFHEExecutor](https://github.com/zama-ai/fhevm/blob/main/lib/TFHEExecutor.sol) contract. One of the main responsibilites of the TFHEExecutor contract is to deterministically generate ciphertext handles. For this, we hash the FHE operation requested and the inputs to produce the result handle H: | ||
|
||
``` | ||
H = keccak256(2, fheOperation, input1, input2, ..., inputN) | ||
H = keccak256(fheOperation, input1, input2, ..., inputN) | ||
``` | ||
|
||
We use 2 as a domain separator for result handles. | ||
Note that inputs can either be other handles or plaintext values. as described in FHE Execution and implemented in the newHandle() functions. | ||
Inputs can either be other handles or plaintext values. | ||
|
||
## ACL | ||
_Note:_ As of now, TFHEExecutor emloys precompiles and not symbolic execution. It will soon be migrated to symbolic execution. | ||
|
||
The [ACL](https://github.com/zama-ai/fhevm/blob/main/lib/ACL.sol) Contract enforces access control for ciphertexts. The model we adopt is very simple - a ciphertext is either allowed for an address or not. An address can be any address - either an EOA address or a contract address. We implement that via the pairs member variable. Essentially, it is a set of keccak256(handle, address) values, where the handle refers to a ciphertext. If a (handle, address) pair is in the set, the ciphertext the handle refers to can be used by the address. In Solidity, we implement the set as a mapping such that all values already in the mapping are true. Access control applies to both passing ciphertexts from one contract to another, for decryption and for reencryption of a ciphertext to a user-provided key. | ||
## ACL Contract | ||
|
||
We use keccak256(handle, address) in order to both save space by persisting only one value instead of two and, also, to allow for a single storage slot proof for reencryption. | ||
The [ACL](https://github.com/zama-ai/fhevm/blob/main/lib/ACL.sol) contract enforces access control for ciphertexts. The model we adopt is very simple - a ciphertext is either allowed for an address or not. An address can be any address - either an EOA address or a contract address. Essentially, it is a mapping from handle to a set of addresses that are allowed to use the handle. | ||
|
||
Access control applies to passing ciphertexts from one contract to another, for FHE computation on ciphertexts, for decryption and for reencryption of a ciphertext to a user-provided key. | ||
|
||
### Garbage Collection of Allowed Ciphertexts Data | ||
|
||
The pairs field in the ACL contract grows indefinitely as new ciphertexts are produced. We might want to expose ways for developers to reclaim space by marking that certain ciphertexts are no longer needed and, consequently, zeroing the slot in pairs. A future effort will look into that. | ||
Data in the ACL contract grows indefinitely as new ciphertexts are produced. We might want to expose ways for developers to reclaim space by marking that certain ciphertexts are no longer needed and, consequently, zeroing the slot in the ACL. A future effort will look into that. | ||
|
||
## Gateway Contract | ||
|
||
## Gateway contract | ||
The [Gateway](https://github.com/zama-ai/fhevm/blob/main/gateway/GatewayContract.sol) contract is an onchain contract designed to interact with an offchain Gateway component that handles decryption requests. When a dApp calls the `requestDecryption` function, the Gateway contract emits an event that is caught by the Gateway service. | ||
|
||
The [Gateway](https://github.com/zama-ai/fhevm/blob/main/gateway/GatewayContract.sol) contract is an on-chain contract designed to interact with an off-chain oracle that handles decryption requests. When a dApp calls the `requestDecryption` function, the contract emits an event that is caught by the Gateway service. | ||
Note: It is possible to have multiple Gateways, so multiple Gateway contracts can also be deployed. | ||
_Note_: It is possible to have multiple Gateways, so multiple Gateway contracts can also be deployed. | ||
|
||
## KMSVerifier contract | ||
## KMSVerifier Contract | ||
|
||
The [KMSVerifier](https://github.com/zama-ai/fhevm/blob/main/lib/KMSVerifier.sol) contract allows any dapp to verify a received decryption. This contract exposes a function `verifySignatures` which receives the decryption and signatures coming from the KMS. | ||
The [KMSVerifier](https://github.com/zama-ai/fhevm/blob/main/lib/KMSVerifier.sol) contract allows any dApp to verify a received decryption. This contract exposes a function `verifySignatures` which receives the decryption and signatures coming from the TKMS. | ||
|
||
Verifier addresses are stored and updated in the contract. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1 +1,52 @@ | ||
# Execution | ||
|
||
Block execution in fhEVM-native is split into two parts: | ||
* symbolic execution | ||
* FHE computation | ||
|
||
Symbolic execution happens onchain, inside the TFHEExecutor contract (inside the EVM). Essentially, the EVM accumulates all requested FHE operations in a block with their input handles and the corresponding result handles. It also remembers which result handles are stored via the SSTORE opcode. No FHE computations are done inside the EVM itself. | ||
|
||
At the end of the block, the EVM sends a networking call to the Executor with the accumulated FHE computations. The Executor is free to do the FHE computations via any method, e.g. in parallel, on a cluster of compute nodes, via CPUs, GPUs, FPGAs or ASICs. The EVM waits until FHE computation for the block is done. | ||
|
||
Finally, when results are returned to the EVM, it persists onchain the ciphertexts whose handles have been SSTOREd during symbolic execution. That way the EVM can avoid persisting ciphertexts that are intermediate results and are never actually stored by the smart contract developer. | ||
|
||
```mermaid | ||
sequenceDiagram | ||
participant Node | ||
participant Executor | ||
loop Block Execution - Symbolic | ||
Note over Node: Symbolic Execution on handles in Solidity | ||
Note over Node: Inside EVM: computations.add(op, [inputs], [result_handles], [input_ciphertexts]) | ||
Note over Node: Inside EVM: if SSTORE(location, result) then sstored.add(result) | ||
end | ||
Note over Node: End of Block Execution | ||
Node->>+Executor: SyncCompute (SyncComputeRequest(computations)) | ||
loop FHE Computation | ||
Note over Executor: Read Inputs from SyncComputeRequest | ||
Note over Executor: FHE Computation | ||
end | ||
Executor->>-Node: SyncComputeResponse (results) | ||
Note over Node: Persist `sstored` Ciphertexts from `results` onchain | ||
Note over Node: Commit Block | ||
``` | ||
|
||
## Symbolic Execution | ||
|
||
As mentioned, symbolic execution doesn't do any FHE computations. Instead, it only operates on input handles, checking constraints on them and deterministically producing result handles based on the input ones. | ||
|
||
For more information on what symbolic execution does, please look at the [TFHEExecutor](https://github.com/zama-ai/fhevm/blob/main/lib/TFHEExecutor.sol) contract. | ||
|
||
## Interaction with the TFHEExecutor Contract | ||
|
||
The TFHEExecutor contract is deployed when the chain is created and is at a well-known address that is also known by blockchain nodes. When a node (validator or full node) detects a call to this address (a CALL or STATITCCALL opcode), the EVM running in the node looks at the function signature and determines which FHE computation is being requested. The result handle is the result of this particular call to the TFHEExecutor contract and the EVM can accumulate it in the computations list for the block. | ||
|
||
## FHE Computation Data Dependencies | ||
|
||
Note that the EVM sends both input handles and result handles to the Executor. It is able to do that, because result handles are computed symbolically in the TFHEExecutor contract. That allows the Executor to do parallel FHE computation by analysing which computations are independent. | ||
|
||
The Executor can detect a conflict if an output of computation A (or the output of another computation depending on the output of A) is also used as an input in a subsequent computation B. We call these computations `dependent` and we need to execute them in order. | ||
|
||
On the other hand, if two computations have inputs that are not related to their outputs, we call them `independent` and the Executor can schedule them to run in parallel. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1 +1,11 @@ | ||
# Genesis | ||
|
||
## Contracts | ||
|
||
For an fhEVM-native blockchain to operate and execute FHE computations, certain contracts need to be available when creating the chain - see [Contracts](contracts.md). Strictly speaking, these contracts don't have to be available in the genesis block and can be deployed in the second block of the chain, at runtime. | ||
|
||
## Keys | ||
|
||
FHE-related keys need to available for the chain to operate properly. For example, a public FHE execution key is needed at the Executor to be able to compute on encrypted data. | ||
|
||
As a convenience, the FHE public key can also be stored on validators/full nodes. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1 +1,46 @@ | ||
# Inputs | ||
|
||
When we talk about inputs, we refer to encrypted data the users send to the fhEVM-native blockchain. Data is in the form of FHE ciphertexts. An example would be the amount to be transferred when calling an ERC20 transfer function. | ||
|
||
## ZKPoK | ||
It is important that confidential data sent by users cannot be seen by anyone. Without measures, there are multiple ways that could happen, for example: | ||
* anyone decrypting the ciphertext | ||
* anyone doing arbitrary computations via the ciphertext (e.g. adding 0 to it), producing a new ciphertext that itself is decrypted (including malicious actors using ciphertexts of other users) | ||
* using the ciphertext in a malicious contract that leads to decryption | ||
|
||
Furthermore, if users are allowed to send arbitrary ciphertexts (including malformed ones or maliciously-crafted ones), that could lead to revealing data about the FHE secret key. | ||
|
||
Therefore, we employ zero-knowledge proofs of knowledge (ZKPoK) of input FHE ciphertexts that guarantee: | ||
|
||
* ciphertext is well-formed (i.e. encryption has been done correctly) | ||
* the user knows the plaintext value | ||
* the input ciphertext can only be used in a particular smart contract | ||
|
||
The ZKPoK is verified by validator nodes when the input byte array is passed to an `TFHE.asEuintXX()` function to convert from a ciphertext to a handle that can be used in smart contracts for FHE operations. | ||
|
||
## Compact Input Lists | ||
|
||
To greatly reduce the size of FHE ciphertexts inputs, we utilize a feature called compact lists. It allows us to pack multiple values efficiently. It is useful when there is only one input and even more so when the are multiple inputs in a call to a smart contract. | ||
|
||
We define the `einput` type that refers to a particular ciphertext in the list. The list itself is serialized and passed as a byte array. For example, `inputA` and `inputB` refer to ciphertexts in the list and the serialized list is `inputProof`: | ||
|
||
```solidity | ||
// SPDX-License-Identifier: BSD-3-Clause-Clear | ||
pragma solidity ^0.8.24; | ||
import "fhevm/lib/TFHE.sol"; | ||
contract Adder { | ||
euint32 result; | ||
function add(einput inputA, einput inputB, bytes calldata inputProof) public { | ||
euint32 a = TFHE.asEuint32(inputA, inputProof); | ||
euint32 b = TFHE.asEuint32(inputB, inputProof); | ||
result = TFHE.add(a, b); | ||
TFHE.allow(result, address(this)); | ||
} | ||
} | ||
``` | ||
|
||
Note that `inputProof` also contains the ZKPoK. |
Oops, something went wrong.