You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
* refactor(lstar): freeze constructed-once SSZ containers
Set frozen=True on the lstar containers that are built once and never
mutated after construction: GenesisConfig, Validator, AggregatedAttestation,
and SignedAggregatedAttestation. This completes the freeze begun on
Checkpoint, AttestationData, Attestation, SingleMessageAggregate, and
MultiMessageAggregate, so accidental post-construction mutation now raises
instead of silently succeeding.
Freezing Validator required one call-site change: the fork-choice test
fixture injected public keys by mutating each validator in place. It now
rebuilds each validator with model_copy(update=...), which is byte-identical
in hash_tree_root and leaves the originals untouched.
Block, BlockHeader, BlockBody, and SignedBlock stay mutable for now. Their
state_root and body are patched in place during block production and the
state transition (block_production.py and state_transition.py), a two-pass
pattern entangled with the larger state-transition refactor. State and Store
remain mutable by design.
Frozen is an enforcement aid, not a hard immutability guarantee: model_config
is itself a mutable dict and an unfreeze-then-assign sequence can still leave
a field mutable (pydantic#12361). It catches accidental mutation; it is not a
soundness boundary.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* refactor(lstar): freeze the Block family
Set frozen=True on BlockBody, BlockHeader, Block, and SignedBlock, the last
mutable containers in the lstar object graph apart from State and Store. With
this, every consensus container except the State accumulator and the
fork-choice Store is immutable: accidental post-construction mutation now
raises instead of silently corrupting a value that may already be referenced
elsewhere or committed to a root.
These four were deferred from the initial freeze because each was patched in
place after construction. Every such site is migrated to model_copy(update=...),
which is byte-identical in hash_tree_root:
- Block production builds the block with a zero state root, processes it,
then rebuilds the block with the real root.
- The slot transition rebuilds the latest header with its cached state root
and reassigns it on the still-mutable State, instead of mutating the header
in place.
- The test-framework builders and the signature-tampering helpers rebuild
blocks and bodies through layered model_copy, matching the pattern the
attestation-swap tamper already used.
State and Store stay mutable. State is the deepcopy-and-mutate accumulator at
the heart of the state transition; converting it to return a new value is a
larger, separate change. Store is fork-choice scratch state.
As with the earlier container freeze, frozen is an enforcement aid, not a hard
immutability guarantee (model_config is a mutable dict; pydantic#12361).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
0 commit comments