diff --git a/.github/workflows/check_commit.yml b/.github/workflows/check_commit.yml index 31f913bb0b..c381ce8c7c 100644 --- a/.github/workflows/check_commit.yml +++ b/.github/workflows/check_commit.yml @@ -10,7 +10,7 @@ jobs: - name: Check first line uses: gsactions/commit-message-checker@16fa2d5de096ae0d35626443bcd24f1e756cafee with: - pattern: '^((feat|fix|chore|refactor|style|test|docs|doc)(\([\w\-_]+\))?\:) .+$' + pattern: '^((feat|fix|chore|refactor|style|test|docs|doc)(\([\w\-_]+\))?\!?\:) .+$' flags: "gs" error: 'Your first line has to contain a commit type and scope like "feat(my_feature): msg".' excludeDescription: "true" # optional: this excludes the description body of a pull request diff --git a/.gitignore b/.gitignore index ac5a6f09e4..0aad68ba3d 100644 --- a/.gitignore +++ b/.gitignore @@ -7,6 +7,7 @@ target/ # In case of symlinked keys /keys +**/*.rmeta **/Cargo.lock **/*.bin diff --git a/Cargo.toml b/Cargo.toml index 323d22495f..96f9a264fd 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -8,10 +8,13 @@ members = [ "concrete-csprng", "backends/tfhe-cuda-backend", "utils/tfhe-versionable", - "utils/tfhe-versionable-derive" + "utils/tfhe-versionable-derive", ] + exclude = [ - "tfhe/backward_compatibility_tests" + "tfhe/backward_compatibility_tests", + "utils/cargo-tfhe-lints-inner", + "utils/cargo-tfhe-lints" ] [profile.bench] diff --git a/Makefile b/Makefile index cd901105f7..0e8d375cf9 100644 --- a/Makefile +++ b/Makefile @@ -21,6 +21,7 @@ BENCH_OP_FLAVOR?=DEFAULT NODE_VERSION=22 FORWARD_COMPAT?=OFF BACKWARD_COMPAT_DATA_URL=https://github.com/zama-ai/tfhe-backward-compat-data.git +BACKWARD_COMPAT_DATA_BRANCH=v0.1 BACKWARD_COMPAT_DATA_DIR=tfhe-backward-compat-data # sed: -n, do not print input stream, -e means a script/expression # 1,/version/ indicates from the first line, to the line matching version at the start of the line @@ -148,6 +149,11 @@ install_tarpaulin: install_rs_build_toolchain cargo $(CARGO_RS_BUILD_TOOLCHAIN) install cargo-tarpaulin --locked || \ ( echo "Unable to install cargo tarpaulin, unknown error." && exit 1 ) +.PHONY: install_tfhe_lints # Install custom tfhe-rs lints +install_tfhe_lints: + (cd utils/cargo-tfhe-lints-inner && cargo install --path .) && \ + cd utils/cargo-tfhe-lints && cargo install --path . + .PHONY: check_linelint_installed # Check if linelint newline linter is installed check_linelint_installed: @printf "\n" | linelint - > /dev/null 2>&1 || \ @@ -330,6 +336,11 @@ clippy_cuda_backend: install_rs_check_toolchain RUSTFLAGS="$(RUSTFLAGS)" cargo "$(CARGO_RS_CHECK_TOOLCHAIN)" clippy --all-targets \ -p tfhe-cuda-backend -- --no-deps -D warnings +.PHONY: tfhe_lints # Run custom tfhe-rs lints +tfhe_lints: install_tfhe_lints + cd tfhe && RUSTFLAGS="$(RUSTFLAGS)" cargo tfhe-lints \ + --features=$(TARGET_ARCH_FEATURE),boolean,shortint,integer -- -D warnings + .PHONY: build_core # Build core_crypto without experimental features build_core: install_rs_build_toolchain install_rs_check_toolchain RUSTFLAGS="$(RUSTFLAGS)" cargo $(CARGO_RS_BUILD_TOOLCHAIN) build --profile $(CARGO_PROFILE) \ @@ -1037,7 +1048,7 @@ write_params_to_file: install_rs_check_toolchain .PHONY: clone_backward_compat_data # Clone the data repo needed for backward compatibility tests clone_backward_compat_data: - ./scripts/clone_backward_compat_data.sh $(BACKWARD_COMPAT_DATA_URL) tfhe/$(BACKWARD_COMPAT_DATA_DIR) + ./scripts/clone_backward_compat_data.sh $(BACKWARD_COMPAT_DATA_URL) $(BACKWARD_COMPAT_DATA_BRANCH) tfhe/$(BACKWARD_COMPAT_DATA_DIR) tfhe/$(BACKWARD_COMPAT_DATA_DIR): clone_backward_compat_data @@ -1067,7 +1078,7 @@ sha256_bool: install_rs_check_toolchain .PHONY: pcc # pcc stands for pre commit checks (except GPU) pcc: no_tfhe_typo no_dbg_log check_fmt lint_doc check_md_docs_are_tested check_intra_md_links \ -clippy_all check_compile_tests +clippy_all tfhe_lints check_compile_tests .PHONY: pcc_gpu # pcc stands for pre commit checks for GPU compilation pcc_gpu: clippy_gpu clippy_cuda_backend check_compile_tests_benches_gpu diff --git a/scripts/clone_backward_compat_data.sh b/scripts/clone_backward_compat_data.sh index e0bacd54eb..35e458f1fe 100755 --- a/scripts/clone_backward_compat_data.sh +++ b/scripts/clone_backward_compat_data.sh @@ -2,8 +2,9 @@ set -e -if [ $# -lt 2 ]; then - echo "$0 git_url dest_path" +if [ $# -lt 3 ]; then + echo "invalid arguments, usage:\n" + echo "$0 git_url branch dest_path" exit 1 fi @@ -12,8 +13,9 @@ if ! git lfs env 2>/dev/null >/dev/null; then exit 1 fi -if [ -d $2 ]; then - cd $2 && git pull +if [ -d $3 ]; then + cd $3 && git fetch --depth 1 && git reset --hard origin/$2 && git clean -dfx + else - git clone $1 $2 + git clone $1 -b $2 --depth 1 $3 fi diff --git a/tfhe/Cargo.toml b/tfhe/Cargo.toml index cd0ec90d74..ae3dbf09e7 100644 --- a/tfhe/Cargo.toml +++ b/tfhe/Cargo.toml @@ -17,7 +17,7 @@ exclude = [ "/js_on_wasm_tests/", "/web_wasm_parallel_tests/", ] -rust-version = "1.75" +rust-version = "1.76" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html @@ -79,7 +79,7 @@ sha3 = { version = "0.10", optional = true } itertools = "0.11.0" rand_core = { version = "0.6.4", features = ["std"] } tfhe-zk-pok = { version = "0.2.0", path = "../tfhe-zk-pok", optional = true } -tfhe-versionable = { version = "0.1.0", path = "../utils/tfhe-versionable" } +tfhe-versionable = { version = "0.2.0", path = "../utils/tfhe-versionable" } # wasm deps wasm-bindgen = { version = "0.2.86", features = [ @@ -329,4 +329,5 @@ crate-type = ["lib", "staticlib", "cdylib"] unexpected_cfgs = { level = "warn", check-cfg = [ 'cfg(bench)', 'cfg(tarpaulin)', + 'cfg(tfhe_lints)' ] } diff --git a/tfhe/docs/guides/data_versioning.md b/tfhe/docs/guides/data_versioning.md index 4441290f9c..f5f344b559 100644 --- a/tfhe/docs/guides/data_versioning.md +++ b/tfhe/docs/guides/data_versioning.md @@ -17,7 +17,7 @@ You can load serialized data with the `unversionize` function, even in newer ver [dependencies] # ... tfhe = { version = "0.8.0", features = ["integer","x86_64-unix"]} -tfhe-versionable = "0.1.0" +tfhe-versionable = "0.2.0" bincode = "1.3.3" ``` diff --git a/tfhe/src/boolean/backward_compatibility/ciphertext.rs b/tfhe/src/boolean/backward_compatibility/ciphertext.rs new file mode 100644 index 0000000000..7e46bffdb1 --- /dev/null +++ b/tfhe/src/boolean/backward_compatibility/ciphertext.rs @@ -0,0 +1,13 @@ +use tfhe_versionable::VersionsDispatch; + +use crate::boolean::ciphertext::{Ciphertext, CompressedCiphertext}; + +#[derive(VersionsDispatch)] +pub enum CiphertextVersions { + V0(Ciphertext), +} + +#[derive(VersionsDispatch)] +pub enum CompressedCiphertextVersions { + V0(CompressedCiphertext), +} diff --git a/tfhe/src/boolean/backward_compatibility/client_key.rs b/tfhe/src/boolean/backward_compatibility/client_key.rs new file mode 100644 index 0000000000..66e087a8be --- /dev/null +++ b/tfhe/src/boolean/backward_compatibility/client_key.rs @@ -0,0 +1,8 @@ +use tfhe_versionable::VersionsDispatch; + +use crate::boolean::client_key::ClientKey; + +#[derive(VersionsDispatch)] +pub enum ClientKeyVersions { + V0(ClientKey), +} diff --git a/tfhe/src/boolean/backward_compatibility/key_switching_key.rs b/tfhe/src/boolean/backward_compatibility/key_switching_key.rs new file mode 100644 index 0000000000..55c2878a83 --- /dev/null +++ b/tfhe/src/boolean/backward_compatibility/key_switching_key.rs @@ -0,0 +1,8 @@ +use tfhe_versionable::VersionsDispatch; + +use crate::boolean::key_switching_key::KeySwitchingKey; + +#[derive(VersionsDispatch)] +pub enum KeySwitchingKeyVersions { + V0(KeySwitchingKey), +} diff --git a/tfhe/src/boolean/backward_compatibility/mod.rs b/tfhe/src/boolean/backward_compatibility/mod.rs new file mode 100644 index 0000000000..f2d5b9574f --- /dev/null +++ b/tfhe/src/boolean/backward_compatibility/mod.rs @@ -0,0 +1,6 @@ +pub mod ciphertext; +pub mod client_key; +pub mod key_switching_key; +pub mod parameters; +pub mod public_key; +pub mod server_key; diff --git a/tfhe/src/boolean/backward_compatibility/parameters.rs b/tfhe/src/boolean/backward_compatibility/parameters.rs new file mode 100644 index 0000000000..87eb8968b5 --- /dev/null +++ b/tfhe/src/boolean/backward_compatibility/parameters.rs @@ -0,0 +1,13 @@ +use tfhe_versionable::VersionsDispatch; + +use crate::boolean::parameters::{BooleanKeySwitchingParameters, BooleanParameters}; + +#[derive(VersionsDispatch)] +pub enum BooleanParametersVersions { + V0(BooleanParameters), +} + +#[derive(VersionsDispatch)] +pub enum BooleanKeySwitchingParametersVersions { + V0(BooleanKeySwitchingParameters), +} diff --git a/tfhe/src/boolean/backward_compatibility/public_key.rs b/tfhe/src/boolean/backward_compatibility/public_key.rs new file mode 100644 index 0000000000..84ddcc69ba --- /dev/null +++ b/tfhe/src/boolean/backward_compatibility/public_key.rs @@ -0,0 +1,13 @@ +use tfhe_versionable::VersionsDispatch; + +use crate::boolean::public_key::{CompressedPublicKey, PublicKey}; + +#[derive(VersionsDispatch)] +pub enum PublicKeyVersions { + V0(PublicKey), +} + +#[derive(VersionsDispatch)] +pub enum CompressedPublicKeyVersions { + V0(CompressedPublicKey), +} diff --git a/tfhe/src/boolean/backward_compatibility/server_key.rs b/tfhe/src/boolean/backward_compatibility/server_key.rs new file mode 100644 index 0000000000..9a5169c88b --- /dev/null +++ b/tfhe/src/boolean/backward_compatibility/server_key.rs @@ -0,0 +1,13 @@ +use tfhe_versionable::VersionsDispatch; + +use crate::boolean::server_key::{CompressedServerKey, ServerKey}; + +#[derive(VersionsDispatch)] +pub enum ServerKeyVersions { + V0(ServerKey), +} + +#[derive(VersionsDispatch)] +pub enum CompressedServerKeyVersions { + V0(CompressedServerKey), +} diff --git a/tfhe/src/boolean/ciphertext/mod.rs b/tfhe/src/boolean/ciphertext/mod.rs index fdfea30c58..8691e2c1ab 100644 --- a/tfhe/src/boolean/ciphertext/mod.rs +++ b/tfhe/src/boolean/ciphertext/mod.rs @@ -4,11 +4,15 @@ use crate::core_crypto::entities::*; use serde::{Deserialize, Serialize}; +use tfhe_versionable::Versionize; + +use super::backward_compatibility::ciphertext::{CiphertextVersions, CompressedCiphertextVersions}; /// A structure containing a ciphertext, meant to encrypt a Boolean message. /// /// It is used to evaluate a Boolean circuits homomorphically. -#[derive(Clone, Debug, Serialize, Deserialize)] +#[derive(Clone, Debug, Serialize, Deserialize, Versionize)] +#[versionize(CiphertextVersions)] pub enum Ciphertext { Encrypted(LweCiphertextOwned), Trivial(bool), @@ -17,7 +21,8 @@ pub enum Ciphertext { /// A structure containing a compressed ciphertext, meant to encrypt a Boolean message. /// /// It has to be decompressed before evaluating a Boolean circuit. -#[derive(Clone, Debug, Serialize, Deserialize)] +#[derive(Clone, Debug, Serialize, Deserialize, Versionize)] +#[versionize(CompressedCiphertextVersions)] pub struct CompressedCiphertext { pub(crate) ciphertext: SeededLweCiphertext, } diff --git a/tfhe/src/boolean/client_key/mod.rs b/tfhe/src/boolean/client_key/mod.rs index 6e65ac1b0a..46d72e9ef9 100644 --- a/tfhe/src/boolean/client_key/mod.rs +++ b/tfhe/src/boolean/client_key/mod.rs @@ -9,6 +9,9 @@ use crate::boolean::parameters::{BooleanParameters, DynamicDistribution, Encrypt use crate::core_crypto::entities::*; use serde::{Deserialize, Serialize}; use std::fmt::{Debug, Formatter}; +use tfhe_versionable::Versionize; + +use super::backward_compatibility::client_key::ClientKeyVersions; /// A structure containing the client key, which must be kept secret. /// @@ -18,7 +21,8 @@ use std::fmt::{Debug, Formatter}; /// * `glwe_secret_key` - a GLWE secret key, used to generate the bootstrapping keys and key /// switching keys. /// * `parameters` - the cryptographic parameter set. -#[derive(Clone, Serialize, Deserialize)] +#[derive(Clone, Serialize, Deserialize, Versionize)] +#[versionize(ClientKeyVersions)] pub struct ClientKey { pub(crate) lwe_secret_key: LweSecretKeyOwned, pub(crate) glwe_secret_key: GlweSecretKeyOwned, diff --git a/tfhe/src/boolean/engine/bootstrapping.rs b/tfhe/src/boolean/engine/bootstrapping.rs index 7d3ca82c73..b9a63388a3 100644 --- a/tfhe/src/boolean/engine/bootstrapping.rs +++ b/tfhe/src/boolean/engine/bootstrapping.rs @@ -1,3 +1,6 @@ +use crate::boolean::backward_compatibility::server_key::{ + CompressedServerKeyVersions, ServerKeyVersions, +}; use crate::boolean::ciphertext::Ciphertext; use crate::boolean::{ClientKey, PLAINTEXT_TRUE}; use crate::core_crypto::algorithms::*; @@ -8,6 +11,7 @@ use crate::core_crypto::commons::parameters::{CiphertextModulus, PBSOrder}; use crate::core_crypto::entities::*; use crate::core_crypto::fft_impl::fft64::math::fft::Fft; use serde::{Deserialize, Serialize}; +use tfhe_versionable::Versionize; /// Memory used as buffer for the bootstrap /// @@ -89,7 +93,8 @@ impl Memory { /// In more details, it contains: /// * `bootstrapping_key` - a public key, used to perform the bootstrapping operation. /// * `key_switching_key` - a public key, used to perform the key-switching operation. -#[derive(Clone, Serialize, Deserialize)] +#[derive(Clone, Serialize, Deserialize, Versionize)] +#[versionize(ServerKeyVersions)] pub struct ServerKey { pub(crate) bootstrapping_key: FourierLweBootstrapKeyOwned, pub(crate) key_switching_key: LweKeyswitchKeyOwned, @@ -182,7 +187,8 @@ impl ServerKey { /// In more details, it contains: /// * `bootstrapping_key` - a public key, used to perform the bootstrapping operation. /// * `key_switching_key` - a public key, used to perform the key-switching operation. -#[derive(Clone, Serialize, Deserialize)] +#[derive(Clone, Serialize, Deserialize, Versionize)] +#[versionize(CompressedServerKeyVersions)] pub struct CompressedServerKey { pub(crate) bootstrapping_key: SeededLweBootstrapKeyOwned, pub(crate) key_switching_key: SeededLweKeyswitchKeyOwned, diff --git a/tfhe/src/boolean/key_switching_key/mod.rs b/tfhe/src/boolean/key_switching_key/mod.rs index 6ab6f5dd77..13b607424e 100644 --- a/tfhe/src/boolean/key_switching_key/mod.rs +++ b/tfhe/src/boolean/key_switching_key/mod.rs @@ -1,13 +1,18 @@ +use tfhe_versionable::Versionize; + use crate::boolean::engine::{BooleanEngine, WithThreadLocalEngine}; use crate::boolean::parameters::BooleanKeySwitchingParameters; use crate::boolean::prelude::Ciphertext; use crate::boolean::ClientKey; use crate::core_crypto::prelude::{keyswitch_lwe_ciphertext, LweKeyswitchKeyOwned}; +use super::backward_compatibility::key_switching_key::KeySwitchingKeyVersions; + #[cfg(test)] mod test; -#[derive(Clone, Debug, serde::Serialize, serde::Deserialize)] +#[derive(Clone, Debug, serde::Serialize, serde::Deserialize, Versionize)] +#[versionize(KeySwitchingKeyVersions)] pub struct KeySwitchingKey { pub(crate) key_switching_key: LweKeyswitchKeyOwned, } diff --git a/tfhe/src/boolean/mod.rs b/tfhe/src/boolean/mod.rs index dc39d267b5..804386e991 100644 --- a/tfhe/src/boolean/mod.rs +++ b/tfhe/src/boolean/mod.rs @@ -56,6 +56,7 @@ use crate::boolean::server_key::ServerKey; #[cfg(test)] use rand::Rng; +pub mod backward_compatibility; pub mod ciphertext; pub mod client_key; pub mod engine; diff --git a/tfhe/src/boolean/parameters/mod.rs b/tfhe/src/boolean/parameters/mod.rs index 31746bb30b..47f221bad0 100644 --- a/tfhe/src/boolean/parameters/mod.rs +++ b/tfhe/src/boolean/parameters/mod.rs @@ -25,6 +25,11 @@ pub use crate::core_crypto::commons::parameters::{ }; use serde::{Deserialize, Serialize}; +use tfhe_versionable::Versionize; + +use super::backward_compatibility::parameters::{ + BooleanKeySwitchingParametersVersions, BooleanParametersVersions, +}; /// A set of cryptographic parameters for homomorphic Boolean circuit evaluation. /// The choice of encryption key for (`boolean ciphertext`)[`super::ciphertext::Ciphertext`]. @@ -39,7 +44,8 @@ use serde::{Deserialize, Serialize}; /// key`)[`super::public_key::PublicKey`] sizes are much more manageable and should always fit in /// memory. When refreshing a ciphertext and/or evaluating a table lookup the keyswitch is /// computed first followed by a PBS. -#[derive(Copy, Clone, Debug, PartialEq, Serialize, Deserialize)] +#[derive(Copy, Clone, Debug, PartialEq, Serialize, Deserialize, Versionize)] +#[versionize(BooleanParametersVersions)] pub struct BooleanParameters { pub lwe_dimension: LweDimension, pub glwe_dimension: GlweDimension, @@ -91,7 +97,8 @@ impl BooleanParameters { } /// A set of cryptographic parameters for homomorphic Boolean key switching. -#[derive(Copy, Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] +#[derive(Copy, Clone, Debug, PartialEq, Eq, Serialize, Deserialize, Versionize)] +#[versionize(BooleanKeySwitchingParametersVersions)] pub struct BooleanKeySwitchingParameters { pub ks_base_log: DecompositionBaseLog, pub ks_level: DecompositionLevelCount, diff --git a/tfhe/src/boolean/public_key/compressed.rs b/tfhe/src/boolean/public_key/compressed.rs index 712e1be42d..d1914933f7 100644 --- a/tfhe/src/boolean/public_key/compressed.rs +++ b/tfhe/src/boolean/public_key/compressed.rs @@ -1,10 +1,13 @@ +use crate::boolean::backward_compatibility::public_key::CompressedPublicKeyVersions; use crate::boolean::engine::{BooleanEngine, WithThreadLocalEngine}; use crate::boolean::prelude::{BooleanParameters, Ciphertext, ClientKey}; use crate::core_crypto::prelude::SeededLwePublicKeyOwned; use serde::{Deserialize, Serialize}; +use tfhe_versionable::Versionize; /// A structure containing a compressed public key. -#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] +#[derive(Clone, Debug, PartialEq, Serialize, Deserialize, Versionize)] +#[versionize(CompressedPublicKeyVersions)] pub struct CompressedPublicKey { pub(crate) compressed_lwe_public_key: SeededLwePublicKeyOwned, pub parameters: BooleanParameters, diff --git a/tfhe/src/boolean/public_key/standard.rs b/tfhe/src/boolean/public_key/standard.rs index 972368f0bb..743a83411c 100644 --- a/tfhe/src/boolean/public_key/standard.rs +++ b/tfhe/src/boolean/public_key/standard.rs @@ -1,15 +1,18 @@ //! Module with the definition of the encryption PublicKey. use super::compressed::CompressedPublicKey; +use crate::boolean::backward_compatibility::public_key::PublicKeyVersions; use crate::boolean::ciphertext::Ciphertext; use crate::boolean::client_key::ClientKey; use crate::boolean::engine::{BooleanEngine, WithThreadLocalEngine}; use crate::boolean::parameters::BooleanParameters; use crate::core_crypto::entities::*; use serde::{Deserialize, Serialize}; +use tfhe_versionable::Versionize; /// A structure containing a public key. -#[derive(Clone, Debug, Serialize, Deserialize)] +#[derive(Clone, Debug, Serialize, Deserialize, Versionize)] +#[versionize(PublicKeyVersions)] pub struct PublicKey { pub(crate) lwe_public_key: LwePublicKeyOwned, pub(crate) parameters: BooleanParameters, diff --git a/tfhe/src/core_crypto/backward_compatibility/commons/dispersion.rs b/tfhe/src/core_crypto/backward_compatibility/commons/dispersion.rs new file mode 100644 index 0000000000..6982f8dee1 --- /dev/null +++ b/tfhe/src/core_crypto/backward_compatibility/commons/dispersion.rs @@ -0,0 +1,7 @@ +use crate::core_crypto::commons::dispersion::StandardDev; +use tfhe_versionable::VersionsDispatch; + +#[derive(VersionsDispatch)] +pub enum StandardDevVersions { + V0(StandardDev), +} diff --git a/tfhe/src/core_crypto/backward_compatibility/commons/math/random/mod.rs b/tfhe/src/core_crypto/backward_compatibility/commons/math/random/mod.rs index 37a13ebd59..162ff5fc8e 100644 --- a/tfhe/src/core_crypto/backward_compatibility/commons/math/random/mod.rs +++ b/tfhe/src/core_crypto/backward_compatibility/commons/math/random/mod.rs @@ -19,6 +19,7 @@ pub enum DynamicDistributionVersions { } #[derive(Serialize)] +#[cfg_attr(tfhe_lints, allow(tfhe_lints::serialize_without_versionize))] pub enum CompressionSeedVersioned<'vers> { V0(&'vers CompressionSeed), } @@ -30,6 +31,7 @@ impl<'vers> From<&'vers CompressionSeed> for CompressionSeedVersioned<'vers> { } #[derive(Serialize, Deserialize)] +#[cfg_attr(tfhe_lints, allow(tfhe_lints::serialize_without_versionize))] pub enum CompressionSeedVersionedOwned { V0(CompressionSeed), } diff --git a/tfhe/src/core_crypto/backward_compatibility/commons/mod.rs b/tfhe/src/core_crypto/backward_compatibility/commons/mod.rs index eb9124218b..d61dc2f223 100644 --- a/tfhe/src/core_crypto/backward_compatibility/commons/mod.rs +++ b/tfhe/src/core_crypto/backward_compatibility/commons/mod.rs @@ -1,3 +1,4 @@ pub mod ciphertext_modulus; +pub mod dispersion; pub mod math; pub mod parameters; diff --git a/tfhe/src/core_crypto/backward_compatibility/entities/cleartext.rs b/tfhe/src/core_crypto/backward_compatibility/entities/cleartext.rs new file mode 100644 index 0000000000..771d38e295 --- /dev/null +++ b/tfhe/src/core_crypto/backward_compatibility/entities/cleartext.rs @@ -0,0 +1,8 @@ +use tfhe_versionable::VersionsDispatch; + +use crate::core_crypto::prelude::{Cleartext, Numeric}; + +#[derive(VersionsDispatch)] +pub enum CleartextVersions { + V0(Cleartext), +} diff --git a/tfhe/src/core_crypto/backward_compatibility/entities/compressed_modulus_switched_glwe_ciphertext.rs b/tfhe/src/core_crypto/backward_compatibility/entities/compressed_modulus_switched_glwe_ciphertext.rs new file mode 100644 index 0000000000..2488f0e200 --- /dev/null +++ b/tfhe/src/core_crypto/backward_compatibility/entities/compressed_modulus_switched_glwe_ciphertext.rs @@ -0,0 +1,9 @@ +use tfhe_versionable::VersionsDispatch; + +use crate::core_crypto::prelude::compressed_modulus_switched_glwe_ciphertext::CompressedModulusSwitchedGlweCiphertext; +use crate::core_crypto::prelude::UnsignedInteger; + +#[derive(VersionsDispatch)] +pub enum CompressedModulusSwitchedGlweCiphertextVersions { + V0(CompressedModulusSwitchedGlweCiphertext), +} diff --git a/tfhe/src/core_crypto/backward_compatibility/entities/compressed_modulus_switched_lwe_ciphertext.rs b/tfhe/src/core_crypto/backward_compatibility/entities/compressed_modulus_switched_lwe_ciphertext.rs index e4a60be451..0dc3b41697 100644 --- a/tfhe/src/core_crypto/backward_compatibility/entities/compressed_modulus_switched_lwe_ciphertext.rs +++ b/tfhe/src/core_crypto/backward_compatibility/entities/compressed_modulus_switched_lwe_ciphertext.rs @@ -1,3 +1,5 @@ +use std::convert::Infallible; + use tfhe_versionable::{Upgrade, Version, VersionsDispatch}; use crate::core_crypto::prelude::compressed_modulus_switched_lwe_ciphertext::CompressedModulusSwitchedLweCiphertext; @@ -17,7 +19,9 @@ pub struct CompressedModulusSwitchedLweCiphertextV0 { impl Upgrade> for CompressedModulusSwitchedLweCiphertextV0 { - fn upgrade(self) -> Result, String> { + type Error = Infallible; + + fn upgrade(self) -> Result, Self::Error> { let packed_integers = PackedIntegers { packed_coeffs: self.packed_coeffs, log_modulus: self.log_modulus, diff --git a/tfhe/src/core_crypto/backward_compatibility/entities/gsw_ciphertext.rs b/tfhe/src/core_crypto/backward_compatibility/entities/gsw_ciphertext.rs new file mode 100644 index 0000000000..f5f20c2819 --- /dev/null +++ b/tfhe/src/core_crypto/backward_compatibility/entities/gsw_ciphertext.rs @@ -0,0 +1,8 @@ +use tfhe_versionable::VersionsDispatch; + +use crate::core_crypto::prelude::{Container, GswCiphertext}; + +#[derive(VersionsDispatch)] +pub enum GswCiphertextVersions { + V0(GswCiphertext), +} diff --git a/tfhe/src/core_crypto/backward_compatibility/entities/lwe_multi_bit_bootstrap_key.rs b/tfhe/src/core_crypto/backward_compatibility/entities/lwe_multi_bit_bootstrap_key.rs index ce6dd0ab0a..f6932dab78 100644 --- a/tfhe/src/core_crypto/backward_compatibility/entities/lwe_multi_bit_bootstrap_key.rs +++ b/tfhe/src/core_crypto/backward_compatibility/entities/lwe_multi_bit_bootstrap_key.rs @@ -17,6 +17,7 @@ where } #[derive(Serialize)] +#[cfg_attr(tfhe_lints, allow(tfhe_lints::serialize_without_versionize))] pub enum FourierLweMultiBitBootstrapKeyVersioned<'vers> { V0(FourierLweMultiBitBootstrapKeyVersion<'vers>), } @@ -30,14 +31,15 @@ impl<'vers, C: Container> From<&'vers FourierLweMultiBitBootstrap } #[derive(Serialize, Deserialize)] +#[cfg_attr(tfhe_lints, allow(tfhe_lints::serialize_without_versionize))] pub enum FourierLweMultiBitBootstrapKeyVersionedOwned { V0(FourierLweMultiBitBootstrapKeyVersionOwned), } -impl> From<&FourierLweMultiBitBootstrapKey> +impl> From> for FourierLweMultiBitBootstrapKeyVersionedOwned { - fn from(value: &FourierLweMultiBitBootstrapKey) -> Self { + fn from(value: FourierLweMultiBitBootstrapKey) -> Self { Self::V0(value.into()) } } diff --git a/tfhe/src/core_crypto/backward_compatibility/entities/mod.rs b/tfhe/src/core_crypto/backward_compatibility/entities/mod.rs index 107e1d0c74..25ceb37029 100644 --- a/tfhe/src/core_crypto/backward_compatibility/entities/mod.rs +++ b/tfhe/src/core_crypto/backward_compatibility/entities/mod.rs @@ -1,3 +1,5 @@ +pub mod cleartext; +pub mod compressed_modulus_switched_glwe_ciphertext; pub mod compressed_modulus_switched_lwe_ciphertext; pub mod compressed_modulus_switched_multi_bit_lwe_ciphertext; pub mod ggsw_ciphertext; @@ -5,6 +7,7 @@ pub mod ggsw_ciphertext_list; pub mod glwe_ciphertext; pub mod glwe_ciphertext_list; pub mod glwe_secret_key; +pub mod gsw_ciphertext; pub mod lwe_bootstrap_key; pub mod lwe_ciphertext; pub mod lwe_ciphertext_list; @@ -17,7 +20,14 @@ pub mod lwe_private_functional_packing_keyswitch_key; pub mod lwe_private_functional_packing_keyswitch_key_list; pub mod lwe_public_key; pub mod lwe_secret_key; +pub mod ntt_ggsw_ciphertext; +pub mod ntt_ggsw_ciphertext_list; +pub mod ntt_lwe_bootstrap_key; pub mod packed_integers; +pub mod plaintext; +pub mod plaintext_list; +pub mod polynomial; +pub mod polynomial_list; pub mod seeded_ggsw_ciphertext; pub mod seeded_ggsw_ciphertext_list; pub mod seeded_glwe_ciphertext; diff --git a/tfhe/src/core_crypto/backward_compatibility/entities/ntt_ggsw_ciphertext.rs b/tfhe/src/core_crypto/backward_compatibility/entities/ntt_ggsw_ciphertext.rs new file mode 100644 index 0000000000..7f914152f3 --- /dev/null +++ b/tfhe/src/core_crypto/backward_compatibility/entities/ntt_ggsw_ciphertext.rs @@ -0,0 +1,11 @@ +use tfhe_versionable::VersionsDispatch; + +use crate::core_crypto::prelude::{Container, NttGgswCiphertext, UnsignedInteger}; + +#[derive(VersionsDispatch)] +pub enum NttGgswCiphertextVersions +where + C::Element: UnsignedInteger, +{ + V0(NttGgswCiphertext), +} diff --git a/tfhe/src/core_crypto/backward_compatibility/entities/ntt_ggsw_ciphertext_list.rs b/tfhe/src/core_crypto/backward_compatibility/entities/ntt_ggsw_ciphertext_list.rs new file mode 100644 index 0000000000..566fe1cb13 --- /dev/null +++ b/tfhe/src/core_crypto/backward_compatibility/entities/ntt_ggsw_ciphertext_list.rs @@ -0,0 +1,11 @@ +use tfhe_versionable::VersionsDispatch; + +use crate::core_crypto::prelude::{Container, NttGgswCiphertextList, UnsignedInteger}; + +#[derive(VersionsDispatch)] +pub enum NttGgswCiphertextListVersions +where + C::Element: UnsignedInteger, +{ + V0(NttGgswCiphertextList), +} diff --git a/tfhe/src/core_crypto/backward_compatibility/entities/ntt_lwe_bootstrap_key.rs b/tfhe/src/core_crypto/backward_compatibility/entities/ntt_lwe_bootstrap_key.rs new file mode 100644 index 0000000000..00298afaf3 --- /dev/null +++ b/tfhe/src/core_crypto/backward_compatibility/entities/ntt_lwe_bootstrap_key.rs @@ -0,0 +1,11 @@ +use tfhe_versionable::VersionsDispatch; + +use crate::core_crypto::prelude::{Container, NttLweBootstrapKey, UnsignedInteger}; + +#[derive(VersionsDispatch)] +pub enum NttLweBootstrapKeyVersions +where + C::Element: UnsignedInteger, +{ + V0(NttLweBootstrapKey), +} diff --git a/tfhe/src/core_crypto/backward_compatibility/entities/plaintext.rs b/tfhe/src/core_crypto/backward_compatibility/entities/plaintext.rs new file mode 100644 index 0000000000..3bf0892e83 --- /dev/null +++ b/tfhe/src/core_crypto/backward_compatibility/entities/plaintext.rs @@ -0,0 +1,8 @@ +use tfhe_versionable::VersionsDispatch; + +use crate::core_crypto::prelude::{Numeric, Plaintext}; + +#[derive(VersionsDispatch)] +pub enum PlaintextVersions { + V0(Plaintext), +} diff --git a/tfhe/src/core_crypto/backward_compatibility/entities/plaintext_list.rs b/tfhe/src/core_crypto/backward_compatibility/entities/plaintext_list.rs new file mode 100644 index 0000000000..f28772622d --- /dev/null +++ b/tfhe/src/core_crypto/backward_compatibility/entities/plaintext_list.rs @@ -0,0 +1,8 @@ +use tfhe_versionable::VersionsDispatch; + +use crate::core_crypto::prelude::{Container, PlaintextList}; + +#[derive(VersionsDispatch)] +pub enum PlaintextListVersions { + V0(PlaintextList), +} diff --git a/tfhe/src/core_crypto/backward_compatibility/entities/polynomial.rs b/tfhe/src/core_crypto/backward_compatibility/entities/polynomial.rs new file mode 100644 index 0000000000..bc485752e7 --- /dev/null +++ b/tfhe/src/core_crypto/backward_compatibility/entities/polynomial.rs @@ -0,0 +1,8 @@ +use tfhe_versionable::VersionsDispatch; + +use crate::core_crypto::prelude::{Container, Polynomial}; + +#[derive(VersionsDispatch)] +pub enum PolynomialVersions { + V0(Polynomial), +} diff --git a/tfhe/src/core_crypto/backward_compatibility/entities/polynomial_list.rs b/tfhe/src/core_crypto/backward_compatibility/entities/polynomial_list.rs new file mode 100644 index 0000000000..e0e0a741f5 --- /dev/null +++ b/tfhe/src/core_crypto/backward_compatibility/entities/polynomial_list.rs @@ -0,0 +1,8 @@ +use tfhe_versionable::VersionsDispatch; + +use crate::core_crypto::prelude::{Container, PolynomialList}; + +#[derive(VersionsDispatch)] +pub enum PolynomialListVersions { + V0(PolynomialList), +} diff --git a/tfhe/src/core_crypto/backward_compatibility/fft_impl/mod.rs b/tfhe/src/core_crypto/backward_compatibility/fft_impl/mod.rs index f3a95efbdf..87fb59dd87 100644 --- a/tfhe/src/core_crypto/backward_compatibility/fft_impl/mod.rs +++ b/tfhe/src/core_crypto/backward_compatibility/fft_impl/mod.rs @@ -1,4 +1,4 @@ -use tfhe_versionable::UnversionizeError; +use tfhe_versionable::{UnversionizeError, VersionsDispatch}; use aligned_vec::ABox; use concrete_fft::c64; @@ -7,10 +7,17 @@ use serde::{Deserialize, Serialize}; use crate::core_crypto::fft_impl::fft64::crypto::bootstrap::{ FourierLweBootstrapKeyVersion, FourierLweBootstrapKeyVersionOwned, }; +use crate::core_crypto::fft_impl::fft64::crypto::ggsw::{ + FourierGgswCiphertextVersion, FourierGgswCiphertextVersionOwned, +}; use crate::core_crypto::fft_impl::fft64::math::fft::FourierPolynomialList; -use crate::core_crypto::prelude::{Container, FourierLweBootstrapKey, IntoContainerOwned}; +use crate::core_crypto::prelude::{ + Container, Fourier128GgswCiphertext, Fourier128LweBootstrapKey, FourierGgswCiphertext, + FourierLweBootstrapKey, IntoContainerOwned, +}; #[derive(Serialize)] +#[cfg_attr(tfhe_lints, allow(tfhe_lints::serialize_without_versionize))] pub enum FourierPolynomialListVersioned<'vers> { V0(FourierPolynomialList<&'vers [c64]>), } @@ -29,14 +36,15 @@ impl<'vers, C: Container> From<&'vers FourierPolynomialList> // Here we do not derive "VersionsDispatch" so that we can implement a non recursive Versionize #[derive(Serialize, Deserialize)] +#[cfg_attr(tfhe_lints, allow(tfhe_lints::serialize_without_versionize))] pub enum FourierPolynomialListVersionedOwned { V0(FourierPolynomialList>), } -impl> From<&FourierPolynomialList> +impl> From> for FourierPolynomialListVersionedOwned { - fn from(value: &FourierPolynomialList) -> Self { + fn from(value: FourierPolynomialList) -> Self { let owned_poly = FourierPolynomialList { data: ABox::collect(value.data.as_ref().iter().copied()), polynomial_size: value.polynomial_size, @@ -59,6 +67,7 @@ impl> From { V0(FourierLweBootstrapKeyVersion<'vers>), } @@ -72,14 +81,15 @@ impl<'vers, C: Container> From<&'vers FourierLweBootstrapKey> } #[derive(Serialize, Deserialize)] +#[cfg_attr(tfhe_lints, allow(tfhe_lints::serialize_without_versionize))] pub enum FourierLweBootstrapKeyVersionedOwned { V0(FourierLweBootstrapKeyVersionOwned), } -impl> From<&FourierLweBootstrapKey> +impl> From> for FourierLweBootstrapKeyVersionedOwned { - fn from(value: &FourierLweBootstrapKey) -> Self { + fn from(value: FourierLweBootstrapKey) -> Self { Self::V0(value.into()) } } @@ -95,3 +105,53 @@ impl> TryFrom { + V0(FourierGgswCiphertextVersion<'vers>), +} + +impl<'vers, C: Container> From<&'vers FourierGgswCiphertext> + for FourierGgswCiphertextVersioned<'vers> +{ + fn from(value: &'vers FourierGgswCiphertext) -> Self { + Self::V0(value.into()) + } +} + +#[derive(Serialize, Deserialize)] +#[cfg_attr(tfhe_lints, allow(tfhe_lints::serialize_without_versionize))] +pub enum FourierGgswCiphertextVersionedOwned { + V0(FourierGgswCiphertextVersionOwned), +} + +impl> From> + for FourierGgswCiphertextVersionedOwned +{ + fn from(value: FourierGgswCiphertext) -> Self { + Self::V0(value.into()) + } +} + +impl> TryFrom + for FourierGgswCiphertext +{ + type Error = UnversionizeError; + + fn try_from(value: FourierGgswCiphertextVersionedOwned) -> Result { + match value { + FourierGgswCiphertextVersionedOwned::V0(v0) => Self::try_from(v0), + } + } +} + +#[derive(VersionsDispatch)] +pub enum Fourier128LweBootstrapKeyVersions> { + V0(Fourier128LweBootstrapKey), +} + +#[derive(VersionsDispatch)] +pub enum Fourier128GgswCiphertextVersions> { + V0(Fourier128GgswCiphertext), +} diff --git a/tfhe/src/core_crypto/commons/ciphertext_modulus.rs b/tfhe/src/core_crypto/commons/ciphertext_modulus.rs index 26e87a39aa..b9406d4b03 100644 --- a/tfhe/src/core_crypto/commons/ciphertext_modulus.rs +++ b/tfhe/src/core_crypto/commons/ciphertext_modulus.rs @@ -7,6 +7,7 @@ use crate::core_crypto::commons::traits::UnsignedInteger; use crate::core_crypto::prelude::CastInto; use core::num::NonZeroU128; use std::cmp::Ordering; +use std::fmt::Display; use std::marker::PhantomData; #[derive(Clone, Copy, PartialEq, Eq)] @@ -58,6 +59,31 @@ pub struct SerializableCiphertextModulus { pub scalar_bits: usize, } +#[derive(Clone, Copy, Debug)] +pub enum CiphertextModulusDeserializationError { + InvalidBitWidth { expected: usize, found: usize }, + ZeroCustomModulus, +} + +impl Display for CiphertextModulusDeserializationError { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + Self::InvalidBitWidth { expected, found } => write!( + f, + "Expected an unsigned integer with {expected} bits, \ + found {found} bits during deserialization of CiphertextModulus, \ + have you mixed types during deserialization?", + ), + Self::ZeroCustomModulus => write!( + f, + "Got zero modulus for CiphertextModulusInner::Custom variant" + ), + } + } +} + +impl std::error::Error for CiphertextModulusDeserializationError {} + impl From> for SerializableCiphertextModulus { fn from(value: CiphertextModulus) -> Self { let modulus = match value.inner { @@ -73,17 +99,14 @@ impl From> for SerializableCi } impl TryFrom for CiphertextModulus { - type Error = String; + type Error = CiphertextModulusDeserializationError; fn try_from(value: SerializableCiphertextModulus) -> Result { if value.scalar_bits != Scalar::BITS { - return Err(format!( - "Expected an unsigned integer with {} bits, \ - found {} bits during deserialization of CiphertextModulus, \ - have you mixed types during deserialization?", - Scalar::BITS, - value.scalar_bits - )); + return Err(CiphertextModulusDeserializationError::InvalidBitWidth { + expected: Scalar::BITS, + found: value.scalar_bits, + }); } let res = if value.modulus == 0 { @@ -93,9 +116,10 @@ impl TryFrom for Ciphert } } else { Self { - inner: CiphertextModulusInner::Custom(NonZeroU128::new(value.modulus).ok_or_else( - || "Got zero modulus for CiphertextModulusInner::Custom variant".to_string(), - )?), + inner: CiphertextModulusInner::Custom( + NonZeroU128::new(value.modulus) + .ok_or(CiphertextModulusDeserializationError::ZeroCustomModulus)?, + ), _scalar: PhantomData, } }; diff --git a/tfhe/src/core_crypto/commons/dispersion.rs b/tfhe/src/core_crypto/commons/dispersion.rs index 5ff7bbd3bb..339e10545c 100644 --- a/tfhe/src/core_crypto/commons/dispersion.rs +++ b/tfhe/src/core_crypto/commons/dispersion.rs @@ -14,6 +14,9 @@ //! defined. use serde::{Deserialize, Serialize}; +use tfhe_versionable::Versionize; + +use crate::core_crypto::backward_compatibility::commons::dispersion::StandardDevVersions; /// A trait for types representing distribution parameters, for a given unsigned integer type. // Warning: @@ -110,7 +113,8 @@ impl DispersionParameter for LogStandardDev { /// 2_f64.powf(32. - 25.).powi(2) /// ); /// ``` -#[derive(Debug, Copy, Clone, PartialEq, PartialOrd, Serialize, Deserialize)] +#[derive(Debug, Copy, Clone, PartialEq, PartialOrd, Serialize, Deserialize, Versionize)] +#[versionize(StandardDevVersions)] pub struct StandardDev(pub f64); impl StandardDev { diff --git a/tfhe/src/core_crypto/commons/math/random/generator.rs b/tfhe/src/core_crypto/commons/math/random/generator.rs index d714cca01d..e75e71b920 100644 --- a/tfhe/src/core_crypto/commons/math/random/generator.rs +++ b/tfhe/src/core_crypto/commons/math/random/generator.rs @@ -31,7 +31,7 @@ pub mod serialization_proxy { } pub(crate) use serialization_proxy::*; -use tfhe_versionable::{Unversionize, Versionize}; +use tfhe_versionable::{Unversionize, Versionize, VersionizeOwned}; #[derive(PartialEq, Eq, Debug, Clone, Copy, Serialize, Deserialize)] /// New type to manage seeds used for compressed/seeded types. @@ -46,11 +46,13 @@ impl Versionize for CompressionSeed { fn versionize(&self) -> Self::Versioned<'_> { self.into() } +} +impl VersionizeOwned for CompressionSeed { type VersionedOwned = CompressionSeedVersionedOwned; - fn versionize_owned(&self) -> Self::VersionedOwned { - (*self).into() + fn versionize_owned(self) -> Self::VersionedOwned { + self.into() } } diff --git a/tfhe/src/core_crypto/entities/cleartext.rs b/tfhe/src/core_crypto/entities/cleartext.rs index d8fe0ba033..a91953222d 100644 --- a/tfhe/src/core_crypto/entities/cleartext.rs +++ b/tfhe/src/core_crypto/entities/cleartext.rs @@ -1,9 +1,13 @@ //! Module containing the definition of the Cleartext. +use tfhe_versionable::Versionize; + +use crate::core_crypto::backward_compatibility::entities::cleartext::CleartextVersions; use crate::core_crypto::commons::traits::*; /// A cleartext, not encoded, value. -#[derive(Debug, Copy, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)] +#[derive(Debug, Copy, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize, Versionize)] +#[versionize(CleartextVersions)] pub struct Cleartext(pub T); /// An immutable reference to a cleartext value. /// diff --git a/tfhe/src/core_crypto/entities/compressed_modulus_switched_glwe_ciphertext.rs b/tfhe/src/core_crypto/entities/compressed_modulus_switched_glwe_ciphertext.rs index 2f6d0569e7..d04f6a262e 100644 --- a/tfhe/src/core_crypto/entities/compressed_modulus_switched_glwe_ciphertext.rs +++ b/tfhe/src/core_crypto/entities/compressed_modulus_switched_glwe_ciphertext.rs @@ -1,5 +1,8 @@ +use tfhe_versionable::Versionize; + use self::packed_integers::PackedIntegers; use crate::conformance::ParameterSetConformant; +use crate::core_crypto::backward_compatibility::entities::compressed_modulus_switched_glwe_ciphertext::CompressedModulusSwitchedGlweCiphertextVersions; use crate::core_crypto::fft_impl::common::modulus_switch; use crate::core_crypto::prelude::*; @@ -74,7 +77,8 @@ use crate::core_crypto::prelude::*; /// ); /// } /// ``` -#[derive(Clone, serde::Serialize, serde::Deserialize)] +#[derive(Clone, serde::Serialize, serde::Deserialize, Versionize)] +#[versionize(CompressedModulusSwitchedGlweCiphertextVersions)] pub struct CompressedModulusSwitchedGlweCiphertext { packed_integers: PackedIntegers, glwe_dimension: GlweDimension, diff --git a/tfhe/src/core_crypto/entities/gsw_ciphertext.rs b/tfhe/src/core_crypto/entities/gsw_ciphertext.rs index 141847c4ad..201e0a7823 100644 --- a/tfhe/src/core_crypto/entities/gsw_ciphertext.rs +++ b/tfhe/src/core_crypto/entities/gsw_ciphertext.rs @@ -1,5 +1,8 @@ //! Module containing the definition of the GswCiphertext. +use tfhe_versionable::Versionize; + +use crate::core_crypto::backward_compatibility::entities::gsw_ciphertext::GswCiphertextVersions; use crate::core_crypto::commons::parameters::*; use crate::core_crypto::commons::traits::*; @@ -149,7 +152,8 @@ use crate::core_crypto::commons::traits::*; /// Simply use the /// [`LWE decryption algorithm`](`crate::core_crypto::algorithms::decrypt_lwe_ciphertext`) /// on one of the LWE ciphertexts contained in the Lev ciphertext. -#[derive(Clone, Debug, PartialEq, Eq, serde::Serialize, serde::Deserialize)] +#[derive(Clone, Debug, PartialEq, Eq, serde::Serialize, serde::Deserialize, Versionize)] +#[versionize(GswCiphertextVersions)] pub struct GswCiphertext { data: C, lwe_size: LweSize, diff --git a/tfhe/src/core_crypto/entities/lwe_multi_bit_bootstrap_key.rs b/tfhe/src/core_crypto/entities/lwe_multi_bit_bootstrap_key.rs index 26439ffc5b..2d4ffda43f 100644 --- a/tfhe/src/core_crypto/entities/lwe_multi_bit_bootstrap_key.rs +++ b/tfhe/src/core_crypto/entities/lwe_multi_bit_bootstrap_key.rs @@ -15,7 +15,7 @@ use crate::core_crypto::entities::*; use crate::core_crypto::fft_impl::fft64::math::fft::FourierPolynomialList; use aligned_vec::{avec, ABox}; use concrete_fft::c64; -use tfhe_versionable::{Unversionize, UnversionizeError, Versionize}; +use tfhe_versionable::{Unversionize, UnversionizeError, Versionize, VersionizeOwned}; #[derive(Clone, Debug, PartialEq, Eq, serde::Serialize, serde::Deserialize, Versionize)] #[versionize(LweMultiBitBootstrapKeyVersions)] @@ -403,6 +403,7 @@ pub struct FourierLweMultiBitBootstrapKey> { } #[derive(serde::Serialize)] +#[cfg_attr(tfhe_lints, allow(tfhe_lints::serialize_without_versionize))] pub struct FourierLweMultiBitBootstrapKeyVersion<'vers> { fourier: FourierPolynomialListVersioned<'vers>, input_lwe_dimension: ::Versioned<'vers>, @@ -413,13 +414,14 @@ pub struct FourierLweMultiBitBootstrapKeyVersion<'vers> { } #[derive(serde::Serialize, serde::Deserialize)] +#[cfg_attr(tfhe_lints, allow(tfhe_lints::serialize_without_versionize))] pub struct FourierLweMultiBitBootstrapKeyVersionOwned { fourier: FourierPolynomialListVersionedOwned, - input_lwe_dimension: ::VersionedOwned, - glwe_size: ::VersionedOwned, - decomposition_base_log: ::VersionedOwned, - decomposition_level_count: ::VersionedOwned, - grouping_factor: ::VersionedOwned, + input_lwe_dimension: ::VersionedOwned, + glwe_size: ::VersionedOwned, + decomposition_base_log: ::VersionedOwned, + decomposition_level_count: ::VersionedOwned, + grouping_factor: ::VersionedOwned, } impl<'vers, C: Container> From<&'vers FourierLweMultiBitBootstrapKey> @@ -437,10 +439,10 @@ impl<'vers, C: Container> From<&'vers FourierLweMultiBitBootstrap } } -impl> From<&FourierLweMultiBitBootstrapKey> +impl> From> for FourierLweMultiBitBootstrapKeyVersionOwned { - fn from(value: &FourierLweMultiBitBootstrapKey) -> Self { + fn from(value: FourierLweMultiBitBootstrapKey) -> Self { Self { fourier: value.fourier.versionize_owned(), input_lwe_dimension: value.input_lwe_dimension.versionize_owned(), @@ -478,10 +480,12 @@ impl> Versionize for FourierLweMultiBitBootstrapKey< fn versionize(&self) -> Self::Versioned<'_> { self.into() } +} +impl> VersionizeOwned for FourierLweMultiBitBootstrapKey { type VersionedOwned = FourierLweMultiBitBootstrapKeyVersionedOwned; - fn versionize_owned(&self) -> Self::VersionedOwned { + fn versionize_owned(self) -> Self::VersionedOwned { self.into() } } diff --git a/tfhe/src/core_crypto/entities/ntt_ggsw_ciphertext.rs b/tfhe/src/core_crypto/entities/ntt_ggsw_ciphertext.rs index d0318cb70f..f7edf035f5 100644 --- a/tfhe/src/core_crypto/entities/ntt_ggsw_ciphertext.rs +++ b/tfhe/src/core_crypto/entities/ntt_ggsw_ciphertext.rs @@ -1,3 +1,4 @@ +use crate::core_crypto::backward_compatibility::entities::ntt_ggsw_ciphertext::NttGgswCiphertextVersions; use crate::core_crypto::commons::math::decomposition::DecompositionLevel; use crate::core_crypto::commons::numeric::UnsignedInteger; use crate::core_crypto::commons::parameters::{ @@ -9,12 +10,14 @@ pub use crate::core_crypto::entities::ggsw_ciphertext::{ }; pub use crate::core_crypto::entities::glwe_ciphertext::glwe_ciphertext_size; use aligned_vec::{avec, ABox}; +use tfhe_versionable::Versionize; /// A [`GGSW ciphertext in the Ntt domain`](`crate::core_crypto::entities::GgswCiphertext`). /// /// See [`the formal definition of a GGSW /// ciphertext`](`crate::core_crypto::entities::GgswCiphertext#formal-definition`) -#[derive(Clone, Copy, Debug, PartialEq, Eq, serde::Serialize, serde::Deserialize)] +#[derive(Clone, Copy, Debug, PartialEq, Eq, serde::Serialize, serde::Deserialize, Versionize)] +#[versionize(NttGgswCiphertextVersions)] pub struct NttGgswCiphertext where C::Element: UnsignedInteger, diff --git a/tfhe/src/core_crypto/entities/ntt_ggsw_ciphertext_list.rs b/tfhe/src/core_crypto/entities/ntt_ggsw_ciphertext_list.rs index 31fb789b13..84d2e961ac 100644 --- a/tfhe/src/core_crypto/entities/ntt_ggsw_ciphertext_list.rs +++ b/tfhe/src/core_crypto/entities/ntt_ggsw_ciphertext_list.rs @@ -1,3 +1,4 @@ +use crate::core_crypto::backward_compatibility::entities::ntt_ggsw_ciphertext_list::NttGgswCiphertextListVersions; use crate::core_crypto::commons::numeric::UnsignedInteger; use crate::core_crypto::commons::parameters::{ CiphertextModulus, DecompositionBaseLog, DecompositionLevelCount, GgswCiphertextCount, @@ -11,10 +12,12 @@ use crate::core_crypto::entities::polynomial_list::{ PolynomialList, PolynomialListMutView, PolynomialListView, }; use aligned_vec::{avec, ABox}; +use tfhe_versionable::Versionize; /// A contiguous list containing /// [`GGSW ciphertexts in the NTT domain`](`crate::core_crypto::entities::NttGgswCiphertext`). -#[derive(Clone, Copy, Debug, PartialEq, Eq, serde::Serialize, serde::Deserialize)] +#[derive(Clone, Copy, Debug, PartialEq, Eq, serde::Serialize, serde::Deserialize, Versionize)] +#[versionize(NttGgswCiphertextListVersions)] pub struct NttGgswCiphertextList where C::Element: UnsignedInteger, diff --git a/tfhe/src/core_crypto/entities/ntt_lwe_bootstrap_key.rs b/tfhe/src/core_crypto/entities/ntt_lwe_bootstrap_key.rs index f68f7cca09..7ad3a00949 100644 --- a/tfhe/src/core_crypto/entities/ntt_lwe_bootstrap_key.rs +++ b/tfhe/src/core_crypto/entities/ntt_lwe_bootstrap_key.rs @@ -1,3 +1,4 @@ +use crate::core_crypto::backward_compatibility::entities::ntt_lwe_bootstrap_key::NttLweBootstrapKeyVersions; use crate::core_crypto::commons::numeric::UnsignedInteger; use crate::core_crypto::commons::parameters::{ CiphertextModulus, DecompositionBaseLog, DecompositionLevelCount, GgswCiphertextCount, @@ -9,8 +10,10 @@ use crate::core_crypto::entities::ntt_ggsw_ciphertext::NttGgswCiphertext; use crate::core_crypto::entities::ntt_ggsw_ciphertext_list::NttGgswCiphertextList; use crate::core_crypto::entities::polynomial_list::{PolynomialListMutView, PolynomialListView}; use aligned_vec::ABox; +use tfhe_versionable::Versionize; -#[derive(Clone, Copy, Debug, PartialEq, Eq, serde::Serialize, serde::Deserialize)] +#[derive(Clone, Copy, Debug, PartialEq, Eq, serde::Serialize, serde::Deserialize, Versionize)] +#[versionize(NttLweBootstrapKeyVersions)] pub struct NttLweBootstrapKey where C::Element: UnsignedInteger, diff --git a/tfhe/src/core_crypto/entities/plaintext.rs b/tfhe/src/core_crypto/entities/plaintext.rs index 4528706a77..dcc0c77c62 100644 --- a/tfhe/src/core_crypto/entities/plaintext.rs +++ b/tfhe/src/core_crypto/entities/plaintext.rs @@ -1,9 +1,13 @@ //! Module containing the definition of the Plaintext. +use tfhe_versionable::Versionize; + +use crate::core_crypto::backward_compatibility::entities::plaintext::PlaintextVersions; use crate::core_crypto::commons::traits::*; /// A plaintext (encoded) value. -#[derive(Debug, Copy, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)] +#[derive(Debug, Copy, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize, Versionize)] +#[versionize(PlaintextVersions)] pub struct Plaintext(pub T); /// An immutable reference to a plaintext (encoded) value. /// diff --git a/tfhe/src/core_crypto/entities/plaintext_list.rs b/tfhe/src/core_crypto/entities/plaintext_list.rs index 2a15d76bab..e3b631fefb 100644 --- a/tfhe/src/core_crypto/entities/plaintext_list.rs +++ b/tfhe/src/core_crypto/entities/plaintext_list.rs @@ -1,11 +1,15 @@ //! Module containing the definition of the PlaintextList. +use tfhe_versionable::Versionize; + +use crate::core_crypto::backward_compatibility::entities::plaintext_list::PlaintextListVersions; use crate::core_crypto::commons::parameters::*; use crate::core_crypto::commons::traits::*; use crate::core_crypto::entities::*; /// A contiguous list containing [`plaintexts`](`crate::core_crypto::entities::Plaintext`). -#[derive(Clone, Debug, PartialEq, Eq, serde::Serialize, serde::Deserialize)] +#[derive(Clone, Debug, PartialEq, Eq, serde::Serialize, serde::Deserialize, Versionize)] +#[versionize(PlaintextListVersions)] pub struct PlaintextList { data: C, } diff --git a/tfhe/src/core_crypto/entities/polynomial.rs b/tfhe/src/core_crypto/entities/polynomial.rs index 2144d90f2e..05d64122cc 100644 --- a/tfhe/src/core_crypto/entities/polynomial.rs +++ b/tfhe/src/core_crypto/entities/polynomial.rs @@ -1,11 +1,15 @@ //! Module containing the definition of the Polynomial. +use tfhe_versionable::Versionize; + +use crate::core_crypto::backward_compatibility::entities::polynomial::PolynomialVersions; use crate::core_crypto::commons::parameters::*; use crate::core_crypto::commons::traits::*; use std::ops::{Index, IndexMut}; /// A [`polynomial`](`Polynomial`). -#[derive(Clone, Copy, Debug, PartialEq, Eq, serde::Serialize, serde::Deserialize)] +#[derive(Clone, Copy, Debug, PartialEq, Eq, serde::Serialize, serde::Deserialize, Versionize)] +#[versionize(PolynomialVersions)] pub struct Polynomial { data: C, } diff --git a/tfhe/src/core_crypto/entities/polynomial_list.rs b/tfhe/src/core_crypto/entities/polynomial_list.rs index 24db0976a4..d6d71916c2 100644 --- a/tfhe/src/core_crypto/entities/polynomial_list.rs +++ b/tfhe/src/core_crypto/entities/polynomial_list.rs @@ -1,12 +1,16 @@ //! Module containing the definition of the PolynomialList. +use tfhe_versionable::Versionize; + +use crate::core_crypto::backward_compatibility::entities::polynomial_list::PolynomialListVersions; use crate::core_crypto::commons::parameters::*; use crate::core_crypto::commons::traits::*; use crate::core_crypto::entities::*; /// A contiguous list containing /// [`polynomials`](`crate::core_crypto::entities::Polynomial`). -#[derive(Clone, Copy, Debug, PartialEq, Eq, serde::Serialize, serde::Deserialize)] +#[derive(Clone, Copy, Debug, PartialEq, Eq, serde::Serialize, serde::Deserialize, Versionize)] +#[versionize(PolynomialListVersions)] pub struct PolynomialList { data: C, polynomial_size: PolynomialSize, diff --git a/tfhe/src/core_crypto/fft_impl/fft128/crypto/bootstrap.rs b/tfhe/src/core_crypto/fft_impl/fft128/crypto/bootstrap.rs index a3f474ef8f..99939d5c12 100644 --- a/tfhe/src/core_crypto/fft_impl/fft128/crypto/bootstrap.rs +++ b/tfhe/src/core_crypto/fft_impl/fft128/crypto/bootstrap.rs @@ -2,6 +2,7 @@ use super::super::math::fft::{Fft128, Fft128View}; use super::ggsw::{cmux, cmux_scratch}; use crate::core_crypto::algorithms::extract_lwe_sample_from_glwe_ciphertext; use crate::core_crypto::algorithms::polynomial_algorithms::*; +use crate::core_crypto::backward_compatibility::fft_impl::Fourier128LweBootstrapKeyVersions; use crate::core_crypto::commons::math::decomposition::SignedDecomposer; use crate::core_crypto::commons::math::torus::UnsignedTorus; use crate::core_crypto::commons::numeric::CastInto; @@ -20,8 +21,10 @@ use aligned_vec::{avec, ABox, CACHELINE_ALIGN}; use core::any::TypeId; use core::mem::transmute; use dyn_stack::{PodStack, ReborrowMut, SizeOverflow, StackReq}; +use tfhe_versionable::Versionize; -#[derive(Clone, Copy, Debug, PartialEq, Eq, serde::Serialize, serde::Deserialize)] +#[derive(Clone, Copy, Debug, PartialEq, Eq, serde::Serialize, serde::Deserialize, Versionize)] +#[versionize(Fourier128LweBootstrapKeyVersions)] pub struct Fourier128LweBootstrapKey> { data_re0: C, data_re1: C, diff --git a/tfhe/src/core_crypto/fft_impl/fft128/crypto/ggsw.rs b/tfhe/src/core_crypto/fft_impl/fft128/crypto/ggsw.rs index a40fef39d9..14aa239746 100644 --- a/tfhe/src/core_crypto/fft_impl/fft128/crypto/ggsw.rs +++ b/tfhe/src/core_crypto/fft_impl/fft128/crypto/ggsw.rs @@ -1,4 +1,5 @@ use super::super::math::fft::Fft128View; +use crate::core_crypto::backward_compatibility::fft_impl::Fourier128GgswCiphertextVersions; use crate::core_crypto::commons::math::decomposition::{DecompositionLevel, SignedDecomposer}; use crate::core_crypto::commons::math::torus::UnsignedTorus; use crate::core_crypto::commons::parameters::{ @@ -17,9 +18,11 @@ use crate::core_crypto::prelude::ContainerMut; use aligned_vec::CACHELINE_ALIGN; use concrete_fft::fft128::f128; use dyn_stack::{PodStack, ReborrowMut, SizeOverflow, StackReq}; +use tfhe_versionable::Versionize; /// A GGSW ciphertext in the Fourier domain. -#[derive(Clone, Copy, Debug, PartialEq, Eq, serde::Serialize, serde::Deserialize)] +#[derive(Clone, Copy, Debug, PartialEq, Eq, serde::Serialize, serde::Deserialize, Versionize)] +#[versionize(Fourier128GgswCiphertextVersions)] pub struct Fourier128GgswCiphertext> { data_re0: C, data_re1: C, diff --git a/tfhe/src/core_crypto/fft_impl/fft64/crypto/bootstrap.rs b/tfhe/src/core_crypto/fft_impl/fft64/crypto/bootstrap.rs index cdd556fa78..c512a1f253 100644 --- a/tfhe/src/core_crypto/fft_impl/fft64/crypto/bootstrap.rs +++ b/tfhe/src/core_crypto/fft_impl/fft64/crypto/bootstrap.rs @@ -24,7 +24,7 @@ use crate::core_crypto::prelude::ContainerMut; use aligned_vec::{avec, ABox, CACHELINE_ALIGN}; use concrete_fft::c64; use dyn_stack::{PodStack, ReborrowMut, SizeOverflow, StackReq}; -use tfhe_versionable::{Unversionize, UnversionizeError, Versionize}; +use tfhe_versionable::{Unversionize, UnversionizeError, Versionize, VersionizeOwned}; #[derive(Clone, Copy, Debug, PartialEq, Eq, serde::Serialize, serde::Deserialize)] #[serde(bound(deserialize = "C: IntoContainerOwned"))] @@ -37,6 +37,7 @@ pub struct FourierLweBootstrapKey> { } #[derive(serde::Serialize)] +#[cfg_attr(tfhe_lints, allow(tfhe_lints::serialize_without_versionize))] pub struct FourierLweBootstrapKeyVersion<'vers> { fourier: FourierPolynomialListVersioned<'vers>, input_lwe_dimension: ::Versioned<'vers>, @@ -46,12 +47,13 @@ pub struct FourierLweBootstrapKeyVersion<'vers> { } #[derive(serde::Serialize, serde::Deserialize)] +#[cfg_attr(tfhe_lints, allow(tfhe_lints::serialize_without_versionize))] pub struct FourierLweBootstrapKeyVersionOwned { fourier: FourierPolynomialListVersionedOwned, - input_lwe_dimension: ::VersionedOwned, - glwe_size: ::VersionedOwned, - decomposition_base_log: ::VersionedOwned, - decomposition_level_count: ::VersionedOwned, + input_lwe_dimension: ::VersionedOwned, + glwe_size: ::VersionedOwned, + decomposition_base_log: ::VersionedOwned, + decomposition_level_count: ::VersionedOwned, } impl<'vers, C: Container> From<&'vers FourierLweBootstrapKey> @@ -68,10 +70,10 @@ impl<'vers, C: Container> From<&'vers FourierLweBootstrapKey> } } -impl> From<&FourierLweBootstrapKey> +impl> From> for FourierLweBootstrapKeyVersionOwned { - fn from(value: &FourierLweBootstrapKey) -> Self { + fn from(value: FourierLweBootstrapKey) -> Self { Self { fourier: value.fourier.versionize_owned(), input_lwe_dimension: value.input_lwe_dimension.versionize_owned(), @@ -107,10 +109,12 @@ impl> Versionize for FourierLweBootstrapKey { fn versionize(&self) -> Self::Versioned<'_> { self.into() } +} +impl> VersionizeOwned for FourierLweBootstrapKey { type VersionedOwned = FourierLweBootstrapKeyVersionedOwned; - fn versionize_owned(&self) -> Self::VersionedOwned { + fn versionize_owned(self) -> Self::VersionedOwned { self.into() } } diff --git a/tfhe/src/core_crypto/fft_impl/fft64/crypto/ggsw.rs b/tfhe/src/core_crypto/fft_impl/fft64/crypto/ggsw.rs index 24b8325262..cf589a9706 100644 --- a/tfhe/src/core_crypto/fft_impl/fft64/crypto/ggsw.rs +++ b/tfhe/src/core_crypto/fft_impl/fft64/crypto/ggsw.rs @@ -1,6 +1,10 @@ use super::super::math::decomposition::TensorSignedDecompositionLendingIter; use super::super::math::fft::{FftView, FourierPolynomialList}; use super::super::math::polynomial::FourierPolynomialMutView; +use crate::core_crypto::backward_compatibility::fft_impl::{ + FourierGgswCiphertextVersioned, FourierGgswCiphertextVersionedOwned, + FourierPolynomialListVersioned, FourierPolynomialListVersionedOwned, +}; use crate::core_crypto::commons::math::decomposition::{DecompositionLevel, SignedDecomposer}; use crate::core_crypto::commons::math::torus::UnsignedTorus; use crate::core_crypto::commons::parameters::{ @@ -17,6 +21,7 @@ use crate::core_crypto::entities::glwe_ciphertext::{GlweCiphertextMutView, GlweC use aligned_vec::{avec, ABox, CACHELINE_ALIGN}; use concrete_fft::c64; use dyn_stack::{PodStack, ReborrowMut, SizeOverflow, StackReq}; +use tfhe_versionable::{Unversionize, UnversionizeError, Versionize, VersionizeOwned}; /// A GGSW ciphertext in the Fourier domain. #[derive(Clone, Copy, Debug, PartialEq, Eq, serde::Serialize, serde::Deserialize)] @@ -28,6 +33,90 @@ pub struct FourierGgswCiphertext> { decomposition_level_count: DecompositionLevelCount, } +#[derive(serde::Serialize)] +#[cfg_attr(tfhe_lints, allow(tfhe_lints::serialize_without_versionize))] +pub struct FourierGgswCiphertextVersion<'vers> { + fourier: FourierPolynomialListVersioned<'vers>, + glwe_size: ::Versioned<'vers>, + decomposition_base_log: ::Versioned<'vers>, + decomposition_level_count: ::Versioned<'vers>, +} + +#[derive(serde::Serialize, serde::Deserialize)] +#[cfg_attr(tfhe_lints, allow(tfhe_lints::serialize_without_versionize))] +pub struct FourierGgswCiphertextVersionOwned { + fourier: FourierPolynomialListVersionedOwned, + glwe_size: ::VersionedOwned, + decomposition_base_log: ::VersionedOwned, + decomposition_level_count: ::VersionedOwned, +} + +impl<'vers, C: Container> From<&'vers FourierGgswCiphertext> + for FourierGgswCiphertextVersion<'vers> +{ + fn from(value: &'vers FourierGgswCiphertext) -> Self { + Self { + fourier: value.fourier.versionize(), + glwe_size: value.glwe_size.versionize(), + decomposition_base_log: value.decomposition_base_log.versionize(), + decomposition_level_count: value.decomposition_level_count.versionize(), + } + } +} + +impl> From> + for FourierGgswCiphertextVersionOwned +{ + fn from(value: FourierGgswCiphertext) -> Self { + Self { + fourier: value.fourier.versionize_owned(), + glwe_size: value.glwe_size.versionize_owned(), + decomposition_base_log: value.decomposition_base_log.versionize_owned(), + decomposition_level_count: value.decomposition_level_count.versionize_owned(), + } + } +} + +impl> TryFrom + for FourierGgswCiphertext +{ + type Error = UnversionizeError; + fn try_from(value: FourierGgswCiphertextVersionOwned) -> Result { + Ok(Self { + fourier: FourierPolynomialList::unversionize(value.fourier)?, + glwe_size: GlweSize::unversionize(value.glwe_size)?, + decomposition_base_log: DecompositionBaseLog::unversionize( + value.decomposition_base_log, + )?, + decomposition_level_count: DecompositionLevelCount::unversionize( + value.decomposition_level_count, + )?, + }) + } +} + +impl> Versionize for FourierGgswCiphertext { + type Versioned<'vers> = FourierGgswCiphertextVersioned<'vers> where C: 'vers; + + fn versionize(&self) -> Self::Versioned<'_> { + self.into() + } +} + +impl> VersionizeOwned for FourierGgswCiphertext { + type VersionedOwned = FourierGgswCiphertextVersionedOwned; + + fn versionize_owned(self) -> Self::VersionedOwned { + self.into() + } +} + +impl> Unversionize for FourierGgswCiphertext { + fn unversionize(versioned: Self::VersionedOwned) -> Result { + Self::try_from(versioned) + } +} + /// A matrix containing a single level of gadget decomposition, in the Fourier domain. #[derive(Clone, Copy, Debug, PartialEq, Eq)] pub struct FourierGgswLevelMatrix> { diff --git a/tfhe/src/core_crypto/fft_impl/fft64/math/fft/mod.rs b/tfhe/src/core_crypto/fft_impl/fft64/math/fft/mod.rs index cb8e4660c3..17d070a041 100644 --- a/tfhe/src/core_crypto/fft_impl/fft64/math/fft/mod.rs +++ b/tfhe/src/core_crypto/fft_impl/fft64/math/fft/mod.rs @@ -20,7 +20,7 @@ use std::mem::{align_of, size_of}; use std::sync::{Arc, OnceLock, RwLock}; #[cfg(not(feature = "experimental-force_fft_algo_dif4"))] use std::time::Duration; -use tfhe_versionable::{Unversionize, UnversionizeError, Versionize}; +use tfhe_versionable::{Unversionize, UnversionizeError, Versionize, VersionizeOwned}; #[cfg(any(target_arch = "x86_64", target_arch = "x86"))] mod x86; @@ -595,10 +595,12 @@ impl> Versionize for FourierPolynomialList { fn versionize(&self) -> Self::Versioned<'_> { self.into() } +} +impl> VersionizeOwned for FourierPolynomialList { type VersionedOwned = FourierPolynomialListVersionedOwned; - fn versionize_owned(&self) -> Self::VersionedOwned { + fn versionize_owned(self) -> Self::VersionedOwned { self.into() } } @@ -618,6 +620,7 @@ impl> serde::Serialize for FourierPolynomialList ) -> Result { use crate::core_crypto::commons::traits::Split; + #[cfg_attr(tfhe_lints, allow(tfhe_lints::serialize_without_versionize))] pub struct SingleFourierPolynomial<'a> { fft: FftView<'a>, buf: &'a [c64], diff --git a/tfhe/src/high_level_api/backward_compatibility/booleans.rs b/tfhe/src/high_level_api/backward_compatibility/booleans.rs index e36e184cae..1b961b835d 100644 --- a/tfhe/src/high_level_api/backward_compatibility/booleans.rs +++ b/tfhe/src/high_level_api/backward_compatibility/booleans.rs @@ -9,6 +9,7 @@ use crate::{CompactCiphertextList as HlCompactCiphertextList, CompressedFheBool, // Manual impl #[derive(Serialize, Deserialize)] +#[cfg_attr(tfhe_lints, allow(tfhe_lints::serialize_without_versionize))] pub(crate) enum InnerBooleanVersionedOwned { V0(InnerBooleanVersionOwned), } diff --git a/tfhe/src/high_level_api/backward_compatibility/compressed_ciphertext_list.rs b/tfhe/src/high_level_api/backward_compatibility/compressed_ciphertext_list.rs new file mode 100644 index 0000000000..f1087ffba0 --- /dev/null +++ b/tfhe/src/high_level_api/backward_compatibility/compressed_ciphertext_list.rs @@ -0,0 +1,8 @@ +use tfhe_versionable::VersionsDispatch; + +use crate::CompressedCiphertextList; + +#[derive(VersionsDispatch)] +pub enum CompressedCiphertextListVersions { + V0(CompressedCiphertextList), +} diff --git a/tfhe/src/high_level_api/backward_compatibility/integers.rs b/tfhe/src/high_level_api/backward_compatibility/integers.rs index 812bfc763c..6d8f92f064 100644 --- a/tfhe/src/high_level_api/backward_compatibility/integers.rs +++ b/tfhe/src/high_level_api/backward_compatibility/integers.rs @@ -1,5 +1,7 @@ #![allow(deprecated)] +use std::convert::Infallible; + use rayon::iter::{IntoParallelRefIterator, ParallelIterator}; use tfhe_versionable::{Upgrade, Version, Versionize, VersionsDispatch}; @@ -21,11 +23,13 @@ use serde::{Deserialize, Serialize}; // Manual impl #[derive(Serialize, Deserialize)] +#[cfg_attr(tfhe_lints, allow(tfhe_lints::serialize_without_versionize))] pub(crate) enum SignedRadixCiphertextVersionedOwned { V0(SignedRadixCiphertextVersionOwned), } #[derive(Serialize, Deserialize)] +#[cfg_attr(tfhe_lints, allow(tfhe_lints::serialize_without_versionize))] pub(crate) enum UnsignedRadixCiphertextVersionedOwned { V0(UnsignedRadixCiphertextVersionOwned), } @@ -51,7 +55,9 @@ pub enum CompressedSignedRadixCiphertextV0 { } impl Upgrade for CompressedSignedRadixCiphertextV0 { - fn upgrade(self) -> Result { + type Error = Infallible; + + fn upgrade(self) -> Result { match self { Self::Seeded(ct) => Ok(CompressedSignedRadixCiphertext::Seeded(ct)), @@ -87,7 +93,9 @@ pub enum CompressedRadixCiphertextV0 { } impl Upgrade for CompressedRadixCiphertextV0 { - fn upgrade(self) -> Result { + type Error = Infallible; + + fn upgrade(self) -> Result { match self { Self::Seeded(ct) => Ok(CompressedRadixCiphertext::Seeded(ct)), diff --git a/tfhe/src/high_level_api/backward_compatibility/keys.rs b/tfhe/src/high_level_api/backward_compatibility/keys.rs index 153395d959..b4a0ab4a49 100644 --- a/tfhe/src/high_level_api/backward_compatibility/keys.rs +++ b/tfhe/src/high_level_api/backward_compatibility/keys.rs @@ -1,4 +1,6 @@ -use serde::{Deserialize, Serialize}; +use std::convert::Infallible; +use std::sync::Arc; + use tfhe_versionable::{Upgrade, Version, VersionsDispatch}; use crate::high_level_api::keys::*; @@ -8,14 +10,27 @@ pub enum ClientKeyVersions { V0(ClientKey), } -#[derive(Serialize)] -pub enum ServerKeyVersioned<'vers> { - V0(ServerKeyVersion<'vers>), +// This type was previously versioned using a manual implementation with a conversion +// to a type where the inner key was name `integer_key` +#[derive(Version)] +pub struct ServerKeyV0 { + pub(crate) integer_key: Arc, } -#[derive(Serialize, Deserialize)] -pub enum ServerKeyVersionedOwned { - V0(ServerKeyVersionOwned), +impl Upgrade for ServerKeyV0 { + type Error = Infallible; + + fn upgrade(self) -> Result { + Ok(ServerKey { + key: self.integer_key, + }) + } +} + +#[derive(VersionsDispatch)] +pub enum ServerKeyVersions { + V0(ServerKeyV0), + V1(ServerKey), } #[derive(VersionsDispatch)] @@ -56,7 +71,9 @@ pub(crate) struct IntegerClientKeyV0 { } impl Upgrade for IntegerClientKeyV0 { - fn upgrade(self) -> Result { + type Error = Infallible; + + fn upgrade(self) -> Result { Ok(IntegerClientKey { key: self.key, wopbs_block_parameters: self.wopbs_block_parameters, @@ -80,7 +97,9 @@ pub struct IntegerServerKeyV0 { } impl Upgrade for IntegerServerKeyV0 { - fn upgrade(self) -> Result { + type Error = Infallible; + + fn upgrade(self) -> Result { Ok(IntegerServerKey { key: self.key, wopbs_key: self.wopbs_key, @@ -103,7 +122,9 @@ pub struct IntegerCompressedServerKeyV0 { } impl Upgrade for IntegerCompressedServerKeyV0 { - fn upgrade(self) -> Result { + type Error = Infallible; + + fn upgrade(self) -> Result { Ok(IntegerCompressedServerKey { key: self.key, cpk_key_switching_key_material: None, diff --git a/tfhe/src/high_level_api/backward_compatibility/mod.rs b/tfhe/src/high_level_api/backward_compatibility/mod.rs index 2d21696ced..97c49f4839 100644 --- a/tfhe/src/high_level_api/backward_compatibility/mod.rs +++ b/tfhe/src/high_level_api/backward_compatibility/mod.rs @@ -2,6 +2,7 @@ pub mod booleans; pub mod compact_list; +pub mod compressed_ciphertext_list; pub mod config; pub mod integers; pub mod keys; diff --git a/tfhe/src/high_level_api/booleans/inner.rs b/tfhe/src/high_level_api/booleans/inner.rs index 67d557e9d8..a9b40226df 100644 --- a/tfhe/src/high_level_api/booleans/inner.rs +++ b/tfhe/src/high_level_api/booleans/inner.rs @@ -6,7 +6,7 @@ use crate::high_level_api::global_state::with_thread_local_cuda_streams; use crate::integer::BooleanBlock; use crate::Device; use serde::{Deserializer, Serializer}; -use tfhe_versionable::{Unversionize, UnversionizeError, Versionize}; +use tfhe_versionable::{Unversionize, UnversionizeError, Versionize, VersionizeOwned}; /// Enum that manages the current inner representation of a boolean. pub(in crate::high_level_api) enum InnerBoolean { @@ -52,8 +52,9 @@ impl<'de> serde::Deserialize<'de> for InnerBoolean { // Only CPU data are serialized so we only versionize the CPU type. #[derive(serde::Serialize, serde::Deserialize)] +#[cfg_attr(tfhe_lints, allow(tfhe_lints::serialize_without_versionize))] pub(crate) struct InnerBooleanVersionOwned( - ::VersionedOwned, + ::VersionedOwned, ); impl Versionize for InnerBoolean { @@ -61,15 +62,18 @@ impl Versionize for InnerBoolean { fn versionize(&self) -> Self::Versioned<'_> { let data = self.on_cpu(); - let versioned = data.versionize_owned(); + let versioned = data.into_owned().versionize_owned(); InnerBooleanVersionedOwned::V0(InnerBooleanVersionOwned(versioned)) } - +} +impl VersionizeOwned for InnerBoolean { type VersionedOwned = InnerBooleanVersionedOwned; - fn versionize_owned(&self) -> Self::VersionedOwned { + fn versionize_owned(self) -> Self::VersionedOwned { let cpu_data = self.on_cpu(); - InnerBooleanVersionedOwned::V0(InnerBooleanVersionOwned(cpu_data.versionize_owned())) + InnerBooleanVersionedOwned::V0(InnerBooleanVersionOwned( + cpu_data.into_owned().versionize_owned(), + )) } } diff --git a/tfhe/src/high_level_api/compressed_ciphertext_list.rs b/tfhe/src/high_level_api/compressed_ciphertext_list.rs index 2a95958630..32d9040e5f 100644 --- a/tfhe/src/high_level_api/compressed_ciphertext_list.rs +++ b/tfhe/src/high_level_api/compressed_ciphertext_list.rs @@ -1,4 +1,7 @@ +use tfhe_versionable::Versionize; + use super::keys::InternalServerKey; +use crate::backward_compatibility::compressed_ciphertext_list::CompressedCiphertextListVersions; use crate::core_crypto::commons::math::random::{Deserialize, Serialize}; use crate::high_level_api::integers::{FheIntId, FheUintId}; use crate::integer::ciphertext::{Compressible, DataKind, Expandable}; @@ -68,7 +71,8 @@ impl CompressedCiphertextListBuilder { } } -#[derive(Clone, Serialize, Deserialize)] +#[derive(Clone, Serialize, Deserialize, Versionize)] +#[versionize(CompressedCiphertextListVersions)] pub struct CompressedCiphertextList(crate::integer::ciphertext::CompressedCiphertextList); impl Named for CompressedCiphertextList { diff --git a/tfhe/src/high_level_api/integers/signed/inner.rs b/tfhe/src/high_level_api/integers/signed/inner.rs index d883fcdd0a..dfcc9e1564 100644 --- a/tfhe/src/high_level_api/integers/signed/inner.rs +++ b/tfhe/src/high_level_api/integers/signed/inner.rs @@ -9,7 +9,7 @@ use crate::integer::gpu::ciphertext::CudaIntegerRadixCiphertext; use crate::integer::gpu::ciphertext::CudaSignedRadixCiphertext; use crate::Device; use serde::{Deserializer, Serializer}; -use tfhe_versionable::{Unversionize, UnversionizeError, Versionize}; +use tfhe_versionable::{Unversionize, UnversionizeError, Versionize, VersionizeOwned}; pub(crate) enum RadixCiphertext { Cpu(crate::integer::SignedRadixCiphertext), @@ -67,8 +67,9 @@ impl<'de> serde::Deserialize<'de> for RadixCiphertext { // Only CPU data are serialized so we only versionize the CPU type. #[derive(serde::Serialize, serde::Deserialize)] +#[cfg_attr(tfhe_lints, allow(tfhe_lints::serialize_without_versionize))] pub(crate) struct RadixCiphertextVersionOwned( - ::VersionedOwned, + ::VersionedOwned, ); impl Versionize for RadixCiphertext { @@ -76,16 +77,18 @@ impl Versionize for RadixCiphertext { fn versionize(&self) -> Self::Versioned<'_> { let data = self.on_cpu(); - let versioned = data.versionize_owned(); + let versioned = data.into_owned().versionize_owned(); SignedRadixCiphertextVersionedOwned::V0(RadixCiphertextVersionOwned(versioned)) } +} +impl VersionizeOwned for RadixCiphertext { type VersionedOwned = SignedRadixCiphertextVersionedOwned; - fn versionize_owned(&self) -> Self::VersionedOwned { + fn versionize_owned(self) -> Self::VersionedOwned { let cpu_data = self.on_cpu(); SignedRadixCiphertextVersionedOwned::V0(RadixCiphertextVersionOwned( - cpu_data.versionize_owned(), + cpu_data.into_owned().versionize_owned(), )) } } diff --git a/tfhe/src/high_level_api/integers/unsigned/inner.rs b/tfhe/src/high_level_api/integers/unsigned/inner.rs index 7926e3613a..8ef5e28880 100644 --- a/tfhe/src/high_level_api/integers/unsigned/inner.rs +++ b/tfhe/src/high_level_api/integers/unsigned/inner.rs @@ -7,7 +7,7 @@ use crate::high_level_api::global_state::with_thread_local_cuda_streams; use crate::integer::gpu::ciphertext::CudaIntegerRadixCiphertext; use crate::Device; use serde::{Deserializer, Serializer}; -use tfhe_versionable::{Unversionize, UnversionizeError, Versionize}; +use tfhe_versionable::{Unversionize, UnversionizeError, Versionize, VersionizeOwned}; pub(crate) enum RadixCiphertext { Cpu(crate::integer::RadixCiphertext), @@ -63,8 +63,9 @@ impl<'de> serde::Deserialize<'de> for RadixCiphertext { // Only CPU data are serialized so we only version the CPU type. #[derive(serde::Serialize, serde::Deserialize)] +#[cfg_attr(tfhe_lints, allow(tfhe_lints::serialize_without_versionize))] pub(crate) struct RadixCiphertextVersionOwned( - ::VersionedOwned, + ::VersionedOwned, ); impl Versionize for RadixCiphertext { @@ -72,16 +73,18 @@ impl Versionize for RadixCiphertext { fn versionize(&self) -> Self::Versioned<'_> { let data = self.on_cpu(); - let versioned = data.versionize_owned(); + let versioned = data.into_owned().versionize_owned(); UnsignedRadixCiphertextVersionedOwned::V0(RadixCiphertextVersionOwned(versioned)) } +} +impl VersionizeOwned for RadixCiphertext { type VersionedOwned = UnsignedRadixCiphertextVersionedOwned; - fn versionize_owned(&self) -> Self::VersionedOwned { + fn versionize_owned(self) -> Self::VersionedOwned { let cpu_data = self.on_cpu(); UnsignedRadixCiphertextVersionedOwned::V0(RadixCiphertextVersionOwned( - cpu_data.versionize_owned(), + cpu_data.into_owned().versionize_owned(), )) } } diff --git a/tfhe/src/high_level_api/keys/mod.rs b/tfhe/src/high_level_api/keys/mod.rs index 60e32e1bde..c803b4557d 100644 --- a/tfhe/src/high_level_api/keys/mod.rs +++ b/tfhe/src/high_level_api/keys/mod.rs @@ -12,8 +12,8 @@ pub use key_switching_key::KeySwitchingKey; pub use public::{CompactPublicKey, CompressedCompactPublicKey, CompressedPublicKey, PublicKey}; #[cfg(feature = "gpu")] pub use server::CudaServerKey; +pub(crate) use server::InternalServerKey; pub use server::{CompressedServerKey, ServerKey}; -pub(crate) use server::{InternalServerKey, ServerKeyVersion, ServerKeyVersionOwned}; pub(in crate::high_level_api) use inner::{ IntegerClientKey, IntegerCompactPublicKey, IntegerCompressedCompactPublicKey, diff --git a/tfhe/src/high_level_api/keys/server.rs b/tfhe/src/high_level_api/keys/server.rs index 40c05583da..a6a16fa1fb 100644 --- a/tfhe/src/high_level_api/keys/server.rs +++ b/tfhe/src/high_level_api/keys/server.rs @@ -1,8 +1,6 @@ -use tfhe_versionable::{Unversionize, Versionize}; +use tfhe_versionable::Versionize; -use crate::backward_compatibility::keys::{ - CompressedServerKeyVersions, ServerKeyVersioned, ServerKeyVersionedOwned, -}; +use crate::backward_compatibility::keys::{CompressedServerKeyVersions, ServerKeyVersions}; #[cfg(feature = "gpu")] use crate::core_crypto::gpu::{synchronize_devices, CudaStreams}; use crate::high_level_api::keys::{IntegerCompressedServerKey, IntegerServerKey}; @@ -23,7 +21,8 @@ use super::ClientKey; // Keys are stored in an Arc, so that cloning them is cheap // (compared to an actual clone hundreds of MB / GB), and cheap cloning is needed for // multithreading with less overhead) -#[derive(Clone)] +#[derive(Clone, Versionize)] +#[versionize(ServerKeyVersions)] pub struct ServerKey { pub(crate) key: Arc, } @@ -102,6 +101,8 @@ impl AsRef for ServerKey { // The inner `Arc` are used to make copying a server key more performant before a `set_server_key` // in multi-threading scenarios. #[derive(serde::Serialize)] +// We directly versionize the `ServerKey` without having to use this intermediate type. +#[cfg_attr(tfhe_lints, allow(tfhe_lints::serialize_without_versionize))] struct SerializableServerKey<'a> { pub(crate) integer_key: &'a IntegerServerKey, } @@ -134,48 +135,6 @@ impl<'de> serde::Deserialize<'de> for ServerKey { } } -#[derive(serde::Serialize)] -pub struct ServerKeyVersion<'vers> { - pub(crate) integer_key: ::Versioned<'vers>, -} - -#[derive(serde::Serialize, serde::Deserialize)] -pub struct ServerKeyVersionOwned { - pub(crate) integer_key: ::VersionedOwned, -} - -impl Versionize for ServerKey { - type Versioned<'vers> = ServerKeyVersioned<'vers>; - - fn versionize(&self) -> Self::Versioned<'_> { - ServerKeyVersioned::V0(ServerKeyVersion { - integer_key: self.key.versionize(), - }) - } - - type VersionedOwned = ServerKeyVersionedOwned; - - fn versionize_owned(&self) -> Self::VersionedOwned { - ServerKeyVersionedOwned::V0(ServerKeyVersionOwned { - integer_key: self.key.versionize_owned(), - }) - } -} - -impl Unversionize for ServerKey { - fn unversionize( - versioned: Self::VersionedOwned, - ) -> Result { - match versioned { - ServerKeyVersionedOwned::V0(v0) => { - IntegerServerKey::unversionize(v0.integer_key).map(|unversioned| Self { - key: Arc::new(unversioned), - }) - } - } - } -} - /// Compressed ServerKey /// /// A CompressedServerKey takes much less disk space / memory space than a diff --git a/tfhe/src/integer/backward_compatibility/ciphertext/mod.rs b/tfhe/src/integer/backward_compatibility/ciphertext/mod.rs index d62f0e520e..f268099091 100644 --- a/tfhe/src/integer/backward_compatibility/ciphertext/mod.rs +++ b/tfhe/src/integer/backward_compatibility/ciphertext/mod.rs @@ -1,8 +1,11 @@ +use std::convert::Infallible; + use tfhe_versionable::{Upgrade, Version, VersionsDispatch}; use crate::integer::ciphertext::{ BaseCrtCiphertext, BaseRadixCiphertext, BaseSignedRadixCiphertext, CompactCiphertextList, - CompressedModulusSwitchedRadixCiphertext, CompressedModulusSwitchedRadixCiphertextGeneric, + CompressedCiphertextList, CompressedModulusSwitchedRadixCiphertext, + CompressedModulusSwitchedRadixCiphertextGeneric, CompressedModulusSwitchedSignedRadixCiphertext, DataKind, }; use crate::integer::BooleanBlock; @@ -30,7 +33,9 @@ pub struct CompactCiphertextListV0 { } impl Upgrade for CompactCiphertextListV0 { - fn upgrade(self) -> Result { + type Error = Infallible; + + fn upgrade(self) -> Result { let radix_count = self.ct_list.ct_list.lwe_ciphertext_count().0 / self.num_blocks_per_integer; // Since we can't guess the type of data here, we set them by default as unsigned integer. @@ -81,3 +86,8 @@ pub type CompressedModulusSwitchedSignedRadixCiphertextTFHE06 = pub type CompressedModulusSwitchedRadixCiphertextTFHE06 = BaseRadixCiphertext; + +#[derive(VersionsDispatch)] +pub enum CompressedCiphertextListVersions { + V0(CompressedCiphertextList), +} diff --git a/tfhe/src/integer/backward_compatibility/client_key/mod.rs b/tfhe/src/integer/backward_compatibility/client_key/mod.rs index a126cf991b..0c1f44271e 100644 --- a/tfhe/src/integer/backward_compatibility/client_key/mod.rs +++ b/tfhe/src/integer/backward_compatibility/client_key/mod.rs @@ -1,8 +1,18 @@ use tfhe_versionable::VersionsDispatch; -use crate::integer::ClientKey; +use crate::integer::{ClientKey, CrtClientKey, RadixClientKey}; #[derive(VersionsDispatch)] pub enum ClientKeyVersions { V0(ClientKey), } + +#[derive(VersionsDispatch)] +pub enum CrtClientKeyVersions { + V0(CrtClientKey), +} + +#[derive(VersionsDispatch)] +pub enum RadixClientKeyVersions { + V0(RadixClientKey), +} diff --git a/tfhe/src/integer/backward_compatibility/key_switching_key.rs b/tfhe/src/integer/backward_compatibility/key_switching_key.rs index d3fb67d3c8..646d298228 100644 --- a/tfhe/src/integer/backward_compatibility/key_switching_key.rs +++ b/tfhe/src/integer/backward_compatibility/key_switching_key.rs @@ -1,7 +1,8 @@ use tfhe_versionable::VersionsDispatch; use crate::integer::key_switching_key::{ - CompressedKeySwitchingKeyMaterial, KeySwitchingKeyMaterial, + CompressedKeySwitchingKey, CompressedKeySwitchingKeyMaterial, KeySwitchingKey, + KeySwitchingKeyMaterial, }; #[derive(VersionsDispatch)] @@ -9,7 +10,17 @@ pub enum KeySwitchingKeyMaterialVersions { V0(KeySwitchingKeyMaterial), } +#[derive(VersionsDispatch)] +pub enum KeySwitchingKeyVersions { + V0(KeySwitchingKey), +} + #[derive(VersionsDispatch)] pub enum CompressedKeySwitchingKeyMaterialVersions { V0(CompressedKeySwitchingKeyMaterial), } + +#[derive(VersionsDispatch)] +pub enum CompressedKeySwitchingKeyVersions { + V0(CompressedKeySwitchingKey), +} diff --git a/tfhe/src/integer/ciphertext/compressed_ciphertext_list.rs b/tfhe/src/integer/ciphertext/compressed_ciphertext_list.rs index b2d7e17a5d..ae1edb93d9 100644 --- a/tfhe/src/integer/ciphertext/compressed_ciphertext_list.rs +++ b/tfhe/src/integer/ciphertext/compressed_ciphertext_list.rs @@ -1,10 +1,12 @@ use super::{DataKind, Expandable, RadixCiphertext, SignedRadixCiphertext}; +use crate::integer::backward_compatibility::ciphertext::CompressedCiphertextListVersions; use crate::integer::BooleanBlock; use crate::shortint::ciphertext::CompressedCiphertextList as ShortintCompressedCiphertextList; use crate::shortint::list_compression::{CompressionKey, DecompressionKey}; use crate::shortint::Ciphertext; use rayon::prelude::*; use serde::{Deserialize, Serialize}; +use tfhe_versionable::Versionize; pub trait Compressible { fn compress_into(self, messages: &mut Vec) -> DataKind; @@ -90,7 +92,8 @@ impl CompressedCiphertextListBuilder { } } -#[derive(Clone, Serialize, Deserialize)] +#[derive(Clone, Serialize, Deserialize, Versionize)] +#[versionize(CompressedCiphertextListVersions)] pub struct CompressedCiphertextList { pub(crate) packed_list: ShortintCompressedCiphertextList, info: Vec, diff --git a/tfhe/src/integer/client_key/crt.rs b/tfhe/src/integer/client_key/crt.rs index 9e04018f14..addf229f3f 100644 --- a/tfhe/src/integer/client_key/crt.rs +++ b/tfhe/src/integer/client_key/crt.rs @@ -1,6 +1,8 @@ use super::{ClientKey, SecretEncryptionKeyView}; +use crate::integer::backward_compatibility::client_key::CrtClientKeyVersions; use crate::integer::CrtCiphertext; use serde::{Deserialize, Serialize}; +use tfhe_versionable::Versionize; /// Client key "specialized" for CRT decomposition. /// @@ -25,7 +27,8 @@ use serde::{Deserialize, Serialize}; /// let dec = cks.decrypt(&ct); /// assert_eq!(msg, dec); /// ``` -#[derive(Serialize, Deserialize, PartialEq, Debug, Clone)] +#[derive(Serialize, Deserialize, PartialEq, Debug, Clone, Versionize)] +#[versionize(CrtClientKeyVersions)] pub struct CrtClientKey { key: ClientKey, moduli: Vec, diff --git a/tfhe/src/integer/client_key/radix.rs b/tfhe/src/integer/client_key/radix.rs index 5c955fbb88..9f21a03927 100644 --- a/tfhe/src/integer/client_key/radix.rs +++ b/tfhe/src/integer/client_key/radix.rs @@ -2,6 +2,7 @@ use super::{ClientKey, RecomposableSignedInteger, SecretEncryptionKeyView}; use crate::core_crypto::prelude::{SignedNumeric, UnsignedNumeric}; +use crate::integer::backward_compatibility::client_key::RadixClientKeyVersions; use crate::integer::block_decomposition::{DecomposableInto, RecomposableFrom}; use crate::integer::ciphertext::{RadixCiphertext, SignedRadixCiphertext}; use crate::integer::BooleanBlock; @@ -9,6 +10,7 @@ use crate::shortint::list_compression::{CompressionKey, CompressionPrivateKeys, use crate::shortint::parameters::list_compression::CompressionParameters; use crate::shortint::{Ciphertext as ShortintCiphertext, PBSParameters as ShortintParameters}; use serde::{Deserialize, Serialize}; +use tfhe_versionable::Versionize; /// Client key "specialized" for radix decomposition. /// @@ -32,7 +34,8 @@ use serde::{Deserialize, Serialize}; /// let dec = cks.decrypt(&ct); /// assert_eq!(msg, dec); /// ``` -#[derive(Serialize, Deserialize, PartialEq, Debug, Clone)] +#[derive(Serialize, Deserialize, PartialEq, Debug, Clone, Versionize)] +#[versionize(RadixClientKeyVersions)] pub struct RadixClientKey { key: ClientKey, num_blocks: usize, diff --git a/tfhe/src/integer/key_switching_key/mod.rs b/tfhe/src/integer/key_switching_key/mod.rs index 291d043e68..cbc1d4be45 100644 --- a/tfhe/src/integer/key_switching_key/mod.rs +++ b/tfhe/src/integer/key_switching_key/mod.rs @@ -1,5 +1,6 @@ use super::backward_compatibility::key_switching_key::{ - CompressedKeySwitchingKeyMaterialVersions, KeySwitchingKeyMaterialVersions, + CompressedKeySwitchingKeyMaterialVersions, CompressedKeySwitchingKeyVersions, + KeySwitchingKeyMaterialVersions, KeySwitchingKeyVersions, }; use super::{ClientKey, CompressedServerKey, ServerKey}; use crate::integer::client_key::secret_encryption_key::SecretEncryptionKeyView; @@ -54,7 +55,8 @@ impl KeySwitchingKeyMaterial { } } -#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] +#[derive(Clone, Debug, PartialEq, Serialize, Deserialize, Versionize)] +#[versionize(KeySwitchingKeyVersions)] pub struct KeySwitchingKey { pub(crate) key: crate::shortint::KeySwitchingKey, } @@ -210,7 +212,8 @@ impl CompressedKeySwitchingKeyMaterial { } } -#[derive(Clone, Debug, Serialize, Deserialize)] +#[derive(Clone, Debug, Serialize, Deserialize, Versionize)] +#[versionize(CompressedKeySwitchingKeyVersions)] pub struct CompressedKeySwitchingKey { pub(crate) key: crate::shortint::CompressedKeySwitchingKey, } diff --git a/tfhe/src/safe_deserialization.rs b/tfhe/src/safe_deserialization.rs index 4c1b74aeb9..ad7ceb21e4 100644 --- a/tfhe/src/safe_deserialization.rs +++ b/tfhe/src/safe_deserialization.rs @@ -20,6 +20,8 @@ const SERIALIZATION_VERSION: &str = "0.4"; /// Tells if this serialized object is versioned or not #[derive(Serialize, Deserialize, PartialEq, Eq)] +// This type should not be versioned because it is part of a wrapper of versioned messages. +#[cfg_attr(tfhe_lints, allow(tfhe_lints::serialize_without_versionize))] enum SerializationMode { /// Serialize with type versioning for backward compatibility Versioned, @@ -29,6 +31,8 @@ enum SerializationMode { /// Header with global metadata about the serialized object. #[derive(Serialize, Deserialize)] +// This type should not be versioned because it is part of a wrapper of versioned messages. +#[cfg_attr(tfhe_lints, allow(tfhe_lints::serialize_without_versionize))] struct SerializationHeader { mode: SerializationMode, version: Cow<'static, str>, diff --git a/tfhe/src/shortint/backward_compatibility/ciphertext/mod.rs b/tfhe/src/shortint/backward_compatibility/ciphertext/mod.rs index 236efc94bb..947e33cfb4 100644 --- a/tfhe/src/shortint/backward_compatibility/ciphertext/mod.rs +++ b/tfhe/src/shortint/backward_compatibility/ciphertext/mod.rs @@ -1,3 +1,5 @@ +use std::convert::Infallible; + use crate::core_crypto::prelude::{ CompressedModulusSwitchedLweCiphertext, LweCompactCiphertextListOwned, }; @@ -42,7 +44,9 @@ pub struct CompactCiphertextListV0 { } impl Upgrade for CompactCiphertextListV0 { - fn upgrade(self) -> Result { + type Error = Infallible; + + fn upgrade(self) -> Result { Ok(CompactCiphertextList { ct_list: self.ct_list, degree: self.degree, @@ -76,7 +80,9 @@ pub struct CompressedModulusSwitchedCiphertextV0 { } impl Upgrade for CompressedModulusSwitchedCiphertextV0 { - fn upgrade(self) -> Result { + type Error = Infallible; + + fn upgrade(self) -> Result { Ok(CompressedModulusSwitchedCiphertext { compressed_modulus_switched_lwe_ciphertext: InternalCompressedModulusSwitchedCiphertext::Classic( @@ -101,3 +107,8 @@ pub enum CompressedModulusSwitchedCiphertextVersions { pub(crate) enum InternalCompressedModulusSwitchedCiphertextVersions { V0(InternalCompressedModulusSwitchedCiphertext), } + +#[derive(VersionsDispatch)] +pub enum CompressedCiphertextListVersions { + V0(CompressedCiphertextList), +} diff --git a/tfhe/src/shortint/backward_compatibility/key_switching_key.rs b/tfhe/src/shortint/backward_compatibility/key_switching_key.rs index 5fed69e5a5..7686a21153 100644 --- a/tfhe/src/shortint/backward_compatibility/key_switching_key.rs +++ b/tfhe/src/shortint/backward_compatibility/key_switching_key.rs @@ -3,13 +3,24 @@ use tfhe_versionable::VersionsDispatch; use crate::shortint::key_switching_key::{ CompressedKeySwitchingKeyMaterial, KeySwitchingKeyMaterial, }; +use crate::shortint::{CompressedKeySwitchingKey, KeySwitchingKey}; #[derive(VersionsDispatch)] pub enum KeySwitchingKeyMaterialVersions { V0(KeySwitchingKeyMaterial), } +#[derive(VersionsDispatch)] +pub enum KeySwitchingKeyVersions { + V0(KeySwitchingKey), +} + #[derive(VersionsDispatch)] pub enum CompressedKeySwitchingKeyMaterialVersions { V0(CompressedKeySwitchingKeyMaterial), } + +#[derive(VersionsDispatch)] +pub enum CompressedKeySwitchingKeyVersions { + V0(CompressedKeySwitchingKey), +} diff --git a/tfhe/src/shortint/backward_compatibility/public_key/mod.rs b/tfhe/src/shortint/backward_compatibility/public_key/mod.rs index a0184c9a5d..8e18cc3485 100644 --- a/tfhe/src/shortint/backward_compatibility/public_key/mod.rs +++ b/tfhe/src/shortint/backward_compatibility/public_key/mod.rs @@ -21,16 +21,15 @@ pub struct CompactPublicKeyV0 { } impl Upgrade for CompactPublicKeyV0 { - fn upgrade(self) -> Result { - let parameters = self - .parameters - .try_into() - .map_err(|err: crate::Error| err.to_string())?; + fn upgrade(self) -> Result { + let parameters = self.parameters.try_into()?; Ok(CompactPublicKey { key: self.key, parameters, }) } + + type Error = crate::Error; } #[derive(VersionsDispatch)] @@ -57,16 +56,15 @@ pub struct CompressedCompactPublicKeyV0 { } impl Upgrade for CompressedCompactPublicKeyV0 { - fn upgrade(self) -> Result { - let parameters = self - .parameters - .try_into() - .map_err(|err: crate::Error| err.to_string())?; + fn upgrade(self) -> Result { + let parameters = self.parameters.try_into()?; Ok(CompressedCompactPublicKey { key: self.key, parameters, }) } + + type Error = crate::Error; } #[derive(VersionsDispatch)] diff --git a/tfhe/src/shortint/backward_compatibility/server_key/mod.rs b/tfhe/src/shortint/backward_compatibility/server_key/mod.rs index 315aa3abbc..fa89f6f2eb 100644 --- a/tfhe/src/shortint/backward_compatibility/server_key/mod.rs +++ b/tfhe/src/shortint/backward_compatibility/server_key/mod.rs @@ -5,6 +5,7 @@ use crate::core_crypto::prelude::{Container, IntoContainerOwned}; use crate::shortint::server_key::*; #[derive(Serialize)] +#[cfg_attr(tfhe_lints, allow(tfhe_lints::serialize_without_versionize))] pub enum SerializableShortintBootstrappingKeyVersioned<'vers> { V0(SerializableShortintBootstrappingKeyVersion<'vers>), } @@ -19,14 +20,15 @@ impl<'vers, C: Container> } #[derive(Serialize, Deserialize)] +#[cfg_attr(tfhe_lints, allow(tfhe_lints::serialize_without_versionize))] pub enum SerializableShortintBootstrappingKeyVersionedOwned { V0(SerializableShortintBootstrappingKeyVersionOwned), } -impl> From<&SerializableShortintBootstrappingKey> +impl> From> for SerializableShortintBootstrappingKeyVersionedOwned { - fn from(value: &SerializableShortintBootstrappingKey) -> Self { + fn from(value: SerializableShortintBootstrappingKey) -> Self { Self::V0(value.into()) } } diff --git a/tfhe/src/shortint/ciphertext/compressed_ciphertext_list.rs b/tfhe/src/shortint/ciphertext/compressed_ciphertext_list.rs index 3c18500f87..892a992fbc 100644 --- a/tfhe/src/shortint/ciphertext/compressed_ciphertext_list.rs +++ b/tfhe/src/shortint/ciphertext/compressed_ciphertext_list.rs @@ -1,10 +1,14 @@ +use tfhe_versionable::Versionize; + use self::compressed_modulus_switched_glwe_ciphertext::CompressedModulusSwitchedGlweCiphertext; use crate::conformance::ParameterSetConformant; use crate::core_crypto::prelude::*; +use crate::shortint::backward_compatibility::ciphertext::CompressedCiphertextListVersions; use crate::shortint::parameters::CompressedCiphertextConformanceParams; use crate::shortint::{CarryModulus, MessageModulus}; -#[derive(Clone, serde::Serialize, serde::Deserialize)] +#[derive(Clone, serde::Serialize, serde::Deserialize, Versionize)] +#[versionize(CompressedCiphertextListVersions)] pub struct CompressedCiphertextList { pub modulus_switched_glwe_ciphertext_list: Vec>, pub ciphertext_modulus: CiphertextModulus, diff --git a/tfhe/src/shortint/key_switching_key/mod.rs b/tfhe/src/shortint/key_switching_key/mod.rs index 83ee772064..ffb7a0b7fe 100644 --- a/tfhe/src/shortint/key_switching_key/mod.rs +++ b/tfhe/src/shortint/key_switching_key/mod.rs @@ -19,7 +19,8 @@ use serde::{Deserialize, Serialize}; use tfhe_versionable::Versionize; use super::backward_compatibility::key_switching_key::{ - CompressedKeySwitchingKeyMaterialVersions, KeySwitchingKeyMaterialVersions, + CompressedKeySwitchingKeyMaterialVersions, CompressedKeySwitchingKeyVersions, + KeySwitchingKeyMaterialVersions, KeySwitchingKeyVersions, }; #[cfg(test)] @@ -55,7 +56,8 @@ pub(crate) struct KeySwitchingKeyBuildHelper<'keys> { /// /// The casting key is generated by the client and is meant to be published: the client /// sends it to the server so it can cast from one set of parameters to another. -#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] +#[derive(Clone, Debug, PartialEq, Serialize, Deserialize, Versionize)] +#[versionize(KeySwitchingKeyVersions)] pub struct KeySwitchingKey { pub(crate) key_switching_key_material: KeySwitchingKeyMaterial, pub(crate) dest_server_key: ServerKey, @@ -634,7 +636,8 @@ pub(crate) struct CompressedKeySwitchingKeyBuildHelper<'keys> { /// /// The casting key is generated by the client and is meant to be published: the client /// sends it to the server so it can cast from one set of parameters to another. -#[derive(Clone, Debug, Serialize, Deserialize)] +#[derive(Clone, Debug, Serialize, Deserialize, Versionize)] +#[versionize(CompressedKeySwitchingKeyVersions)] pub struct CompressedKeySwitchingKey { pub(crate) key_switching_key_material: CompressedKeySwitchingKeyMaterial, pub(crate) dest_server_key: CompressedServerKey, diff --git a/tfhe/src/shortint/server_key/mod.rs b/tfhe/src/shortint/server_key/mod.rs index 0d4cc86075..351b0b2cc7 100644 --- a/tfhe/src/shortint/server_key/mod.rs +++ b/tfhe/src/shortint/server_key/mod.rs @@ -19,14 +19,13 @@ mod shift; mod sub; pub mod compressed; -use ::tfhe_versionable::{Unversionize, UnversionizeError, Versionize}; +use ::tfhe_versionable::{Unversionize, UnversionizeError, Versionize, VersionizeOwned}; use aligned_vec::ABox; pub use bivariate_pbs::{ BivariateLookupTableMutView, BivariateLookupTableOwned, BivariateLookupTableView, }; pub use compressed::{CompressedServerKey, ShortintCompressedBootstrappingKey}; pub(crate) use scalar_mul::unchecked_scalar_mul_assign; -use serde::de::DeserializeOwned; #[cfg(test)] pub(crate) mod tests; @@ -157,6 +156,7 @@ pub enum SerializableShortintBootstrappingKey { Classic(FourierLweBootstrapKeyVersioned<'vers>), MultiBit { @@ -166,6 +166,7 @@ pub enum SerializableShortintBootstrappingKeyVersion<'vers> { } #[derive(Serialize, Deserialize)] +#[cfg_attr(tfhe_lints, allow(tfhe_lints::serialize_without_versionize))] pub enum SerializableShortintBootstrappingKeyVersionOwned { Classic(FourierLweBootstrapKeyVersionedOwned), MultiBit { @@ -192,10 +193,10 @@ impl<'vers, C: Container> } } -impl> From<&SerializableShortintBootstrappingKey> +impl> From> for SerializableShortintBootstrappingKeyVersionOwned { - fn from(value: &SerializableShortintBootstrappingKey) -> Self { + fn from(value: SerializableShortintBootstrappingKey) -> Self { match value { SerializableShortintBootstrappingKey::Classic(bsk) => { Self::Classic(bsk.versionize_owned()) @@ -205,7 +206,7 @@ impl> From<&SerializableShortintBootst deterministic_execution, } => Self::MultiBit { fourier_bsk: fourier_bsk.versionize_owned(), - deterministic_execution: *deterministic_execution, + deterministic_execution, }, } } @@ -243,15 +244,19 @@ impl> Versionize fn versionize(&self) -> Self::Versioned<'_> { self.into() } +} +impl> VersionizeOwned + for SerializableShortintBootstrappingKey +{ type VersionedOwned = SerializableShortintBootstrappingKeyVersionedOwned; - fn versionize_owned(&self) -> Self::VersionedOwned { + fn versionize_owned(self) -> Self::VersionedOwned { self.into() } } -impl + Serialize + DeserializeOwned> Unversionize +impl> Unversionize for SerializableShortintBootstrappingKey { fn unversionize(versioned: Self::VersionedOwned) -> Result { @@ -320,12 +325,13 @@ impl Versionize for ShortintBootstrappingKey { fn versionize(&self) -> Self::Versioned<'_> { SerializableShortintBootstrappingKey::from(self).versionize_owned() } +} - type VersionedOwned = > as Versionize>::VersionedOwned; +impl VersionizeOwned for ShortintBootstrappingKey { + type VersionedOwned = > as VersionizeOwned>::VersionedOwned; - fn versionize_owned(&self) -> Self::VersionedOwned { - todo!() - //SerializableShortintBootstrappingKey::from(self).versionize_owned() + fn versionize_owned(self) -> Self::VersionedOwned { + SerializableShortintBootstrappingKey::from(self).versionize_owned() } } diff --git a/utils/cargo-tfhe-lints-inner/Cargo.toml b/utils/cargo-tfhe-lints-inner/Cargo.toml new file mode 100644 index 0000000000..400d708b19 --- /dev/null +++ b/utils/cargo-tfhe-lints-inner/Cargo.toml @@ -0,0 +1,10 @@ +[package] +name = "cargo-tfhe-lints-inner" +version = "0.1.0" +edition = "2021" + +[dependencies] +rustc-tools = "0.78" + +[package.metadata.rust-analyzer] +rustc_private=true diff --git a/utils/cargo-tfhe-lints-inner/README.md b/utils/cargo-tfhe-lints-inner/README.md new file mode 100644 index 0000000000..cde2b7603d --- /dev/null +++ b/utils/cargo-tfhe-lints-inner/README.md @@ -0,0 +1,16 @@ +# TFHE-lints-inner + +Implementation of the lints in the custom [tfhe-lints](../cargo-tfhe-lints/README.md) tool. + +## Installation +``` +cargo install --path . +``` + +## Usage +This can be run directly by specifying the toolchain: +``` +cargo +nightly-2024-05-02 tfhe-lints-inner +``` + +For a more ergonomic usage, see [tfhe-lints](../cargo-tfhe-lints/README.md) diff --git a/utils/cargo-tfhe-lints-inner/rust-toolchain.toml b/utils/cargo-tfhe-lints-inner/rust-toolchain.toml new file mode 100644 index 0000000000..a6a6e0cf31 --- /dev/null +++ b/utils/cargo-tfhe-lints-inner/rust-toolchain.toml @@ -0,0 +1,4 @@ +[toolchain] +channel = "nightly-2024-05-02" +components = ["rustc-dev", "rust-src", "llvm-tools-preview"] +profile = "minimal" diff --git a/utils/cargo-tfhe-lints-inner/src/main.rs b/utils/cargo-tfhe-lints-inner/src/main.rs new file mode 100644 index 0000000000..d4b35995d2 --- /dev/null +++ b/utils/cargo-tfhe-lints-inner/src/main.rs @@ -0,0 +1,54 @@ +#![feature(rustc_private)] +#![warn(clippy::pedantic)] +#![allow(clippy::module_name_repetitions)] +#![allow(clippy::must_use_candidate)] + +mod serialize_without_versionize; +pub mod utils; + +// We need to import them like this otherwise it doesn't work. +extern crate rustc_ast; +extern crate rustc_hir; +extern crate rustc_lint; +extern crate rustc_middle; +extern crate rustc_session; +extern crate rustc_span; + +use rustc_lint::LintStore; +use rustc_tools::with_lints; +use serialize_without_versionize::SerializeWithoutVersionize; + +fn main() { + let tool_args = std::env::args().skip(2).collect::>(); + + let (cargo_args, rustc_args) = if let Some(idx) = tool_args.iter().position(|arg| arg == "--") { + tool_args.split_at(idx) + } else { + (tool_args.as_slice(), &[] as &[String]) + }; + + rustc_tools::cargo_integration(&cargo_args, |args| { + let mut args = args.to_vec(); + args.extend(rustc_args.iter().skip(1).cloned()); + args.extend( + [ + "--emit=metadata", + // These params allows to use the syntax + // `#[cfg_attr(tfhe_lints, allow(tfhe_lints::serialize_without_versionize))]` + "-Zcrate-attr=feature(register_tool)", + "-Zcrate-attr=register_tool(tfhe_lints)", + "--cfg=tfhe_lints", + ] + .iter() + .map(ToString::to_string), + ); + let serialize_without_versionize = SerializeWithoutVersionize::new(); + + with_lints(&args, vec![], move |store: &mut LintStore| { + let lint = serialize_without_versionize.clone(); + store.register_late_pass(move |_| Box::new(lint.clone())); + }) + .expect("with_lints failed"); + }) + .expect("cargo_integration failed"); +} diff --git a/utils/cargo-tfhe-lints-inner/src/serialize_without_versionize.rs b/utils/cargo-tfhe-lints-inner/src/serialize_without_versionize.rs new file mode 100644 index 0000000000..3ccec6fc13 --- /dev/null +++ b/utils/cargo-tfhe-lints-inner/src/serialize_without_versionize.rs @@ -0,0 +1,119 @@ +use std::sync::{Arc, OnceLock}; + +use rustc_hir::def_id::DefId; +use rustc_hir::{Impl, Item, ItemKind}; +use rustc_lint::{LateContext, LateLintPass, LintContext}; +use rustc_session::{declare_tool_lint, impl_lint_pass}; +use rustc_span::sym; + +use crate::utils::{get_def_id_from_ty, is_allowed_lint, symbols_list_from_str}; + +declare_tool_lint! { + pub tfhe_lints::SERIALIZE_WITHOUT_VERSIONIZE, + Warn, + "warns if `Serialize` is implemented without `Versionize`" +} + +#[derive(Default)] +pub struct SerializeWithoutVersionizeInner { + pub versionize_trait: OnceLock>, +} + +const VERSIONIZE_TRAIT: [&str; 2] = ["tfhe_versionable", "Versionize"]; +const SERIALIZE_TRAIT: [&str; 3] = ["serde", "ser", "Serialize"]; +const LINT_NAME: &str = "serialize_without_versionize"; + +impl SerializeWithoutVersionizeInner { + /// Tries to find the definition of the `Versionize` trait. The value is memoized and is + /// instantly accessed after the first lookup. + pub fn versionize_trait(&self, cx: &LateContext<'_>) -> Option { + self.versionize_trait + .get_or_init(|| { + let versionize_trait = cx.tcx.all_traits().find(|def_id| { + cx.match_def_path(*def_id, symbols_list_from_str(&VERSIONIZE_TRAIT).as_slice()) + }); + + versionize_trait + }) + .to_owned() + } +} + +#[derive(Default, Clone)] +pub struct SerializeWithoutVersionize(pub Arc); + +impl SerializeWithoutVersionize { + pub fn new() -> Self { + Self::default() + } +} + +impl_lint_pass!(SerializeWithoutVersionize => [SERIALIZE_WITHOUT_VERSIONIZE]); + +impl<'tcx> LateLintPass<'tcx> for SerializeWithoutVersionize { + fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'_>) { + // If the currently checked item is a trait impl + if let ItemKind::Impl(Impl { + of_trait: Some(ref trait_ref), + .. + }) = item.kind + { + // Gets the target type of the implementation + let ty: rustc_middle::ty::Ty<'tcx> = + cx.tcx.type_of(item.owner_id).instantiate_identity(); + + if let Some(type_def_id) = get_def_id_from_ty(ty) { + // If the type has been automatically generated, skip it + if cx.tcx.has_attr(type_def_id, sym::automatically_derived) { + return; + } + + // Skip it if the user explicitly allowed it. + if is_allowed_lint(cx, type_def_id, LINT_NAME) { + return; + } + + // Check if the implemented trait is `Serialize` + if let Some(versionize_trait) = self.0.versionize_trait(cx) { + if let Some(def_id) = trait_ref.trait_def_id() { + if cx.match_def_path( + def_id, + symbols_list_from_str(&SERIALIZE_TRAIT).as_slice(), + ) { + // Try to find an implementation of versionize for this type + let mut found_impl = false; + cx.tcx + .for_each_relevant_impl(versionize_trait, ty, |impl_id| { + if !found_impl { + let trait_ref = cx + .tcx + .impl_trait_ref(impl_id) + .expect("must be a trait implementation"); + + if trait_ref.instantiate_identity().args.type_at(0) == ty { + found_impl = true; + } + } + }); + + if !found_impl { + // Emit a warning + cx.span_lint( + SERIALIZE_WITHOUT_VERSIONIZE, + cx.tcx.def_span(type_def_id), + format!( + "Type {ty} implements `Serialize` but does not implement `Versionize`" + ),|diag| { + diag.note("Add `#[derive(Versionize)] for this type or silence this warning using \ +`#[cfg_attr(tfhe_lints, allow(tfhe_lints::serialize_without_versionize))]``"); + diag.span_note(item.span, "`Serialize` derived here"); + }, + ); + } + } + } + } + } + } + } +} diff --git a/utils/cargo-tfhe-lints-inner/src/utils.rs b/utils/cargo-tfhe-lints-inner/src/utils.rs new file mode 100644 index 0000000000..f16b0763a1 --- /dev/null +++ b/utils/cargo-tfhe-lints-inner/src/utils.rs @@ -0,0 +1,52 @@ +use rustc_ast::tokenstream::TokenTree; +use rustc_hir::def_id::DefId; +use rustc_lint::LateContext; +use rustc_middle::ty::{Ty, TyKind}; +use rustc_span::Symbol; + +const TOOL_NAME: &str = "tfhe_lints"; + +/// Converts an array of str into a Vec of [`Symbol`] +pub fn symbols_list_from_str(list: &[&str]) -> Vec { + list.iter().map(|s| Symbol::intern(s)).collect() +} + +/// Checks if the lint is allowed for the item represented by [`DefId`]. +/// This shouldn't be necessary since the lints are declared with the +/// `declare_tool_lint` macro but for a mysterious reason this does not +/// work automatically. +pub fn is_allowed_lint(cx: &LateContext<'_>, target: DefId, lint_name: &str) -> bool { + for attr in cx.tcx.get_attrs(target, Symbol::intern("allow")) { + let tokens = attr.get_normal_item().args.inner_tokens(); + let mut trees = tokens.trees(); + + if let Some(TokenTree::Token(tool_token, _)) = trees.next() { + if tool_token.is_ident_named(Symbol::intern(TOOL_NAME)) { + trees.next(); + if let Some(TokenTree::Token(tool_token, _)) = trees.next() { + if tool_token.is_ident_named(Symbol::intern(lint_name)) { + return true; + } + } + } + } + } + + false +} + +/// Gets the [`DefId`] of a type +pub fn get_def_id_from_ty(ty: Ty<'_>) -> Option { + match ty.kind() { + TyKind::Adt(adt_def, _) => Some(adt_def.did()), + TyKind::Alias(_, alias_ty) => Some(alias_ty.def_id), + TyKind::Dynamic(predicates, ..) => predicates.principal_def_id(), + TyKind::FnDef(def_id, _) + | TyKind::Foreign(def_id) + | TyKind::Closure(def_id, ..) + | TyKind::CoroutineClosure(def_id, _) + | TyKind::Coroutine(def_id, _) + | TyKind::CoroutineWitness(def_id, _) => Some(*def_id), + _ => None, + } +} diff --git a/utils/cargo-tfhe-lints/Cargo.toml b/utils/cargo-tfhe-lints/Cargo.toml new file mode 100644 index 0000000000..c2c779838f --- /dev/null +++ b/utils/cargo-tfhe-lints/Cargo.toml @@ -0,0 +1,8 @@ +[package] +name = "cargo-tfhe-lints" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] diff --git a/utils/cargo-tfhe-lints/README.md b/utils/cargo-tfhe-lints/README.md new file mode 100644 index 0000000000..91d98df960 --- /dev/null +++ b/utils/cargo-tfhe-lints/README.md @@ -0,0 +1,25 @@ +# TFHE-lints + +A collection of rust lints specific to the **TFHE-rs** project. This tool is built using [rustc-tools](https://github.com/GuillaumeGomez/rustc-tools). + +## List of lints +- `serilaize_without_versionize`: warns if `Serialize` is implemented without `Versionize` + +## Installation + +### Install the inner tool +``` +cargo install --path ../cargo-tfhe-lints-inner +``` + +### Install this wrapper +``` +cargo install --path . +``` + +## Usage +Use it as any other cargo tool: +``` +cargo tfhe-lints +``` +You can specify features like you would do with clippy. diff --git a/utils/cargo-tfhe-lints/src/main.rs b/utils/cargo-tfhe-lints/src/main.rs new file mode 100644 index 0000000000..47bdc57e40 --- /dev/null +++ b/utils/cargo-tfhe-lints/src/main.rs @@ -0,0 +1,38 @@ +use std::process::{exit, Command}; + +fn get_supported_rustc_version() -> &'static str { + const TOOLCHAIN_FILE: &str = include_str!("../../cargo-tfhe-lints-inner/rust-toolchain.toml"); + + TOOLCHAIN_FILE + .lines() + .find(|line| line.starts_with("channel")) + .and_then(|line| { + line.rsplit('=') + .next() + .unwrap() + .trim() + .strip_prefix('"') + .unwrap() + .strip_suffix('"') + }) + .unwrap() +} + +fn main() { + let cargo_args = std::env::args().skip(2).collect::>(); + let toolchain = format!("+{}", get_supported_rustc_version()); + + if let Err(err) = Command::new("cargo") + .arg(toolchain.as_str()) + .arg("tfhe-lints-inner") + .args(&cargo_args) + .spawn() + .and_then(|mut child| child.wait()) + { + eprintln!( + "Command `cargo {toolchain} tfhe-lints-inner {}` failed: {err:?}", + cargo_args.join(" "), + ); + exit(1); + } +} diff --git a/utils/tfhe-versionable-derive/Cargo.toml b/utils/tfhe-versionable-derive/Cargo.toml index 9d12a5227d..a0889dde64 100644 --- a/utils/tfhe-versionable-derive/Cargo.toml +++ b/utils/tfhe-versionable-derive/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "tfhe-versionable-derive" -version = "0.1.0" +version = "0.2.0" edition = "2021" keywords = ["versioning", "serialization", "encoding", "proc-macro", "derive"] homepage = "https://zama.ai/" diff --git a/utils/tfhe-versionable-derive/src/associated.rs b/utils/tfhe-versionable-derive/src/associated.rs index e3e4ddd1dd..98971ae510 100644 --- a/utils/tfhe-versionable-derive/src/associated.rs +++ b/utils/tfhe-versionable-derive/src/associated.rs @@ -27,6 +27,7 @@ pub(crate) fn generate_from_trait_impl( ) -> syn::Result { let from_variable = Ident::new(from_variable_name, Span::call_site()); Ok(parse_quote! { + #[automatically_derived] impl #impl_generics From<#src> for #dest #where_clause { fn from(#from_variable: #src) -> Self { #constructor @@ -55,6 +56,7 @@ pub(crate) fn generate_try_from_trait_impl( ) -> syn::Result { let from_variable = Ident::new(from_variable_name, Span::call_site()); Ok(parse_quote! { + #[automatically_derived] impl #impl_generics TryFrom<#src> for #dest #where_clause { type Error = #error; fn try_from(#from_variable: #src) -> Result { diff --git a/utils/tfhe-versionable-derive/src/dispatch_type.rs b/utils/tfhe-versionable-derive/src/dispatch_type.rs index 5f6040a216..3e6725bbb1 100644 --- a/utils/tfhe-versionable-derive/src/dispatch_type.rs +++ b/utils/tfhe-versionable-derive/src/dispatch_type.rs @@ -94,6 +94,7 @@ impl AssociatedType for DispatchType { Ok(ItemEnum { ident: self.ident(), generics: self.generics()?, + attrs: vec![parse_quote! { #[automatically_derived] }], variants: variants?, ..self.orig_type.clone() } @@ -144,7 +145,7 @@ impl AssociatedType for DispatchType { // Wraps the highest version into the dispatch enum let src_type = self.latest_version_type()?; - let src = parse_quote! { &#src_type }; + let src = parse_quote! { #src_type }; let dest_ident = self.ident(); let dest = parse_quote! { #dest_ident #ty_generics }; let constructor = self.generate_conversion_constructor_ref("value")?; @@ -335,7 +336,7 @@ impl DispatchType { value .upgrade() .map_err(|e| - #error_ty::upgrade(#src_variant, #dest_variant, &e) + #error_ty::upgrade(#src_variant, #dest_variant, e) ) }) } diff --git a/utils/tfhe-versionable-derive/src/lib.rs b/utils/tfhe-versionable-derive/src/lib.rs index 7e3c6058bf..1b9dead2f2 100644 --- a/utils/tfhe-versionable-derive/src/lib.rs +++ b/utils/tfhe-versionable-derive/src/lib.rs @@ -34,8 +34,10 @@ pub(crate) const LIFETIME_NAME: &str = "'vers"; pub(crate) const VERSION_TRAIT_NAME: &str = crate_full_path!("Version"); pub(crate) const DISPATCH_TRAIT_NAME: &str = crate_full_path!("VersionsDispatch"); pub(crate) const VERSIONIZE_TRAIT_NAME: &str = crate_full_path!("Versionize"); -pub(crate) const UNVERSIONIZE_TRAIT_NAME: &str = crate_full_path!("Unversionize"); +pub(crate) const VERSIONIZE_OWNED_TRAIT_NAME: &str = crate_full_path!("VersionizeOwned"); +pub(crate) const VERSIONIZE_SLICE_TRAIT_NAME: &str = crate_full_path!("VersionizeSlice"); pub(crate) const VERSIONIZE_VEC_TRAIT_NAME: &str = crate_full_path!("VersionizeVec"); +pub(crate) const UNVERSIONIZE_TRAIT_NAME: &str = crate_full_path!("Unversionize"); pub(crate) const UNVERSIONIZE_VEC_TRAIT_NAME: &str = crate_full_path!("UnversionizeVec"); pub(crate) const UNVERSIONIZE_ERROR_NAME: &str = crate_full_path!("UnversionizeError"); @@ -83,6 +85,8 @@ fn impl_version_trait(input: &DeriveInput) -> proc_macro2::TokenStream { quote! { const _: () = { #version_types + + #[automatically_derived] #version_impl }; } @@ -101,6 +105,7 @@ pub fn derive_versions_dispatch(input: TokenStream) -> TokenStream { &[ VERSIONIZE_TRAIT_NAME, VERSIONIZE_VEC_TRAIT_NAME, + VERSIONIZE_SLICE_TRAIT_NAME, UNVERSIONIZE_TRAIT_NAME, UNVERSIONIZE_VEC_TRAIT_NAME, SERIALIZE_TRAIT_NAME, @@ -116,6 +121,8 @@ pub fn derive_versions_dispatch(input: TokenStream) -> TokenStream { quote! { const _: () = { #dispatch_types + + #[automatically_derived] #dispatch_impl }; } @@ -183,8 +190,10 @@ pub fn derive_versionize(input: TokenStream) -> TokenStream { )); let versionize_trait: Path = parse_const_str(VERSIONIZE_TRAIT_NAME); + let versionize_owned_trait: Path = parse_const_str(VERSIONIZE_OWNED_TRAIT_NAME); let unversionize_trait: Path = parse_const_str(UNVERSIONIZE_TRAIT_NAME); let versionize_vec_trait: Path = parse_const_str(VERSIONIZE_VEC_TRAIT_NAME); + let versionize_slice_trait: Path = parse_const_str(VERSIONIZE_SLICE_TRAIT_NAME); let unversionize_vec_trait: Path = parse_const_str(UNVERSIONIZE_VEC_TRAIT_NAME); let mut versionize_generics = trait_generics.clone(); @@ -199,10 +208,16 @@ pub fn derive_versionize(input: TokenStream) -> TokenStream { } // Add Generics for the `VersionizeVec` and `UnversionizeVec` traits + let mut versionize_slice_generics = versionize_generics.clone(); + syn_unwrap!(add_trait_bound( + &mut versionize_slice_generics, + VERSIONIZE_TRAIT_NAME + )); + let mut versionize_vec_generics = versionize_generics.clone(); syn_unwrap!(add_trait_bound( &mut versionize_vec_generics, - VERSIONIZE_TRAIT_NAME + VERSIONIZE_OWNED_TRAIT_NAME )); let mut unversionize_vec_generics = unversionize_generics.clone(); syn_unwrap!(add_trait_bound( @@ -217,6 +232,8 @@ pub fn derive_versionize(input: TokenStream) -> TokenStream { let (unversionize_impl_generics, _, unversionize_where_clause) = unversionize_generics.split_for_impl(); + let (versionize_slice_impl_generics, _, versionize_slice_where_clause) = + versionize_slice_generics.split_for_impl(); let (versionize_vec_impl_generics, _, versionize_vec_where_clause) = versionize_vec_generics.split_for_impl(); let (unversionize_vec_impl_generics, _, unversionize_vec_where_clause) = @@ -238,6 +255,7 @@ pub fn derive_versionize(input: TokenStream) -> TokenStream { quote! { #version_trait_impl + #[automatically_derived] impl #versionize_impl_generics #versionize_trait for #input_ident #ty_generics #versionize_where_clause { @@ -248,17 +266,22 @@ pub fn derive_versionize(input: TokenStream) -> TokenStream { fn versionize(&self) -> Self::Versioned<'_> { #versionize_body } + } - fn versionize_owned(&self) -> Self::VersionedOwned { - #versionize_body - } - - type VersionedOwned = + #[automatically_derived] + impl #versionize_impl_generics #versionize_owned_trait for #input_ident #ty_generics + #versionize_where_clause + { + type VersionedOwned = <#dispatch_enum_path #dispatch_generics as #dispatch_trait<#dispatch_target>>::Owned #owned_where_clause; + fn versionize_owned(self) -> Self::VersionedOwned { + #versionize_body + } } + #[automatically_derived] impl #unversionize_impl_generics #unversionize_trait for #input_ident #ty_generics #unversionize_where_clause { @@ -267,22 +290,29 @@ pub fn derive_versionize(input: TokenStream) -> TokenStream { } } - impl #versionize_vec_impl_generics #versionize_vec_trait for #input_ident #ty_generics - #versionize_vec_where_clause + #[automatically_derived] + impl #versionize_slice_impl_generics #versionize_slice_trait for #input_ident #ty_generics + #versionize_slice_where_clause { type VersionedSlice<#lifetime> = Vec<::Versioned<#lifetime>> #ref_where_clause; fn versionize_slice(slice: &[Self]) -> Self::VersionedSlice<'_> { - slice.iter().map(|val| val.versionize()).collect() + slice.iter().map(|val| #versionize_trait::versionize(val)).collect() } + } + + impl #versionize_vec_impl_generics #versionize_vec_trait for #input_ident #ty_generics + #versionize_vec_where_clause + { - type VersionedVec = Vec<::VersionedOwned> #owned_where_clause; + type VersionedVec = Vec<::VersionedOwned> #owned_where_clause; - fn versionize_vec(slice: &[Self]) -> Self::VersionedVec { - slice.iter().map(|val| val.versionize_owned()).collect() + fn versionize_vec(vec: Vec) -> Self::VersionedVec { + vec.into_iter().map(|val| #versionize_owned_trait::versionize_owned(val)).collect() } } + #[automatically_derived] impl #unversionize_vec_impl_generics #unversionize_vec_trait for #input_ident #ty_generics #unversionize_vec_where_clause { fn unversionize_vec(versioned: Self::VersionedVec) -> Result, #unversionize_error> { @@ -313,30 +343,38 @@ pub fn derive_not_versioned(input: TokenStream) -> TokenStream { let input_ident = &input.ident; let versionize_trait: Path = parse_const_str(VERSIONIZE_TRAIT_NAME); + let versionize_owned_trait: Path = parse_const_str(VERSIONIZE_OWNED_TRAIT_NAME); let unversionize_trait: Path = parse_const_str(UNVERSIONIZE_TRAIT_NAME); let unversionize_error: Path = parse_const_str(UNVERSIONIZE_ERROR_NAME); let lifetime = Lifetime::new(LIFETIME_NAME, Span::call_site()); quote! { + #[automatically_derived] impl #impl_generics #versionize_trait for #input_ident #ty_generics #where_clause { type Versioned<#lifetime> = &#lifetime Self; - type VersionedOwned = Self; fn versionize(&self) -> Self::Versioned<'_> { self } + } - fn versionize_owned(&self) -> Self::VersionedOwned { - self.clone() + #[automatically_derived] + impl #impl_generics #versionize_owned_trait for #input_ident #ty_generics #where_clause { + type VersionedOwned = Self; + + fn versionize_owned(self) -> Self::VersionedOwned { + self } } + #[automatically_derived] impl #impl_generics #unversionize_trait for #input_ident #ty_generics #where_clause { fn unversionize(versioned: Self::VersionedOwned) -> Result { Ok(versioned) } } + #[automatically_derived] impl NotVersioned for #input_ident #ty_generics #where_clause {} } diff --git a/utils/tfhe-versionable-derive/src/version_type.rs b/utils/tfhe-versionable-derive/src/version_type.rs index 931da963d8..1c31877ff5 100644 --- a/utils/tfhe-versionable-derive/src/version_type.rs +++ b/utils/tfhe-versionable-derive/src/version_type.rs @@ -18,7 +18,7 @@ use crate::associated::{ use crate::{ add_lifetime_bound, add_trait_where_clause, parse_const_str, parse_trait_bound, punctuated_from_iter_result, LIFETIME_NAME, UNVERSIONIZE_ERROR_NAME, UNVERSIONIZE_TRAIT_NAME, - VERSIONIZE_TRAIT_NAME, + VERSIONIZE_OWNED_TRAIT_NAME, VERSIONIZE_TRAIT_NAME, }; /// The types generated for a specific version of a given exposed type. These types are identical to @@ -132,7 +132,7 @@ impl AssociatedType for VersionType { let (impl_generics, ty_generics, where_clause) = generics.split_for_impl(); let src_ident = self.orig_type.ident.clone(); - let src = parse_quote! { &#src_ident #orig_generics }; + let src = parse_quote! { #src_ident #orig_generics }; let dest_ident = self.ident(); let dest = parse_quote! { #dest_ident #ty_generics }; let constructor = self.generate_conversion_constructor( @@ -193,12 +193,19 @@ impl VersionType { fn type_generics(&self) -> syn::Result { let mut generics = self.orig_type.generics.clone(); - if let AssociatedTypeKind::Ref(Some(lifetime)) = &self.kind { - add_lifetime_bound(&mut generics, lifetime); + if let AssociatedTypeKind::Ref(opt_lifetime) = &self.kind { + if let Some(lifetime) = opt_lifetime { + add_lifetime_bound(&mut generics, lifetime); + } + add_trait_where_clause(&mut generics, self.inner_types()?, &[VERSIONIZE_TRAIT_NAME])?; + } else { + add_trait_where_clause( + &mut generics, + self.inner_types()?, + &[VERSIONIZE_OWNED_TRAIT_NAME], + )?; } - add_trait_where_clause(&mut generics, self.inner_types()?, &[VERSIONIZE_TRAIT_NAME])?; - Ok(generics) } @@ -230,7 +237,7 @@ impl VersionType { fields, ident: self.ident(), vis: self.orig_type.vis.clone(), - attrs: Vec::new(), + attrs: vec![parse_quote! { #[automatically_derived] }], generics: self.type_generics()?, struct_token: stru.struct_token, semi_token: stru.semi_token, @@ -257,7 +264,7 @@ impl VersionType { let versioned_enu = ItemEnum { ident: self.ident(), vis: self.orig_type.vis.clone(), - attrs: Vec::new(), + attrs: vec![parse_quote! { #[automatically_derived] }], generics: self.type_generics()?, enum_token: enu.enum_token, brace_token: enu.brace_token, @@ -275,7 +282,7 @@ impl VersionType { fields, ident: self.ident(), vis: self.orig_type.vis.clone(), - attrs: Vec::new(), + attrs: vec![parse_quote! { #[automatically_derived] }], generics: self.type_generics()?, union_token: uni.union_token, }; @@ -327,13 +334,14 @@ impl VersionType { let unver_ty = field.ty.clone(); let versionize_trait = parse_trait_bound(VERSIONIZE_TRAIT_NAME)?; + let versionize_owned_trait = parse_trait_bound(VERSIONIZE_OWNED_TRAIT_NAME)?; let ty: Type = match &kind { AssociatedTypeKind::Ref(lifetime) => parse_quote! { <#unver_ty as #versionize_trait>::Versioned<#lifetime> }, AssociatedTypeKind::Owned => parse_quote! { - <#unver_ty as #versionize_trait>::VersionedOwned + <#unver_ty as #versionize_owned_trait>::VersionedOwned }, }; @@ -632,22 +640,24 @@ impl VersionType { direction: ConversionDirection, ) -> syn::Result { let versionize_trait: Path = parse_const_str(VERSIONIZE_TRAIT_NAME); + let versionize_owned_trait: Path = parse_const_str(VERSIONIZE_OWNED_TRAIT_NAME); let unversionize_trait: Path = parse_const_str(UNVERSIONIZE_TRAIT_NAME); let field_constructor = match direction { ConversionDirection::OrigToAssociated => { - let param = if is_ref { - field_param - } else { - quote! {&#field_param} - }; - match self.kind { - AssociatedTypeKind::Ref(_) => quote! { - #versionize_trait::versionize(#param) - }, + AssociatedTypeKind::Ref(_) => { + let param = if is_ref { + field_param + } else { + quote! {&#field_param} + }; + + quote! { + #versionize_trait::versionize(#param) + }}, AssociatedTypeKind::Owned => quote! { - #versionize_trait::versionize_owned(#param) + #versionize_owned_trait::versionize_owned(#field_param) }, } } diff --git a/utils/tfhe-versionable-derive/src/versionize_attribute.rs b/utils/tfhe-versionable-derive/src/versionize_attribute.rs index 63c26620b7..9a4a529e08 100644 --- a/utils/tfhe-versionable-derive/src/versionize_attribute.rs +++ b/utils/tfhe-versionable-derive/src/versionize_attribute.rs @@ -3,10 +3,11 @@ use quote::{quote, ToTokens}; use syn::punctuated::Punctuated; use syn::spanned::Spanned; use syn::{ - Attribute, Expr, ExprLit, Ident, Lit, Meta, MetaNameValue, Path, Token, Type, TypeParam, + Attribute, Expr, ExprLit, Ident, Lit, Meta, MetaNameValue, Path, Token, TraitBound, Type, + TypeParam, }; -use crate::{parse_const_str, UNVERSIONIZE_ERROR_NAME}; +use crate::{parse_const_str, UNVERSIONIZE_ERROR_NAME, VERSIONIZE_OWNED_TRAIT_NAME}; /// Name of the attribute used to give arguments to our macros const VERSIONIZE_ATTR_NAME: &str = "versionize"; @@ -184,11 +185,12 @@ impl VersionizeAttribute { } pub(crate) fn versionize_method_body(&self) -> proc_macro2::TokenStream { + let versionize_owned_trait: TraitBound = parse_const_str(VERSIONIZE_OWNED_TRAIT_NAME); self.into .as_ref() .map(|target| { quote! { - #target::from(self.to_owned()).versionize_owned() + #versionize_owned_trait::versionize_owned(#target::from(self.to_owned())) } }) .unwrap_or_else(|| { @@ -205,7 +207,7 @@ impl VersionizeAttribute { } else if let Some(target) = &self.try_from { let target_name = format!("{}", target.to_token_stream()); quote! { #target::unversionize(#arg_name).and_then(|value| TryInto::::try_into(value) - .map_err(|e| #error::conversion(#target_name, &format!("{}", e)))) + .map_err(|e| #error::conversion(#target_name, e))) } } else { quote! { #arg_name.try_into() } diff --git a/utils/tfhe-versionable/Cargo.toml b/utils/tfhe-versionable/Cargo.toml index 644219d2fe..cc8b6efab2 100644 --- a/utils/tfhe-versionable/Cargo.toml +++ b/utils/tfhe-versionable/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "tfhe-versionable" -version = "0.1.0" +version = "0.2.0" edition = "2021" keywords = ["versioning", "serialization", "encoding"] homepage = "https://zama.ai/" @@ -8,6 +8,7 @@ documentation = "https://docs.rs/tfhe_versionable" repository = "https://github.com/zama-ai/tfhe-rs" license = "BSD-3-Clause-Clear" description = "tfhe-versionable: Add versioning informations/backward compatibility on rust types used for serialization" +rust-version = "1.76" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html @@ -25,6 +26,6 @@ toml = "0.8" [dependencies] serde = { version = "1.0", features = ["derive"] } -tfhe-versionable-derive = { version = "0.1.0", path = "../tfhe-versionable-derive" } +tfhe-versionable-derive = { version = "0.2.0", path = "../tfhe-versionable-derive" } num-complex = { version = "0.4", features = ["serde"] } aligned-vec = { version = "0.5", features = ["serde"] } diff --git a/utils/tfhe-versionable/README.md b/utils/tfhe-versionable/README.md index 1e88b0d477..3ff1cf0675 100644 --- a/utils/tfhe-versionable/README.md +++ b/utils/tfhe-versionable/README.md @@ -1,6 +1,6 @@ # TFHE-versionable This crate provides type level versioning for serialized data. It offers a way to add backward -compatibility on any data type. The versioning scheme works recursively and is idependant of the +compatibility on any data type. The versioning scheme works recursively and is independant of the chosen serialization backend. To use it, simply define an enum that have a variant for each version of your target type. diff --git a/utils/tfhe-versionable/examples/bounds.rs b/utils/tfhe-versionable/examples/bounds.rs index 18e853b04d..dddfdfa085 100644 --- a/utils/tfhe-versionable/examples/bounds.rs +++ b/utils/tfhe-versionable/examples/bounds.rs @@ -3,23 +3,27 @@ use serde::de::DeserializeOwned; use serde::Serialize; -use tfhe_versionable::{Unversionize, UnversionizeError, Versionize, VersionsDispatch}; +use tfhe_versionable::{ + Unversionize, UnversionizeError, Versionize, VersionizeOwned, VersionsDispatch, +}; // Example of a simple struct with a manual Versionize impl that requires a specific bound struct MyStruct { val: T, } -impl> Versionize for MyStruct { +impl Versionize for MyStruct { type Versioned<'vers> = &'vers T where T: 'vers; fn versionize(&self) -> Self::Versioned<'_> { &self.val } +} +impl> VersionizeOwned for MyStruct { type VersionedOwned = T; - fn versionize_owned(&self) -> Self::VersionedOwned { + fn versionize_owned(self) -> Self::VersionedOwned { self.val.to_owned() } } diff --git a/utils/tfhe-versionable/examples/failed_upgrade.rs b/utils/tfhe-versionable/examples/failed_upgrade.rs index 806641f2e3..7ae4a19e24 100644 --- a/utils/tfhe-versionable/examples/failed_upgrade.rs +++ b/utils/tfhe-versionable/examples/failed_upgrade.rs @@ -37,6 +37,9 @@ mod v1 { pub struct MyStruct(pub u32); mod backward_compat { + use std::error::Error; + use std::fmt::Display; + use tfhe_versionable::{Upgrade, Version, VersionsDispatch}; use super::MyStruct; @@ -44,11 +47,23 @@ mod v1 { #[derive(Version)] pub struct MyStructV0(pub Option); + #[derive(Debug, Clone)] + pub struct EmptyValueError; + + impl Display for EmptyValueError { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "Value is empty") + } + } + + impl Error for EmptyValueError {} + impl Upgrade for MyStructV0 { - fn upgrade(self) -> Result { + type Error = EmptyValueError; + fn upgrade(self) -> Result { match self.0 { Some(val) => Ok(MyStruct(val)), - None => Err("Cannot convert from empty \"MyStructV0\"".to_string()), + None => Err(EmptyValueError), } } } diff --git a/utils/tfhe-versionable/examples/manual_impl.rs b/utils/tfhe-versionable/examples/manual_impl.rs index e80683b2a5..26283083ad 100644 --- a/utils/tfhe-versionable/examples/manual_impl.rs +++ b/utils/tfhe-versionable/examples/manual_impl.rs @@ -1,8 +1,10 @@ //! The simple example, with manual implementation of the versionize trait +use std::convert::Infallible; + use serde::de::DeserializeOwned; use serde::{Deserialize, Serialize}; -use tfhe_versionable::{Unversionize, UnversionizeError, Upgrade, Versionize}; +use tfhe_versionable::{Unversionize, UnversionizeError, Upgrade, Versionize, VersionizeOwned}; struct MyStruct { attr: T, @@ -15,7 +17,9 @@ struct MyStructV0 { } impl Upgrade> for MyStructV0 { - fn upgrade(self) -> Result, String> { + type Error = Infallible; + + fn upgrade(self) -> Result, Self::Error> { Ok(MyStruct { attr: T::default(), builtin: self.builtin, @@ -30,7 +34,7 @@ struct MyStructVersion<'vers, T: 'vers + Default + Versionize> { } #[derive(Serialize, Deserialize)] -struct MyStructVersionOwned { +struct MyStructVersionOwned { attr: T::VersionedOwned, builtin: u32, } @@ -47,10 +51,12 @@ impl Versionize for MySt }; MyStructVersionsDispatch::V1(ver) } +} +impl VersionizeOwned for MyStruct { type VersionedOwned = MyStructVersionsDispatchOwned; - fn versionize_owned(&self) -> Self::VersionedOwned { + fn versionize_owned(self) -> Self::VersionedOwned { let ver = MyStructVersionOwned { attr: self.attr.versionize_owned(), builtin: self.builtin, @@ -59,14 +65,14 @@ impl Versionize for MySt } } -impl Unversionize +impl Unversionize for MyStruct { fn unversionize(versioned: Self::VersionedOwned) -> Result { match versioned { MyStructVersionsDispatchOwned::V0(v0) => v0 .upgrade() - .map_err(|e| UnversionizeError::upgrade("V0", "V1", &e)), + .map_err(|e| UnversionizeError::upgrade("V0", "V1", e)), MyStructVersionsDispatchOwned::V1(v1) => Ok(Self { attr: T::unversionize(v1.attr)?, builtin: v1.builtin, @@ -83,7 +89,7 @@ enum MyStructVersionsDispatch<'vers, T: 'vers + Default + Versionize> { } #[derive(Serialize, Deserialize)] -enum MyStructVersionsDispatchOwned { +enum MyStructVersionsDispatchOwned { V0(MyStructV0), V1(MyStructVersionOwned), } diff --git a/utils/tfhe-versionable/examples/recursive.rs b/utils/tfhe-versionable/examples/recursive.rs index 8b37fcf952..76202a8379 100644 --- a/utils/tfhe-versionable/examples/recursive.rs +++ b/utils/tfhe-versionable/examples/recursive.rs @@ -1,5 +1,7 @@ //! An example of recursive versioning +use std::convert::Infallible; + use tfhe_versionable::{Upgrade, Version, Versionize, VersionsDispatch}; #[derive(Versionize)] @@ -15,7 +17,9 @@ struct MyStructInnerV0 { } impl Upgrade> for MyStructInnerV0 { - fn upgrade(self) -> Result, String> { + type Error = Infallible; + + fn upgrade(self) -> Result, Self::Error> { Ok(MyStructInner { attr: T::default(), builtin: 0, diff --git a/utils/tfhe-versionable/examples/simple.rs b/utils/tfhe-versionable/examples/simple.rs index a2f233e731..a9995ff5c7 100644 --- a/utils/tfhe-versionable/examples/simple.rs +++ b/utils/tfhe-versionable/examples/simple.rs @@ -1,5 +1,7 @@ //! Shows a basic usage of this crate +use std::convert::Infallible; + use tfhe_versionable::{Unversionize, Upgrade, Version, Versionize, VersionsDispatch}; // The structure that should be versioned, as defined in your code @@ -21,7 +23,9 @@ struct MyStructV0 { // The Upgrade trait tells how to go from the first version to the last. During unversioning, the // upgrade method will be called on the deserialized value enough times to go to the last variant. impl Upgrade> for MyStructV0 { - fn upgrade(self) -> Result, String> { + type Error = Infallible; + + fn upgrade(self) -> Result, Self::Error> { Ok(MyStruct { attr: T::default(), builtin: self.builtin, diff --git a/utils/tfhe-versionable/examples/upgrades.rs b/utils/tfhe-versionable/examples/upgrades.rs index fb931bdb69..b9019c6199 100644 --- a/utils/tfhe-versionable/examples/upgrades.rs +++ b/utils/tfhe-versionable/examples/upgrades.rs @@ -37,6 +37,8 @@ mod v1 { pub struct MyStruct(pub u32, pub T); mod backward_compat { + use std::convert::Infallible; + use tfhe_versionable::{Upgrade, Version, VersionsDispatch}; use super::MyStruct; @@ -45,7 +47,9 @@ mod v1 { pub struct MyStructV0(pub u32); impl Upgrade> for MyStructV0 { - fn upgrade(self) -> Result, String> { + type Error = Infallible; + + fn upgrade(self) -> Result, Self::Error> { Ok(MyStruct(self.0, T::default())) } } @@ -81,6 +85,8 @@ mod v2 { } mod backward_compat { + use std::convert::Infallible; + use tfhe_versionable::{Upgrade, Version, VersionsDispatch}; use super::{MyEnum, MyStruct}; @@ -89,7 +95,9 @@ mod v2 { pub struct MyStructV0(pub u32); impl Upgrade> for MyStructV0 { - fn upgrade(self) -> Result, String> { + type Error = Infallible; + + fn upgrade(self) -> Result, Self::Error> { Ok(MyStructV1(self.0, T::default())) } } @@ -98,7 +106,9 @@ mod v2 { pub struct MyStructV1(pub u32, pub T); impl Upgrade> for MyStructV1 { - fn upgrade(self) -> Result, String> { + type Error = Infallible; + + fn upgrade(self) -> Result, Self::Error> { Ok(MyStruct { count: self.0, attr: T::default(), diff --git a/utils/tfhe-versionable/src/derived_traits.rs b/utils/tfhe-versionable/src/derived_traits.rs index dedd37b25e..84372a712a 100644 --- a/utils/tfhe-versionable/src/derived_traits.rs +++ b/utils/tfhe-versionable/src/derived_traits.rs @@ -11,10 +11,7 @@ pub trait Version: Sized { type Ref<'vers>: From<&'vers Self> + Serialize where Self: 'vers; - type Owned: for<'vers> From<&'vers Self> - + TryInto - + DeserializeOwned - + Serialize; + type Owned: From + TryInto + DeserializeOwned + Serialize; } /// This trait is implemented on the dispatch enum for a given type. The dispatch enum @@ -24,7 +21,7 @@ pub trait VersionsDispatch: Sized { type Ref<'vers>: From<&'vers Unversioned> + Serialize where Unversioned: 'vers; - type Owned: for<'vers> From<&'vers Unversioned> + type Owned: From + TryInto + DeserializeOwned + Serialize; diff --git a/utils/tfhe-versionable/src/lib.rs b/utils/tfhe-versionable/src/lib.rs index f7101d09d0..b163fa17f3 100644 --- a/utils/tfhe-versionable/src/lib.rs +++ b/utils/tfhe-versionable/src/lib.rs @@ -12,8 +12,10 @@ pub mod upgrade; use aligned_vec::{ABox, AVec}; use num_complex::Complex; use std::convert::Infallible; +use std::error::Error; use std::fmt::Display; use std::marker::PhantomData; +use std::sync::Arc; pub use derived_traits::{Version, VersionsDispatch}; pub use upgrade::Upgrade; @@ -34,28 +36,32 @@ pub trait Versionize { /// Wraps the object into a versioned enum with a variant for each version. This will /// use references on the underlying types if possible. fn versionize(&self) -> Self::Versioned<'_>; +} +pub trait VersionizeOwned { type VersionedOwned: Serialize + DeserializeOwned; /// Wraps the object into a versioned enum with a variant for each version. This will /// clone the underlying types. - fn versionize_owned(&self) -> Self::VersionedOwned; + fn versionize_owned(self) -> Self::VersionedOwned; } /// This trait is used as a proxy to be more felxible when deriving Versionize for Vec. /// This way, we can chose to skip versioning Vec if T is a native types but still versionize in /// a loop if T is a custom type. /// This is used as a workaround for feature(specialization) and to bypass the orphan rule. -pub trait VersionizeVec: Sized { +pub trait VersionizeSlice: Sized { type VersionedSlice<'vers>: Serialize where Self: 'vers; fn versionize_slice(slice: &[Self]) -> Self::VersionedSlice<'_>; +} +pub trait VersionizeVec: Sized { type VersionedVec: Serialize + DeserializeOwned; - fn versionize_vec(slice: &[Self]) -> Self::VersionedVec; + fn versionize_vec(vec: Vec) -> Self::VersionedVec; } #[derive(Debug)] @@ -65,12 +71,15 @@ pub enum UnversionizeError { Upgrade { from_vers: String, into_vers: String, - message: String, + source: Box, }, /// An error has been returned in the conversion method provided by the `try_from` parameter /// attribute - Conversion { from_type: String, message: String }, + Conversion { + from_type: String, + source: Box, + }, } impl Display for UnversionizeError { @@ -79,31 +88,40 @@ impl Display for UnversionizeError { Self::Upgrade { from_vers, into_vers, - message, + source, } => write!( f, - "Failed to upgrade from {from_vers} into {into_vers}: {message}" + "Failed to upgrade from {from_vers} into {into_vers}: {source}" ), - Self::Conversion { from_type, message } => { - write!(f, "Failed to convert from {from_type}: {message}") + Self::Conversion { from_type, source } => { + write!(f, "Failed to convert from {from_type}: {source}") } } } } +impl Error for UnversionizeError { + fn source(&self) -> Option<&(dyn Error + 'static)> { + match self { + UnversionizeError::Upgrade { source, .. } => Some(source.as_ref()), + UnversionizeError::Conversion { source, .. } => Some(source.as_ref()), + } + } +} + impl UnversionizeError { - pub fn upgrade(from_vers: &str, into_vers: &str, message: &str) -> Self { + pub fn upgrade(from_vers: &str, into_vers: &str, source: E) -> Self { Self::Upgrade { from_vers: from_vers.to_string(), into_vers: into_vers.to_string(), - message: message.to_string(), + source: Box::new(source), } } - pub fn conversion(from_type: &str, message: &str) -> Self { + pub fn conversion(from_type: &str, source: E) -> Self { Self::Conversion { from_type: from_type.to_string(), - message: message.to_string(), + source: Box::new(source), } } } @@ -117,7 +135,7 @@ impl From for UnversionizeError { /// This trait means that we can convert from a versioned enum into the target type. This trait /// can only be implemented on Owned/static types, whereas `Versionize` can also be implemented /// on reference types. -pub trait Unversionize: Versionize + Sized { +pub trait Unversionize: VersionizeOwned + Sized { /// Creates an object from a versioned enum, and eventually upgrades from previous /// variants. fn unversionize(versioned: Self::VersionedOwned) -> Result; @@ -131,17 +149,19 @@ pub trait UnversionizeVec: VersionizeVec { /// Self or &Self. pub trait NotVersioned: Versionize {} -impl VersionizeVec for T { +impl VersionizeSlice for T { type VersionedSlice<'vers> = &'vers [T] where T: 'vers; fn versionize_slice(slice: &[Self]) -> Self::VersionedSlice<'_> { slice } +} +impl VersionizeVec for T { type VersionedVec = Vec; - fn versionize_vec(slice: &[Self]) -> Self::VersionedVec { - slice.to_vec() + fn versionize_vec(vec: Vec) -> Self::VersionedVec { + vec } } @@ -159,14 +179,15 @@ macro_rules! impl_scalar_versionize { impl Versionize for $t { type Versioned<'vers> = $t; - type VersionedOwned = $t; - fn versionize(&self) -> Self::Versioned<'_> { *self } + } - fn versionize_owned(&self) -> Self::VersionedOwned { - *self + impl VersionizeOwned for $t { + type VersionedOwned = $t; + fn versionize_owned(self) -> Self::VersionedOwned { + self } } @@ -208,11 +229,13 @@ impl Versionize for Box { fn versionize(&self) -> Self::Versioned<'_> { self.as_ref().versionize() } +} +impl VersionizeOwned for Box { type VersionedOwned = Box; - fn versionize_owned(&self) -> Self::VersionedOwned { - Box::new(T::versionize_owned(self)) + fn versionize_owned(self) -> Self::VersionedOwned { + Box::new(T::versionize_owned(*self)) } } @@ -222,31 +245,35 @@ impl Unversionize for Box { } } -impl Versionize for Vec { +impl Versionize for Vec { type Versioned<'vers> = T::VersionedSlice<'vers> where T: 'vers; fn versionize(&self) -> Self::Versioned<'_> { T::versionize_slice(self) } +} +impl VersionizeOwned for Vec { type VersionedOwned = T::VersionedVec; - fn versionize_owned(&self) -> Self::VersionedOwned { + fn versionize_owned(self) -> Self::VersionedOwned { T::versionize_vec(self) } } -impl Versionize for [T] { +impl Versionize for [T] { type Versioned<'vers> = T::VersionedSlice<'vers> where T: 'vers; fn versionize(&self) -> Self::Versioned<'_> { T::versionize_slice(self) } +} +impl VersionizeOwned for &[T] { type VersionedOwned = T::VersionedVec; - fn versionize_owned(&self) -> Self::VersionedOwned { - T::versionize_vec(self) + fn versionize_owned(self) -> Self::VersionedOwned { + T::versionize_vec(self.to_vec()) } } @@ -262,11 +289,13 @@ impl Versionize for String { fn versionize(&self) -> Self::Versioned<'_> { self.as_ref() } +} +impl VersionizeOwned for String { type VersionedOwned = Self; - fn versionize_owned(&self) -> Self::VersionedOwned { - self.clone() + fn versionize_owned(self) -> Self::VersionedOwned { + self } } @@ -284,10 +313,12 @@ impl Versionize for str { fn versionize(&self) -> Self::Versioned<'_> { self } +} +impl VersionizeOwned for &str { type VersionedOwned = String; - fn versionize_owned(&self) -> Self::VersionedOwned { + fn versionize_owned(self) -> Self::VersionedOwned { self.to_string() } } @@ -300,11 +331,13 @@ impl Versionize for Option { fn versionize(&self) -> Self::Versioned<'_> { self.as_ref().map(|val| val.versionize()) } +} +impl VersionizeOwned for Option { type VersionedOwned = Option; - fn versionize_owned(&self) -> Self::VersionedOwned { - self.as_ref().map(|val| val.versionize_owned()) + fn versionize_owned(self) -> Self::VersionedOwned { + self.map(|val| val.versionize_owned()) } } @@ -324,11 +357,13 @@ impl Versionize for PhantomData { fn versionize(&self) -> Self::Versioned<'_> { *self } +} +impl VersionizeOwned for PhantomData { type VersionedOwned = Self; - fn versionize_owned(&self) -> Self::VersionedOwned { - *self + fn versionize_owned(self) -> Self::VersionedOwned { + self } } @@ -340,6 +375,32 @@ impl Unversionize for PhantomData { impl NotVersioned for PhantomData {} +impl Versionize for Arc { + type Versioned<'vers> = T::Versioned<'vers> + where + T: 'vers; + + fn versionize(&self) -> Self::Versioned<'_> { + self.as_ref().versionize() + } +} + +impl VersionizeOwned for Arc { + type VersionedOwned = T::VersionedOwned; + + fn versionize_owned(self) -> Self::VersionedOwned { + Arc::unwrap_or_clone(self).versionize_owned() + } +} + +impl Unversionize for Arc { + fn unversionize(versioned: Self::VersionedOwned) -> Result { + Ok(Arc::new(T::unversionize(versioned)?)) + } +} + +impl NotVersioned for Arc {} + impl Versionize for Complex { type Versioned<'vers> = Complex> where T: 'vers; @@ -349,10 +410,12 @@ impl Versionize for Complex { im: self.im.versionize(), } } +} +impl VersionizeOwned for Complex { type VersionedOwned = Complex; - fn versionize_owned(&self) -> Self::VersionedOwned { + fn versionize_owned(self) -> Self::VersionedOwned { Complex { re: self.re.versionize_owned(), im: self.im.versionize_owned(), @@ -377,16 +440,18 @@ impl Versionize for ABox { fn versionize(&self) -> Self::Versioned<'_> { self.as_ref().versionize() } +} +impl VersionizeOwned for ABox { // Alignment doesn't matter for versioned types type VersionedOwned = Box; - fn versionize_owned(&self) -> Self::VersionedOwned { - Box::new(T::versionize_owned(self)) + fn versionize_owned(self) -> Self::VersionedOwned { + Box::new(T::versionize_owned(*self)) } } -impl Unversionize for ABox +impl Unversionize for ABox where T::VersionedOwned: Clone, { @@ -395,22 +460,24 @@ where } } -impl Versionize for AVec { +impl Versionize for AVec { type Versioned<'vers> = T::VersionedSlice<'vers> where T: 'vers; fn versionize(&self) -> Self::Versioned<'_> { T::versionize_slice(self) } +} - // Alignment doesn't matter for versioned types +// Alignment doesn't matter for versioned types +impl VersionizeOwned for AVec { type VersionedOwned = T::VersionedVec; - fn versionize_owned(&self) -> Self::VersionedOwned { - T::versionize_vec(self) + fn versionize_owned(self) -> Self::VersionedOwned { + T::versionize_vec(self.to_vec()) } } -impl Unversionize for AVec { +impl Unversionize for AVec { fn unversionize(versioned: Self::VersionedOwned) -> Result { T::unversionize_vec(versioned).map(|unver| AVec::from_iter(0, unver)) } @@ -418,16 +485,38 @@ impl Unversionize for AVec { impl NotVersioned for AVec {} +impl Versionize for () { + type Versioned<'vers> = (); + + fn versionize(&self) -> Self::Versioned<'_> {} +} + +impl VersionizeOwned for () { + type VersionedOwned = (); + + fn versionize_owned(self) -> Self::VersionedOwned {} +} + +impl Unversionize for () { + fn unversionize(_versioned: Self::VersionedOwned) -> Result { + Ok(()) + } +} + +impl NotVersioned for () {} + impl Versionize for (T, U) { type Versioned<'vers> = (T::Versioned<'vers>, U::Versioned<'vers>) where T: 'vers, U: 'vers; fn versionize(&self) -> Self::Versioned<'_> { (self.0.versionize(), self.1.versionize()) } +} +impl VersionizeOwned for (T, U) { type VersionedOwned = (T::VersionedOwned, U::VersionedOwned); - fn versionize_owned(&self) -> Self::VersionedOwned { + fn versionize_owned(self) -> Self::VersionedOwned { (self.0.versionize_owned(), self.1.versionize_owned()) } } @@ -439,3 +528,39 @@ impl Unversionize for (T, U) { } impl NotVersioned for (T, U) {} + +impl Versionize for (T, U, V) { + type Versioned<'vers> = (T::Versioned<'vers>, U::Versioned<'vers>, V::Versioned<'vers>) where T: 'vers, U: 'vers, V: 'vers; + + fn versionize(&self) -> Self::Versioned<'_> { + ( + self.0.versionize(), + self.1.versionize(), + self.2.versionize(), + ) + } +} + +impl VersionizeOwned for (T, U, V) { + type VersionedOwned = (T::VersionedOwned, U::VersionedOwned, V::VersionedOwned); + + fn versionize_owned(self) -> Self::VersionedOwned { + ( + self.0.versionize_owned(), + self.1.versionize_owned(), + self.2.versionize_owned(), + ) + } +} + +impl Unversionize for (T, U, V) { + fn unversionize(versioned: Self::VersionedOwned) -> Result { + Ok(( + T::unversionize(versioned.0)?, + U::unversionize(versioned.1)?, + V::unversionize(versioned.2)?, + )) + } +} + +impl NotVersioned for (T, U, V) {} diff --git a/utils/tfhe-versionable/src/upgrade.rs b/utils/tfhe-versionable/src/upgrade.rs index 0697a964ef..d0417af9a2 100644 --- a/utils/tfhe-versionable/src/upgrade.rs +++ b/utils/tfhe-versionable/src/upgrade.rs @@ -3,5 +3,6 @@ /// This trait should be implemented for each version of the original type that is not the current /// one. The upgrade method is called in chains until we get to the last version of the type. pub trait Upgrade { - fn upgrade(self) -> Result; + type Error: std::error::Error; + fn upgrade(self) -> Result; } diff --git a/utils/tfhe-versionable/tests/formats.rs b/utils/tfhe-versionable/tests/formats.rs index 1294c1838a..9d5da05f8b 100644 --- a/utils/tfhe-versionable/tests/formats.rs +++ b/utils/tfhe-versionable/tests/formats.rs @@ -49,6 +49,8 @@ mod v1 { pub struct MyStruct(pub u32, pub T); mod backward_compat { + use std::convert::Infallible; + use tfhe_versionable::{Upgrade, Version, VersionsDispatch}; use super::MyStruct; @@ -57,7 +59,9 @@ mod v1 { pub struct MyStructV0(pub u32); impl Upgrade> for MyStructV0 { - fn upgrade(self) -> Result, String> { + type Error = Infallible; + + fn upgrade(self) -> Result, Self::Error> { Ok(MyStruct(self.0, T::default())) } } @@ -85,6 +89,8 @@ mod v2 { } mod backward_compat { + use std::convert::Infallible; + use tfhe_versionable::{Upgrade, Version, VersionsDispatch}; use super::MyStruct; @@ -93,7 +99,9 @@ mod v2 { pub struct MyStructV0(pub u32); impl Upgrade> for MyStructV0 { - fn upgrade(self) -> Result, String> { + type Error = Infallible; + + fn upgrade(self) -> Result, Self::Error> { Ok(MyStructV1(self.0, T::default())) } } @@ -102,7 +110,9 @@ mod v2 { pub struct MyStructV1(pub u32, pub T); impl Upgrade> for MyStructV1 { - fn upgrade(self) -> Result, String> { + type Error = Infallible; + + fn upgrade(self) -> Result, Self::Error> { Ok(MyStruct { count: self.0, attr: T::default(),