mirror of
https://github.com/zama-ai/tfhe-rs.git
synced 2026-01-07 22:04:10 -05:00
238 lines
7.9 KiB
Markdown
238 lines
7.9 KiB
Markdown
# Contributing to TFHE-rs
|
|
|
|
This document provides guidance on how to contribute to **TFHE-rs**.
|
|
|
|
There are two ways to contribute:
|
|
|
|
- **Report issues:** Open issues on GitHub to report bugs, suggest improvements, or note typos.
|
|
- **Submit codes**: To become an official contributor, you must sign our Contributor License Agreement (CLA). Our CLA-bot will guide you through this process when you open your first pull request.
|
|
|
|
## 1. Setting up the project
|
|
|
|
Start by [forking](https://docs.github.com/en/pull-requests/collaborating-with-pull-requests/working-with-forks/fork-a-repo) the **TFHE-rs** repository.
|
|
|
|
{% hint style="info" %}
|
|
- **Rust version**: Ensure that you use a Rust version >= 1.81 to compile **TFHE-rs**.
|
|
- **Incompatibility**: AArch64-based machines are not yet supported for Windows as it's currently missing an entropy source to be able to seed the [CSPRNGs](https://en.wikipedia.org/wiki/Cryptographically_secure_pseudorandom_number_generator) used in **TFHE-rs**.
|
|
- **Performance**: For optimal performance, it is highly recommended to run **TFHE-rs** code in release mode with cargo's `--release` flag.
|
|
{% endhint %}
|
|
|
|
To get more details about the library, please refer to the [documentation](https://docs.zama.ai/tfhe-rs).
|
|
|
|
## 2. Creating a new branch
|
|
|
|
When creating your branch, make sure to use the following format :
|
|
|
|
```
|
|
git checkout -b {feat|fix|docs|chore…}/short_description
|
|
```
|
|
|
|
For example:
|
|
|
|
```
|
|
git checkout -b feat/new_feature_X
|
|
```
|
|
|
|
## 3. Before committing
|
|
|
|
### 3.1 Linting
|
|
|
|
Each commit to **TFHE-rs** should conform to the standards of the project. In particular, every source code, docker or workflows files should be linted to prevent programmatic and stylistic errors.
|
|
|
|
- Rust source code linters: `clippy`
|
|
- Typescript/Javascript source code linters: `eslint`, `prettier`
|
|
|
|
To apply automatic code formatting, run:
|
|
|
|
```
|
|
make fmt
|
|
```
|
|
|
|
You can perform linting of all Cargo targets with:
|
|
|
|
```
|
|
make clippy_all_targets
|
|
```
|
|
|
|
### 3.2 Testing
|
|
|
|
Your contributions must include comprehensive documentation and tests without breaking existing tests. To run pre-commit checks, execute:
|
|
|
|
```
|
|
make pcc
|
|
```
|
|
|
|
This command ensure that all the targets in the library are building correctly.
|
|
For a faster check, use:
|
|
|
|
```
|
|
make fpcc
|
|
```
|
|
|
|
If you're contributing to GPU code, run also:
|
|
|
|
```
|
|
make pcc_gpu
|
|
```
|
|
|
|
Unit testing suites are heavy and can require a lot of computing power and RAM availability.
|
|
Whilst tests are run automatically in continuous integration pipeline, you can run tests locally.
|
|
|
|
All unit tests have a command formatted as:
|
|
|
|
```
|
|
make test_*
|
|
```
|
|
|
|
Run `make help` to display a list of all the commands available.
|
|
|
|
To quickly test your changes locally, follow these steps:
|
|
1. Locate where the code has changed.
|
|
2. Add (or modify) a Cargo test filter to the corresponding `make` target in Makefile.
|
|
3. Run the target.
|
|
|
|
{% hint style="success" %}
|
|
`make test_<something>` will print the underlying cargo command in STDOUT. You can quickly test your changes by copy/pasting the command and then modify it to suit your needs.
|
|
{% endhint %}
|
|
|
|
For example, if you made changes in `tfhe/src/integer/*`, you can test them with the following steps:
|
|
1. In `test_integer` target, replace the filter `-- integer::` by `-- my_new_test`.
|
|
2. Run `make test_integer`.
|
|
|
|
## 4. Committing
|
|
|
|
**TFHE-rs** follows the conventional commit specification to maintain a consistent commit history, essential for Semantic Versioning ([semver.org](https://semver.org/)).
|
|
Commit messages are automatically checked in CI and will be rejected if they do not comply, so make sure that you follow the commit conventions detailed on [this page](https://www.conventionalcommits.org/en/v1.0.0/).
|
|
|
|
## 5. Rebasing
|
|
|
|
Before creating a pull request, rebase your branch on the repository's `main` branch. Merge commits are not permitted, thus rebasing ensures fewer conflicts and a smoother PR review process.
|
|
|
|
## 6. Opening a Pull Request
|
|
|
|
Once your changes are ready, open a pull request.
|
|
|
|
For instructions on creating a PR from a fork, refer to GitHub's [official documentation](https://docs.github.com/en/pull-requests/collaborating-with-pull-requests/proposing-changes-to-your-work-with-pull-requests/creating-a-pull-request-from-a-fork).
|
|
|
|
## 7. Continuous integration
|
|
|
|
Before a pull request can be merged, several test suites run automatically. Below is an overview of the CI process:
|
|
|
|
```mermaid
|
|
---
|
|
title: Continuous Integration Process
|
|
---
|
|
sequenceDiagram
|
|
autonumber
|
|
|
|
participant Contributor
|
|
participant GitHub
|
|
participant Reviewer
|
|
participant CI-pipeline
|
|
|
|
Contributor ->> GitHub: Open pull-request
|
|
GitHub -->> Contributor: Ask for CLA signing (once)
|
|
loop
|
|
Reviewer ->> GitHub: Review code
|
|
Reviewer ->> CI-pipeline: Approve workflows (short-run)
|
|
CI-pipeline -->> GitHub: Send checks results
|
|
Contributor ->> GitHub: Make changes
|
|
end
|
|
Reviewer ->> GitHub: Pull-request approval
|
|
Reviewer ->> CI-pipeline: Approve workflows (long-run)
|
|
CI-pipeline -->> GitHub: Send checks results
|
|
Reviewer -->> GitHub: Merge if pipeline green
|
|
```
|
|
|
|
{% hint style="info" %}
|
|
|
|
## Useful details:
|
|
|
|
- pipeline is triggered by humans
|
|
- review team is located in Paris timezone, pipeline launch will most likely happen during office hours
|
|
- direct changes to CI related files are not allowed for external contributors
|
|
- run `make pcc` to fix any build errors before pushing commits
|
|
{% endhint %}
|
|
|
|
## 8. Data versioning
|
|
|
|
Data serialized with TFHE-rs must remain backward compatible. This is done using the [tfhe-versionable](https://crates.io/crates/tfhe-versionable) crate.
|
|
|
|
If you modify a type that derives `Versionize` in a backward-incompatible way, an upgrade implementation must be provided.
|
|
|
|
For example, these changes are data breaking:
|
|
* Adding a field to a struct.
|
|
* Changing the order of the fields within a struct or the variants within an enum.
|
|
* Renaming a field of a struct or a variant of an enum.
|
|
* Changing the type of field in a struct or a variant in an enum.
|
|
|
|
On the contrary, these changes are *not* data breaking:
|
|
* Renaming a type (unless it implements the `Named` trait).
|
|
* Adding a variant to the end of an enum.
|
|
|
|
Historical data from previous TFHE-rs versions are stored inside `utils/tfhe-backward-compat-data`. They are used to check on every PR that backward compatibility has been preserved.
|
|
|
|
## Example: adding a field
|
|
|
|
Suppose you want to add an i32 field to a type named `MyType`. The original type is defined as:
|
|
```rust
|
|
#[derive(Serialize, Deserialize, Versionize)]
|
|
#[versionize(MyTypeVersions)]
|
|
struct MyType {
|
|
val: u64,
|
|
}
|
|
```
|
|
And you want to change it to:
|
|
```rust
|
|
#[derive(Serialize, Deserialize, Versionize)]
|
|
#[versionize(MyTypeVersions)]
|
|
struct MyType {
|
|
val: u64,
|
|
other_val: i32
|
|
}
|
|
```
|
|
|
|
Follow these steps:
|
|
|
|
1. Navigate to the definition of the dispatch enum of this type. This is the type inside the `#[versionize(MyTypeVersions)]` macro attribute. In general, this type has the same name as the base type with a `Versions` suffix. You should find something like
|
|
|
|
```rust
|
|
#[derive(VersionsDispatch)]
|
|
enum MyTypeVersions {
|
|
V0(MyTypeV0),
|
|
V1(MyType)
|
|
}
|
|
```
|
|
|
|
2. Add a new variant to the enum to preserve the previous version of the type. You can simply copy and paste the previous definition of the type and add a version suffix:
|
|
|
|
```rust
|
|
#[derive(Version)]
|
|
struct MyTypeV1 {
|
|
val: u64,
|
|
}
|
|
|
|
#[derive(VersionsDispatch)]
|
|
enum MyTypeVersions {
|
|
V0(MyTypeV0),
|
|
V1(MyTypeV1),
|
|
V2(MyType) // Here this points to your modified type
|
|
}
|
|
```
|
|
|
|
3. Implement the `Upgrade` trait to define how we should go from the previous version to the current version:
|
|
```rust
|
|
impl Upgrade<MyType> for MyTypeV1 {
|
|
type Error = Infallible;
|
|
|
|
fn upgrade(self) -> Result<MyType, Self::Error> {
|
|
Ok(MyType {
|
|
val: self.val,
|
|
other_val: 0
|
|
})
|
|
}
|
|
}
|
|
```
|
|
|
|
4. Fix the upgrade target of the previous version. In this example, `impl Upgrade<MyType> for MyTypeV0 {` should simply be changed to `impl Upgrade<MyTypeV1> for MyTypeV0 {`
|