Is there an existing issue for this?
Current Behavior
CiphertextTallySelection defines its ciphertext field with a default instance:
ciphertext: ElGamalCiphertext = field(
default=ElGamalCiphertext(ONE_MOD_P, ONE_MOD_P)
)
This means the default ElGamalCiphertext object is created once at class definition time and then shared across all CiphertextTallySelection instances that do not receive an explicit ciphertext value.
As a result, multiple tally selections can unintentionally reference the same default ciphertext object. Any in-place mutation of that object can leak across instances.
This pattern also causes Python 3.11+ dataclass validation to fail because mutable defaults must be provided through default_factory.
Expected Behavior
Each CiphertextTallySelection should receive its own ElGamalCiphertext instance initialized at instance initialization, and not at class definition (thanks to default_factory instead of default).
The default value should represent the same initial ciphertext content for every instance, but it should not be the same shared object in memory.
Using a per-instance default avoids unintended shared state and keeps the field compatible with Python 3.11+ dataclass validation rules.
Steps To Reproduce
- Create two
CiphertextTallySelection instances without passing an explicit ciphertext:
from electionguard.tally import CiphertextTallySelection
from electionguard.group import ONE_MOD_Q, TWO_MOD_P
first = CiphertextTallySelection("selection-1", 1, ONE_MOD_Q)
second = CiphertextTallySelection("selection-2", 2, ONE_MOD_Q)
- Observe that both instances share the same default object:
print(first.ciphertext is second.ciphertext)
- Mutate the ciphertext of one instance in place:
first.ciphertext.pad = TWO_MOD_P
- Observe that the mutation is visible from the second instance as well:
print(second.ciphertext.pad == TWO_MOD_P)
- On Python 3.11+, importing
electionguard will raise an error, making the lib unusable:
ValueError: mutable default <class 'electionguard.elgamal.ElGamalCiphertext'> for field ciphertext is not allowed: use default_factory
Environment
Anything else?
https://docs.python.org/3/library/dataclasses.html#mutable-default-values
Is there an existing issue for this?
Current Behavior
CiphertextTallySelectiondefines itsciphertextfield with a default instance:This means the default
ElGamalCiphertextobject is created once at class definition time and then shared across allCiphertextTallySelectioninstances that do not receive an explicitciphertextvalue.As a result, multiple tally selections can unintentionally reference the same default ciphertext object. Any in-place mutation of that object can leak across instances.
This pattern also causes Python 3.11+ dataclass validation to fail because mutable defaults must be provided through
default_factory.Expected Behavior
Each
CiphertextTallySelectionshould receive its ownElGamalCiphertextinstance initialized at instance initialization, and not at class definition (thanks todefault_factoryinstead ofdefault).The default value should represent the same initial ciphertext content for every instance, but it should not be the same shared object in memory.
Using a per-instance default avoids unintended shared state and keeps the field compatible with Python 3.11+ dataclass validation rules.
Steps To Reproduce
CiphertextTallySelectioninstances without passing an explicitciphertext:electionguardwill raise an error, making the lib unusable:ValueError: mutable default <class 'electionguard.elgamal.ElGamalCiphertext'> for field ciphertext is not allowed: use default_factoryEnvironment
- OS: Windows 11Anything else?
https://docs.python.org/3/library/dataclasses.html#mutable-default-values