Problem
Context::encrypt currently uses a workaround pipeline because the upstream poulpy FheUint::encrypt_sk -> FheUintPrepared::prepare path produces incorrect ciphertexts in the pinned poulpy revision (see the crate::ciphertext module docs).
The workaround forces several deviations from squid's normal patterns:
Ciphertext carries an extra prepared: Option<FheUintPrepared> cache field (src/ciphertext.rs).
Context::encrypt routes through FheUintPrepared::encrypt_sk followed by FheUint::from_fhe_uint_prepared instead of the natural encrypt_sk -> prepare.
Context::encrypt allocates a hardcoded 1 << 22 (4 MiB) scratch arena (ENCRYPT_SCRATCH_BYTES) because poulpy exposes no wrapper-level *_tmp_bytes for the two workaround functions.
Context::eval_binary has a "chaining limitation": only freshly-encrypted ciphertexts can be operated on, because the prepared cache cannot be rebuilt from a serialized / op-output Ciphertext (the prepare path that would rebuild it is exactly the broken one).
Proposed fix
When the poulpy prepare bug is resolved upstream, revert squid's encrypt (and supporting types) back to the original pattern:
FheUint::encrypt_sk (size via FheUint::encrypt_sk_tmp_bytes).
FheUintPrepared::prepare on demand (size via Module::fhe_uint_prepare_tmp_bytes).
Then collapse the workaround scaffolding:
- Drop
Ciphertext::prepared; Ciphertext becomes a thin wrapper over FheUint again.
- Replace
ENCRYPT_SCRATCH_BYTES with a single max(...) of the two *_tmp_bytes helpers above (matches keygen / decrypt / eval_binary).
- Remove the chaining-limitation panic and docs in
Context::eval_binary and crate::ciphertext; rebuild the prepared form on the fly inside eval_binary from the packed FheUint.
Tradeoff
None expected — this is a pure regression-of-a-workaround. Memory drops (no per-Ciphertext prepared cache, smaller encrypt arena) and the API surface gets simpler (op outputs and deserialized ciphertexts become first-class operands again).
Before implementing
Confirm the upstream bug is fixed against squid's reproducer (tests/poulpy_manual_bdd.rs and the prepare_then_add test added in poulpy). Bump the poulpy pin, then do the revert in one PR.
Related code
src/context.rs — Context::encrypt (with the TODO(poulpy-bug) comment), Context::eval_binary.
src/ciphertext.rs — Ciphertext struct + chaining-limitation docs.
tests/poulpy_manual_bdd.rs — local reproducer of the upstream bug.
Problem
Context::encryptcurrently uses a workaround pipeline because the upstream poulpyFheUint::encrypt_sk -> FheUintPrepared::preparepath produces incorrect ciphertexts in the pinned poulpy revision (see thecrate::ciphertextmodule docs).The workaround forces several deviations from squid's normal patterns:
Ciphertextcarries an extraprepared: Option<FheUintPrepared>cache field (src/ciphertext.rs).Context::encryptroutes throughFheUintPrepared::encrypt_skfollowed byFheUint::from_fhe_uint_preparedinstead of the naturalencrypt_sk -> prepare.Context::encryptallocates a hardcoded1 << 22(4 MiB) scratch arena (ENCRYPT_SCRATCH_BYTES) because poulpy exposes no wrapper-level*_tmp_bytesfor the two workaround functions.Context::eval_binaryhas a "chaining limitation": only freshly-encrypted ciphertexts can be operated on, because thepreparedcache cannot be rebuilt from a serialized / op-outputCiphertext(thepreparepath that would rebuild it is exactly the broken one).Proposed fix
When the poulpy
preparebug is resolved upstream, revert squid'sencrypt(and supporting types) back to the original pattern:FheUint::encrypt_sk(size viaFheUint::encrypt_sk_tmp_bytes).FheUintPrepared::prepareon demand (size viaModule::fhe_uint_prepare_tmp_bytes).Then collapse the workaround scaffolding:
Ciphertext::prepared;Ciphertextbecomes a thin wrapper overFheUintagain.ENCRYPT_SCRATCH_BYTESwith a singlemax(...)of the two*_tmp_byteshelpers above (matcheskeygen/decrypt/eval_binary).Context::eval_binaryandcrate::ciphertext; rebuild the prepared form on the fly insideeval_binaryfrom the packedFheUint.Tradeoff
None expected — this is a pure regression-of-a-workaround. Memory drops (no per-
Ciphertextprepared cache, smallerencryptarena) and the API surface gets simpler (op outputs and deserialized ciphertexts become first-class operands again).Before implementing
Confirm the upstream bug is fixed against squid's reproducer (
tests/poulpy_manual_bdd.rsand theprepare_then_addtest added in poulpy). Bump the poulpy pin, then do the revert in one PR.Related code
src/context.rs—Context::encrypt(with theTODO(poulpy-bug)comment),Context::eval_binary.src/ciphertext.rs—Ciphertextstruct + chaining-limitation docs.tests/poulpy_manual_bdd.rs— local reproducer of the upstream bug.