From 569abd9a3bc4f1fc0680c234e66832d1b2dba22f Mon Sep 17 00:00:00 2001 From: pgardratzama Date: Fri, 19 Dec 2025 17:57:22 +0100 Subject: [PATCH] fix(hpu): fix whitepaper erc20 for HPU using if_then_zero --- .../benches/high_level_api/erc20.rs | 14 ++--- tfhe/src/high_level_api/booleans/base.rs | 52 ++++++++++++++++++- tfhe/src/high_level_api/prelude.rs | 1 + tfhe/src/high_level_api/traits.rs | 4 ++ 4 files changed, 64 insertions(+), 7 deletions(-) diff --git a/tfhe-benchmark/benches/high_level_api/erc20.rs b/tfhe-benchmark/benches/high_level_api/erc20.rs index 1e8e81b3b..124b35482 100644 --- a/tfhe-benchmark/benches/high_level_api/erc20.rs +++ b/tfhe-benchmark/benches/high_level_api/erc20.rs @@ -29,12 +29,13 @@ pub fn transfer_whitepaper( ) -> (FheType, FheType) where FheType: Add + for<'a> FheOrd<&'a FheType> + FheTrivialEncrypt, - FheBool: IfThenElse, + FheBool: IfThenZero, for<'a> &'a FheType: Add + Sub, { let has_enough_funds = (from_amount).ge(amount); - let zero_amount = FheType::encrypt_trivial(0u64); - let amount_to_transfer = has_enough_funds.select(amount, &zero_amount); + //let zero_amount = FheType::encrypt_trivial(0u64); + //let amount_to_transfer = has_enough_funds.select(amount, &zero_amount); + let amount_to_transfer = has_enough_funds.if_then_zero(amount); let new_to_amount = to_amount + &amount_to_transfer; let new_from_amount = from_amount - &amount_to_transfer; @@ -51,12 +52,13 @@ pub fn par_transfer_whitepaper( where FheType: Add + for<'a> FheOrd<&'a FheType> + Send + Sync + FheTrivialEncrypt, - FheBool: IfThenElse, + FheBool: IfThenZero, for<'a> &'a FheType: Add + Sub, { let has_enough_funds = (from_amount).ge(amount); - let zero_amount = FheType::encrypt_trivial(0u64); - let amount_to_transfer = has_enough_funds.select(amount, &zero_amount); + //let zero_amount = FheType::encrypt_trivial(0u64); + //let amount_to_transfer = has_enough_funds.select(amount, &zero_amount); + let amount_to_transfer = has_enough_funds.if_then_zero(amount); let (new_to_amount, new_from_amount) = rayon::join( || to_amount + &amount_to_transfer, diff --git a/tfhe/src/high_level_api/booleans/base.rs b/tfhe/src/high_level_api/booleans/base.rs index efbe61e14..1bb94fa75 100644 --- a/tfhe/src/high_level_api/booleans/base.rs +++ b/tfhe/src/high_level_api/booleans/base.rs @@ -7,7 +7,7 @@ use crate::high_level_api::integers::{FheInt, FheIntId, FheUint, FheUintId}; use crate::high_level_api::keys::InternalServerKey; use crate::high_level_api::re_randomization::ReRandomizationMetadata; use crate::high_level_api::traits::{ - FheEq, Flip, IfThenElse, ReRandomize, ScalarIfThenElse, Tagged, + FheEq, Flip, IfThenElse, IfThenZero, ReRandomize, ScalarIfThenElse, Tagged, }; use crate::high_level_api::{global_state, CompactPublicKey}; use crate::integer::block_decomposition::DecomposableInto; @@ -552,6 +552,56 @@ where } } +impl IfThenZero> for FheBool +where + Id: FheUintId, +{ + /// Conditional selection. + /// + /// The output value returned depends on the value of `self`. + /// + /// - if `self` is true, the output will have the value of `ct_then` + /// - if `self` is false, the output will be an encryption of 0 + fn if_then_zero(&self, ct_then: &FheUint) -> FheUint { + global_state::with_internal_keys(|sks| match sks { + InternalServerKey::Cpu(_) => { + panic!("CPU does not support if_then_zero at this point") + } + #[cfg(feature = "gpu")] + InternalServerKey::Cuda(_) => { + panic!("Cuda does not support if_then_zero at this point") + } + #[cfg(feature = "hpu")] + InternalServerKey::Hpu(device) => { + let hpu_then = ct_then.ciphertext.on_hpu(device); + let hpu_cond = self.ciphertext.on_hpu(device); + + let (opcode, proto) = { + let asm_iop = &hpu_asm::iop::IOP_IF_THEN_ZERO; + ( + asm_iop.opcode(), + &asm_iop.format().expect("Unspecified IOP format").proto, + ) + }; + // These clones are cheap are they are just Arc + let hpu_result = HpuRadixCiphertext::exec( + proto, + opcode, + &[hpu_then.clone(), hpu_cond.clone()], + &[], + ) + .pop() + .unwrap(); + FheUint::new( + hpu_result, + device.tag.clone(), + ReRandomizationMetadata::default(), + ) + } + }) + } +} + impl IfThenElse> for FheBool { /// Conditional selection. /// diff --git a/tfhe/src/high_level_api/prelude.rs b/tfhe/src/high_level_api/prelude.rs index acacb163d..fa71317e9 100644 --- a/tfhe/src/high_level_api/prelude.rs +++ b/tfhe/src/high_level_api/prelude.rs @@ -11,6 +11,7 @@ pub use crate::high_level_api::traits::{ FheOrd, FheTrivialEncrypt, FheTryEncrypt, FheTryTrivialEncrypt, FheWait, Flip, IfThenElse, OverflowingAdd, OverflowingMul, OverflowingNeg, OverflowingSub, ReRandomize, RotateLeft, RotateLeftAssign, RotateRight, RotateRightAssign, ScalarIfThenElse, SquashNoise, Tagged, + IfThenZero, }; #[cfg(feature = "hpu")] pub use crate::high_level_api::traits::{FheHpu, HpuHandle}; diff --git a/tfhe/src/high_level_api/traits.rs b/tfhe/src/high_level_api/traits.rs index d05a0cda8..75e9561e0 100644 --- a/tfhe/src/high_level_api/traits.rs +++ b/tfhe/src/high_level_api/traits.rs @@ -149,6 +149,10 @@ pub trait IfThenElse { } } +pub trait IfThenZero { + fn if_then_zero(&self, ct_then: &Ciphertext) -> Ciphertext; +} + pub trait ScalarIfThenElse { type Output;